defmodule Auth.Init do
  @moduledoc """
  `Init` as its' name suggests initializes the Auth Application 
  by creating the necessary records in the various tables.

  This is the sequence of steps that are followed to init the App:

  1. Create the "Super Admin" person who owns the Auth App
  based on the `ADMIN_EMAIL` environment/config variable.

  > The person.id (1) for the Super Admin 
  will own the remaining records so it needs to be created first. 

  2. Create default records (Statuses & Roles)

  3. Create the App and `AUTH_API_KEY` for the Auth App.
  > Log the `AUTH_API_KEY` so that it can be exported.
  """

  require Logger
  import Ecto.Changeset
  alias Auth.{Person, Repo}

  def main do
    Logger.info("Initialising the Auth Database ...")

    # if the #1 App does not exist, create it:
    api_key = case Auth.App.get_app!(1) do
      nil -> 
        admin = Auth.Init.create_admin()

        # Create Generic Roles & Statuses
        Auth.InitStatuses.insert_statuses()
        Auth.InitRoles.create_default_roles()
    
        # Update status of Admin to "verified"
        Auth.Person.verify_person_by_id(admin.id)
        
        # Create App and API Key for the admin:
        Auth.Init.create_apikey_for_admin(admin)

      app ->
        key = List.last(app.apikeys)
        key.client_id <> "/" <> key.client_secret <> "/" <> get_auth_url()
        
    end

    case Envar.get("MIX_ENV") do
      "test" ->
        # set the AUTH_API_KEY environment variable during test run:
        Envar.set("AUTH_API_KEY", api_key)

      # ignore the next lines because we can't test them:
      # coveralls-ignore-start
      _ ->
        # Log the AUTH_API_KEY so it can be exported:
        Logger.info("export AUTH_API_KEY=#{api_key}")
        # coveralls-ignore-stop
    end

    :ok
  end

  # Get AUTH_URL or fallback to localhost:
  defp get_auth_url do
    # see .env_sample for example
    Envar.get("AUTH_URL") || "localhost:4000"
  end

  def create_admin do
    email = Envar.get("ADMIN_EMAIL")
    Logger.debug("ADMIN_EMAIL:#{email}")

    case Person.get_person_by_email(email) do
      # Ignore if the Super Admin already exists:
      nil ->
        %Person{}
        |> Person.changeset(%{email: email})
        |> Repo.insert!()

      person ->
        person
    end
  end

  def create_apikey_for_admin(person) do
    {:ok, app} =
      %{
        "name" => "default system app",
        "desc" => "Created by lib/auth/init/init.ex during setup.",
        "url" => "localhost:4000",
        "person_id" => person.id,
        "status" => 3
      }
      |> Auth.App.create_app()

    # Self-grant superadmin role to App "owner":
    Auth.PeopleRoles.upsert(app.id, person.id, person.id, 1)

    # If AUTH_API_KEY environment variable is already set, use it:
    key = if Envar.is_set?("AUTH_API_KEY") do

      update_attrs = %{
        "client_id" => AuthPlug.Token.client_id(),
        "client_secret" => AuthPlug.Token.client_secret()
      }

      # Update value of client_id & client_secret in the DB to match Env.
      {:ok, key} =
        Auth.Apikey.get_apikey_by_app_id(app.id)
        |> cast(update_attrs, [:client_id, :client_secret])
        |> Repo.update()

      key
      
    # coveralls-ignore-start
    else
      List.last(app.apikeys)
    # coveralls-ignore-stop
    end

    key.client_id <> "/" <> key.client_secret <> "/" <> get_auth_url()
  end
end
