defmodule Berrypod.Mailer do
  use Swoosh.Mailer, otp_app: :berrypod

  alias Berrypod.Mailer.Adapters
  alias Berrypod.Settings

  @doc """
  Returns whether a real email adapter is configured.

  True when the adapter is anything other than `Swoosh.Adapters.Local`
  (which just stores emails in memory for dev use).
  """
  def email_configured? do
    adapter = Application.get_env(:berrypod, __MODULE__)[:adapter]
    adapter != nil and adapter != Swoosh.Adapters.Local
  end

  @doc """
  Returns whether email delivery has been verified via a successful test email.

  This is the flag the login page uses to decide whether to show the magic link
  form. A configured adapter alone isn't enough — the admin must have sent a
  test email that succeeded.
  """
  def email_verified? do
    email_configured?() and Settings.get_setting("email_verified", false) == true
  end

  @doc "Marks email delivery as verified (called after a successful test email)."
  def mark_email_verified do
    Settings.put_setting("email_verified", true, "boolean")
  end

  @doc "Clears the email verified flag (called when config changes)."
  def clear_email_verified do
    Settings.delete_setting("email_verified")
  end

  @doc """
  Returns true if email is configured via environment variables (SMTP_HOST).

  When env vars are active, the admin UI shows the config as read-only.
  """
  def env_var_configured? do
    System.get_env("SMTP_HOST") != nil
  end

  @doc """
  Returns the current adapter key and config for display in the admin UI.

  Returns `{adapter_key, config_map}` or `{nil, %{}}` if using the default.
  """
  def current_config do
    mailer_config = Application.get_env(:berrypod, __MODULE__, [])
    adapter = mailer_config[:adapter]

    case Enum.find(Adapters.all(), &(&1.module == adapter)) do
      nil ->
        {nil, %{}}

      adapter_info ->
        config =
          for field <- adapter_info.fields, into: %{} do
            settings_key = Adapters.settings_key(adapter_info.key, field.key)

            value =
              case field.type do
                :secret -> Settings.secret_hint(settings_key)
                _ -> Settings.get_setting(settings_key)
              end

            {field.key, value}
          end

        {adapter_info.key, config}
    end
  end

  @doc """
  Loads email config from the Settings table and applies it to Application env.

  Env vars take precedence — if SMTP_HOST is set, this is a no-op since
  runtime.exs already configured the adapter.

  Called on boot (from Application.start) and after admin saves email settings.
  """
  def load_config do
    if env_var_configured?() do
      :ok
    else
      case Settings.get_setting("email_adapter") do
        nil ->
          :ok

        adapter_key ->
          case Adapters.get(adapter_key) do
            nil ->
              :ok

            adapter_info ->
              config = build_config(adapter_info)
              Application.put_env(:berrypod, __MODULE__, config)
              # API-based adapters need a real HTTP client (dev defaults to false)
              if adapter_info.module != Swoosh.Adapters.SMTP do
                Application.put_env(:swoosh, :api_client, Swoosh.ApiClient.Req)
              end

              :ok
          end
      end
    end
  end

  @doc """
  Sends a test email to the given address using the current config.
  """
  def send_test_email(to_address, from \\ nil) do
    import Swoosh.Email

    email =
      new()
      |> to(to_address)
      |> from({"Berrypod", from || from_address()})
      |> subject("Berrypod test email")
      |> text_body("This is a test email from your Berrypod shop. Email delivery is working.")

    deliver(email)
  end

  @doc "Returns the configured from address for outbound email."
  def from_address do
    Settings.get_setting("email_from_address") || "noreply@#{default_from_domain()}"
  end

  # Build Swoosh config keyword list from Settings for a given adapter
  defp build_config(adapter_info) do
    opts =
      for field <- adapter_info.fields, reduce: [] do
        acc ->
          settings_key = Adapters.settings_key(adapter_info.key, field.key)

          value =
            case field.type do
              :secret -> Settings.get_secret(settings_key)
              _ -> Settings.get_setting(settings_key)
            end

          case {value, field} do
            {nil, _} ->
              acc

            {val, %{type: :integer}} ->
              [{String.to_atom(field.key), to_integer(val)} | acc]

            {val, _} ->
              [{String.to_atom(field.key), val} | acc]
          end
      end

    # SMTP uses :relay, others use the native Swoosh key names
    [{:adapter, adapter_info.module} | opts]
  end

  defp to_integer(val) when is_integer(val), do: val
  defp to_integer(val) when is_binary(val), do: String.to_integer(val)

  defp default_from_domain do
    case Application.get_env(:berrypod, BerrypodWeb.Endpoint)[:url][:host] do
      nil -> "example.com"
      host -> host
    end
  end
end
