defmodule BerrypodWeb.Admin.EmailSettings do
  use BerrypodWeb, :live_view

  alias Berrypod.Mailer
  alias Berrypod.Mailer.Adapters
  alias Berrypod.Settings

  @impl true
  def mount(_params, _session, socket) do
    env_locked = Mailer.env_var_configured?()
    {current_adapter, _current_values} = Mailer.current_config()
    saved_adapter = Settings.get_setting("email_adapter")

    # Controller fallback passes errors via flash after failed validation
    flash_adapter = Phoenix.Flash.get(socket.assigns.flash, :error_adapter)
    flash_errors = Phoenix.Flash.get(socket.assigns.flash, :field_errors) || %{}

    adapter_key = flash_adapter || current_adapter || saved_adapter
    grouped = Adapters.grouped()
    all_adapters = Adapters.all()
    all_values = load_all_adapter_values()

    {:ok,
     socket
     |> assign(:page_title, "Email settings")
     |> assign(:env_locked, env_locked)
     |> assign(:adapter_key, adapter_key)
     |> assign(:all_values, all_values)
     |> assign(:all_email_adapters, grouped[:all_email] || [])
     |> assign(:advanced_adapters, grouped[:advanced] || [])
     |> assign(:all_adapters, all_adapters)
     |> assign(:email_configured, Mailer.email_configured?())
     |> assign(:selected_adapter, adapter_key && Adapters.get(adapter_key))
     |> assign(:sending_test, false)
     |> assign(:test_result, if(Mailer.email_verified?(), do: :ok))
     |> assign(:test_error, nil)
     |> assign(:test_retryable, false)
     |> assign(:from_checklist, false)
     |> assign(:field_errors, flash_errors)
     |> assign(:save_status, :idle)
     |> assign(:form, to_form(%{}, as: :email))}
  end

  @impl true
  def handle_params(params, _uri, socket) do
    {:noreply, assign(socket, :from_checklist, params["from"] == "checklist")}
  end

  defp load_all_adapter_values do
    for adapter <- Adapters.all(), into: %{} do
      values =
        for field <- adapter.fields, into: %{} do
          settings_key = Adapters.settings_key(adapter.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.key, values}
    end
  end

  @impl true
  def handle_event("form_change", %{"email" => %{"adapter" => key}}, socket) do
    if key == socket.assigns.adapter_key do
      {:noreply, assign(socket, :save_status, :idle)}
    else
      {:noreply,
       socket
       |> assign(:adapter_key, key)
       |> assign(:selected_adapter, Adapters.get(key))
       |> assign(:field_errors, %{})
       |> assign(:test_result, nil)
       |> assign(:test_error, nil)
       |> assign(:save_status, :idle)}
    end
  end

  def handle_event("save", %{"email" => params}, socket) do
    if socket.assigns.env_locked do
      {:noreply, put_flash(socket, :error, "Email config is controlled by environment variables")}
    else
      adapter_key = params["adapter"]
      # Fields are namespaced: email[brevo][api_key] → params["brevo"]["api_key"]
      adapter_params = params[adapter_key] || %{}

      case Mailer.save_config(
             adapter_key,
             adapter_params,
             socket.assigns.current_scope.user.email
           ) do
        {:ok, _adapter_info} ->
          {:noreply,
           socket
           |> assign(:adapter_key, adapter_key)
           |> assign(:selected_adapter, Adapters.get(adapter_key))
           |> assign(:all_values, load_all_adapter_values())
           |> assign(:email_configured, Mailer.email_configured?())
           |> assign(:field_errors, %{})
           |> assign(:test_result, nil)
           |> assign(:test_error, nil)
           |> assign(:save_status, :saved)}

        {:error, field_errors} when is_map(field_errors) ->
          {:noreply, assign(socket, :field_errors, field_errors)}
      end
    end
  end

  def handle_event("send_test", _params, socket) do
    send(self(), :do_send_test)
    {:noreply, assign(socket, :sending_test, true)}
  end

  @impl true
  def handle_info(:do_send_test, socket) do
    user = socket.assigns.current_scope.user

    case Mailer.send_test_email(user.email, Mailer.from_address()) do
      {:ok, _} ->
        Mailer.mark_email_verified()

        {:noreply,
         socket
         |> assign(:sending_test, false)
         |> assign(:test_result, :ok)
         |> assign(:test_error, nil)}

      {:error, reason} ->
        {:noreply,
         socket
         |> assign(:sending_test, false)
         |> assign(:test_result, :error)
         |> assign(:test_error, Mailer.friendly_error(reason))
         |> assign(:test_retryable, Mailer.retryable_error?(reason))}
    end
  end

  # Swoosh test adapter sends {:email, ...} messages — ignore them
  def handle_info({:email, _}, socket), do: {:noreply, socket}

  @impl true
  def render(assigns) do
    ~H"""
    <div class="admin-content-medium">
      <div :if={@from_checklist} class="admin-checklist-banner">
        <.icon name="hero-clipboard-document-check" class="size-5 admin-checklist-banner-icon" />
        <span class="admin-checklist-banner-text">
          You're setting up email for your shop.
        </span>
        <.link navigate={~p"/admin"} class="admin-link admin-checklist-banner-link">
          &larr; Back to checklist
        </.link>
      </div>
      <.header>
        Email settings
        <:subtitle>
          Your shop needs an email provider to send order confirmations,
          shipping updates, and newsletters to your customers.
        </:subtitle>
      </.header>

      <%= if @env_locked do %>
        <div class="admin-callout-warning">
          <div class="admin-callout-warning-body">
            <span class="admin-callout-warning-icon">
              <.icon name="hero-lock-closed" class="size-5" />
            </span>
            <div>
              <p class="admin-callout-warning-title">
                Controlled by environment variables
              </p>
              <p class="admin-callout-warning-desc">
                Email is configured via <code>SMTP_HOST</code> and related env vars.
                Remove them to configure email from this page instead.
              </p>
            </div>
          </div>
        </div>
      <% end %>

      <section class="admin-section">
        <.form
          for={@form}
          action={~p"/admin/settings/email"}
          method="post"
          phx-change="form_change"
          phx-submit="save"
        >
          <%!-- Step 1: Choose a provider --%>
          <div class="admin-setup-step">
            <h2 class="admin-setup-step-header">
              <span class="admin-setup-step-number">1</span> Choose a provider
            </h2>

            <fieldset class="card-radio-fieldset" disabled={@env_locked}>
              <legend class="sr-only">Email provider</legend>

              <div class="card-radio-grid">
                <.provider_card
                  :for={adapter <- @all_email_adapters}
                  adapter={adapter}
                  selected={@adapter_key}
                  disabled={@env_locked}
                />
              </div>

              <details
                class="admin-provider-other"
                open={@adapter_key in Enum.map(@advanced_adapters, & &1.key)}
              >
                <summary class="admin-provider-other-toggle">
                  Already have your own email server?
                </summary>
                <div class="card-radio-grid">
                  <.provider_card
                    :for={adapter <- @advanced_adapters}
                    adapter={adapter}
                    selected={@adapter_key}
                    disabled={@env_locked}
                  />
                </div>
              </details>
            </fieldset>
          </div>

          <%!-- Steps 2 & 3: config for each adapter (hidden keeps HTML correct without CSS) --%>
          <.adapter_config
            :for={adapter <- @all_adapters}
            adapter={adapter}
            selected={@adapter_key == adapter.key}
            values={@all_values[adapter.key] || %{}}
            field_errors={if(@adapter_key == adapter.key, do: @field_errors, else: %{})}
            env_locked={@env_locked}
            save_status={if(@adapter_key == adapter.key, do: @save_status, else: :idle)}
          />
        </.form>
      </section>

      <%!-- Step 4: Send a test email (only after config saved) --%>
      <div
        :if={@email_configured}
        id="test-email-step"
        class="admin-setup-step admin-setup-step-spaced"
      >
        <h2 class="admin-setup-step-header">
          <span class={[
            "admin-setup-step-number",
            @test_result == :ok && "admin-setup-step-number-done",
            @test_result == :error && "admin-setup-step-number-error"
          ]}>
            <%= cond do %>
              <% @test_result == :ok -> %>
                <.icon name="hero-check-mini" class="size-4" />
              <% @test_result == :error -> %>
                <.icon name="hero-x-mark-mini" class="size-4" />
              <% true -> %>
                {if @selected_adapter && @selected_adapter.url, do: "4", else: "3"}
            <% end %>
          </span>
          <%= cond do %>
            <% @test_result == :ok -> %>
              Email is working
            <% @test_result == :error -> %>
              Test failed
            <% true -> %>
              Send a test email
          <% end %>
        </h2>

        <%= if @test_result == :ok do %>
          <p class="admin-setup-step-desc">
            Test email sent to <strong>{@current_scope.user.email}</strong>.
            Check your inbox to confirm it arrived.
          </p>
          <div class="admin-row admin-row-sm">
            <.link
              :if={@from_checklist}
              navigate={~p"/admin"}
              class="admin-btn admin-btn-primary admin-btn-sm"
            >
              Continue setup &rarr;
            </.link>
            <.button type="button" phx-click="send_test">
              Send again
            </.button>
          </div>
        <% else %>
          <%= if @test_result == :error do %>
            <p class="admin-test-error">{@test_error}</p>
            <div class="admin-row admin-row-sm">
              <%= if @test_retryable do %>
                <.button
                  type="button"
                  phx-click="send_test"
                  disabled={@sending_test}
                  class="admin-btn admin-btn-outline admin-btn-error"
                >
                  <.icon name="hero-paper-airplane" class="size-4" />
                  {if @sending_test, do: "Sending...", else: "Try again"}
                </.button>
              <% else %>
                <p class="admin-setup-step-desc">
                  Fix your settings above and reconnect, then try the test again.
                </p>
              <% end %>
            </div>
          <% else %>
            <p class="admin-setup-step-desc">
              Send a test to <strong>{@current_scope.user.email}</strong> to check everything works.
            </p>
            <.form
              for={%{}}
              action={~p"/admin/settings/email/test"}
              phx-submit="send_test"
              method="post"
              class="admin-row admin-row-sm"
            >
              <.button disabled={@sending_test} phx-disable-with="Sending...">
                <.icon name="hero-paper-airplane" class="size-4" /> Send test email
              </.button>
            </.form>
          <% end %>
        <% end %>
      </div>
    </div>
    """
  end

  # ── Local components ──

  attr :adapter, :map, required: true
  attr :selected, :boolean, default: false
  attr :values, :map, required: true
  attr :field_errors, :map, required: true
  attr :env_locked, :boolean, required: true
  attr :save_status, :atom, default: :idle

  defp adapter_config(assigns) do
    ~H"""
    <details open={@selected} class="admin-adapter-config" data-adapter={@adapter.key}>
      <summary class="admin-adapter-config-summary">Configure {@adapter.name}</summary>
      <%!-- Create an account (providers with sign-up URLs) --%>
      <div :if={@adapter.url} class="admin-setup-step">
        <h2 class="admin-setup-step-header">
          <span class="admin-setup-step-number">2</span> Create a free account
        </h2>
        <p class="admin-setup-step-desc">
          <.external_link href={@adapter.url} class="admin-link">
            Sign up at {@adapter.name} &nearr;
          </.external_link>
          if you don't already have an account. It's free.
        </p>
      </div>

      <%!-- Paste your key / server details --%>
      <div class="admin-setup-step">
        <h2 class="admin-setup-step-header">
          <span class="admin-setup-step-number">
            {if @adapter.url, do: "3", else: "2"}
          </span>
          {adapter_fields_title(@adapter)}
        </h2>
        <p class="admin-setup-step-desc">{adapter_fields_instruction(@adapter)}</p>
        <%= for field <- @adapter.fields do %>
          <.adapter_field_input
            adapter_key={@adapter.key}
            field_def={field}
            value={@values[field.key]}
            disabled={@env_locked}
            error={@field_errors[field.key]}
          />
        <% end %>
        <%= unless @env_locked do %>
          <div class="admin-row admin-row-lg">
            <.button phx-disable-with="Saving...">
              Save settings
            </.button>
            <.inline_feedback status={@save_status} message="Now send a test email" />
          </div>
        <% end %>
      </div>
    </details>
    """
  end

  defp adapter_fields_title(%{key: "smtp"}), do: "Enter your server details"
  defp adapter_fields_title(%{key: "postal"}), do: "Enter your server details"
  defp adapter_fields_title(%{key: "mailjet"}), do: "Paste your API keys"
  defp adapter_fields_title(_adapter), do: "Paste your API key"

  defp adapter_fields_instruction(%{key: "smtp"}),
    do: "Enter your SMTP server connection details below."

  defp adapter_fields_instruction(%{key: "postal"}),
    do: "Enter your Postal server URL and API key below."

  defp adapter_fields_instruction(%{key: "mailjet"}),
    do: "Find your API key and secret key under API Key Management in your Mailjet account."

  defp adapter_fields_instruction(%{key: "mailgun"}),
    do: "Find your API key in your Mailgun dashboard, and enter your sending domain."

  defp adapter_fields_instruction(adapter),
    do: "Find your API key in your #{adapter.name} account settings and paste it here."

  attr :adapter, :map, required: true
  attr :selected, :string, default: nil
  attr :disabled, :boolean, default: false

  defp provider_card(assigns) do
    ~H"""
    <label class="card-radio-card">
      <input
        type="radio"
        id={"email-adapter-#{@adapter.key}"}
        name="email[adapter]"
        value={@adapter.key}
        checked={@selected == @adapter.key}
        disabled={@disabled}
        class="card-radio-input"
      />
      <span class="card-radio-name">
        <strong>{@adapter.name}</strong>
        <span :if={@adapter.recommended} class="card-radio-badge card-radio-recommended">
          Recommended
        </span>
      </span>
      <div :if={@adapter.free_tier} class="card-radio-description">{@adapter.free_tier}</div>
      <div :if={@adapter.setup_hint} class="card-radio-description">{@adapter.setup_hint}</div>
    </label>
    """
  end

  # ── Field renderers ──
  # Fields are namespaced per adapter: email[brevo][api_key], email[smtp][relay], etc.

  attr :adapter_key, :string, required: true
  attr :field_def, :map, required: true
  attr :value, :any, default: nil
  attr :disabled, :boolean, default: false
  attr :error, :string, default: nil

  defp adapter_field_input(%{field_def: %{type: :secret}} = assigns) do
    assigns = assign(assigns, :errors, if(assigns.error, do: [assigns.error], else: []))

    ~H"""
    <.input
      name={"email[#{@adapter_key}][#{@field_def.key}]"}
      value=""
      type="text"
      label={@field_def.label}
      autocomplete="off"
      placeholder={@value || ""}
      disabled={@disabled}
      errors={@errors}
    />
    """
  end

  defp adapter_field_input(%{field_def: %{type: :integer}} = assigns) do
    assigns = assign(assigns, :errors, if(assigns.error, do: [assigns.error], else: []))

    ~H"""
    <.input
      name={"email[#{@adapter_key}][#{@field_def.key}]"}
      value={@value || @field_def.default || ""}
      type="number"
      label={@field_def.label}
      disabled={@disabled}
      errors={@errors}
    />
    """
  end

  defp adapter_field_input(assigns) do
    assigns = assign(assigns, :errors, if(assigns.error, do: [assigns.error], else: []))

    ~H"""
    <.input
      name={"email[#{@adapter_key}][#{@field_def.key}]"}
      value={@value || @field_def.default || ""}
      type="text"
      label={@field_def.label}
      disabled={@disabled}
      errors={@errors}
    />
    """
  end
end
