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

  alias Berrypod.Accounts

  @impl true
  def mount(_params, session, socket) do
    user = socket.assigns.current_scope.user

    # Restore TOTP setup from session if it exists (persists across reconnects)
    totp_setup =
      case session["totp_setup_secret"] do
        nil -> nil
        secret -> %{secret: secret, uri: Accounts.totp_uri(user, secret)}
      end

    # Check for backup codes from successful TOTP enablement
    backup_codes = session["totp_backup_codes"]

    email_changeset = Accounts.change_user_email(user, %{}, validate_unique: false)
    password_changeset = Accounts.change_user_password(user, %{}, hash_password: false)

    {:ok,
     socket
     |> assign(:page_title, "Account")
     |> assign(:current_email, user.email)
     |> assign(:email_form, to_form(email_changeset))
     |> assign(:password_form, to_form(password_changeset))
     |> assign(:trigger_submit, false)
     |> assign(:totp_enabled, Accounts.totp_enabled?(user))
     |> assign(:totp_setup, totp_setup)
     |> assign(:totp_form, to_form(%{"code" => ""}, as: :totp))
     |> assign(:backup_codes, backup_codes)
     |> assign(:disable_totp_form, nil)}
  end

  # -- Events: email --

  @impl true
  def handle_event("validate_email", %{"user" => user_params}, socket) do
    email_form =
      socket.assigns.current_scope.user
      |> Accounts.change_user_email(user_params, validate_unique: false)
      |> Map.put(:action, :validate)
      |> to_form()

    {:noreply, assign(socket, email_form: email_form)}
  end

  def handle_event("update_email", %{"user" => user_params}, socket) do
    user = socket.assigns.current_scope.user

    unless Accounts.sudo_mode?(user) do
      {:noreply,
       socket
       |> put_flash(:error, "Please log in again to change account settings.")
       |> redirect(to: ~p"/users/log-in?return_to=/admin/account")}
    else
      case Accounts.change_user_email(user, user_params) do
        %{valid?: true} = changeset ->
          Accounts.deliver_user_update_email_instructions(
            Ecto.Changeset.apply_action!(changeset, :insert),
            user.email,
            &url(~p"/users/settings/confirm-email/#{&1}")
          )

          info = "A link to confirm your email change has been sent to the new address."
          {:noreply, put_flash(socket, :info, info)}

        changeset ->
          {:noreply, assign(socket, :email_form, to_form(changeset, action: :insert))}
      end
    end
  end

  # -- Events: password --

  def handle_event("validate_password", %{"user" => user_params}, socket) do
    password_form =
      socket.assigns.current_scope.user
      |> Accounts.change_user_password(user_params, hash_password: false)
      |> Map.put(:action, :validate)
      |> to_form()

    {:noreply, assign(socket, password_form: password_form)}
  end

  def handle_event("update_password", %{"user" => user_params}, socket) do
    user = socket.assigns.current_scope.user

    unless Accounts.sudo_mode?(user) do
      {:noreply,
       socket
       |> put_flash(:error, "Please log in again to change account settings.")
       |> redirect(to: ~p"/users/log-in?return_to=/admin/account")}
    else
      case Accounts.change_user_password(user, user_params) do
        %{valid?: true} = changeset ->
          {:noreply, assign(socket, trigger_submit: true, password_form: to_form(changeset))}

        changeset ->
          {:noreply, assign(socket, password_form: to_form(changeset, action: :insert))}
      end
    end
  end

  # -- Events: 2FA --
  # Note: start_totp_setup and cancel_totp_setup are handled by the controller
  # (POST /admin/account/totp/start and /admin/account/totp/cancel) to persist
  # the secret in the session, which survives LiveView reconnects on mobile.

  def handle_event("verify_totp", %{"totp" => %{"code" => code}}, socket) do
    user = socket.assigns.current_scope.user
    setup = socket.assigns.totp_setup

    case Accounts.enable_totp(user, setup.secret, code) do
      {:ok, _user, backup_codes} ->
        # Redirect through controller to clear the session and pass backup codes
        # This ensures reconnects don't show the setup flow again
        codes_param = Enum.join(backup_codes, ",")

        {:noreply,
         socket
         |> put_flash(:info, "Two-factor authentication enabled")
         |> redirect(to: ~p"/admin/account/totp/complete?codes=#{codes_param}")}

      {:error, :invalid_code} ->
        {:noreply,
         socket
         |> assign(:totp_form, to_form(%{"code" => ""}, as: :totp))
         |> put_flash(:error, "Invalid code. Please try again.")}

      {:error, _} ->
        {:noreply, put_flash(socket, :error, "Something went wrong. Please try again.")}
    end
  end

  def handle_event("start_disable_totp", _params, socket) do
    user = socket.assigns.current_scope.user

    unless Accounts.sudo_mode?(user) do
      {:noreply,
       socket
       |> put_flash(:error, "Please log in again to disable 2FA.")
       |> redirect(to: ~p"/users/log-in?return_to=/admin/account")}
    else
      {:noreply, assign(socket, :disable_totp_form, to_form(%{"code" => ""}, as: :disable_totp))}
    end
  end

  def handle_event("cancel_disable_totp", _params, socket) do
    {:noreply, assign(socket, :disable_totp_form, nil)}
  end

  def handle_event("confirm_disable_totp", %{"disable_totp" => %{"code" => code}}, socket) do
    user = socket.assigns.current_scope.user

    unless Accounts.sudo_mode?(user) do
      {:noreply,
       socket
       |> put_flash(:error, "Please log in again to disable 2FA.")
       |> redirect(to: ~p"/users/log-in?return_to=/admin/account")}
    else
      case Accounts.verify_totp(user, code) do
        :ok ->
          case Accounts.disable_totp(user) do
            {:ok, _user} ->
              {:noreply,
               socket
               |> assign(:totp_enabled, false)
               |> assign(:disable_totp_form, nil)
               |> put_flash(:info, "Two-factor authentication disabled")}

            {:error, _} ->
              {:noreply, put_flash(socket, :error, "Something went wrong. Please try again.")}
          end

        :error ->
          {:noreply,
           socket
           |> assign(:disable_totp_form, to_form(%{"code" => ""}, as: :disable_totp))
           |> put_flash(:error, "Invalid code. Please try again.")}
      end
    end
  end

  # -- Render --

  @impl true
  def render(assigns) do
    ~H"""
    <div class="admin-settings">
      <.header>
        Account
      </.header>

      <%!-- Email --%>
      <section class="admin-section">
        <h2 class="admin-section-title">Email</h2>
        <div class="admin-section-body">
          <.form
            for={@email_form}
            id="email_form"
            phx-submit="update_email"
            phx-change="validate_email"
          >
            <.input
              field={@email_form[:email]}
              type="email"
              label="Email address"
              autocomplete="username"
              required
            />
            <div class="admin-form-actions-sm">
              <.button phx-disable-with="Saving...">Change email</.button>
            </div>
          </.form>
        </div>
      </section>

      <%!-- Password --%>
      <section class="admin-section">
        <h2 class="admin-section-title">Password</h2>
        <div class="admin-section-body">
          <.form
            for={@password_form}
            id="password_form"
            action={~p"/users/update-password"}
            method="post"
            phx-change="validate_password"
            phx-submit="update_password"
            phx-trigger-action={@trigger_submit}
          >
            <input
              name={@password_form[:email].name}
              type="hidden"
              id="hidden_user_email"
              autocomplete="username"
              value={@current_email}
            />
            <.input
              field={@password_form[:password]}
              type="password"
              label="New password"
              autocomplete="new-password"
              required
            />
            <.input
              field={@password_form[:password_confirmation]}
              type="password"
              label="Confirm new password"
              autocomplete="new-password"
            />
            <div class="admin-form-actions-sm">
              <.button phx-disable-with="Saving...">Change password</.button>
            </div>
          </.form>
        </div>
      </section>

      <%!-- Two-factor authentication --%>
      <section class="admin-section">
        <div class="admin-section-header">
          <h2 class="admin-section-title">Two-factor authentication</h2>
          <%= if @totp_enabled do %>
            <.status_pill color="green">
              <.icon name="hero-check-circle-mini" class="size-3" /> Enabled
            </.status_pill>
          <% else %>
            <.status_pill color="zinc">Off</.status_pill>
          <% end %>
        </div>

        <%= if @backup_codes do %>
          <%!-- Show backup codes after enabling --%>
          <div class="admin-section-body">
            <div class="admin-info-box admin-info-box-amber">
              <p class="admin-text-bold">Save your backup codes</p>
              <p>
                These codes can be used to access your account if you lose your authenticator.
                Each code can only be used once. Store them somewhere safe.
              </p>
              <div class="admin-backup-codes">
                <%= for code <- @backup_codes do %>
                  <code class="admin-backup-code">{code}</code>
                <% end %>
              </div>
              <div class="admin-form-actions-sm">
                <.link
                  href={~p"/admin/account/totp/dismiss-codes"}
                  method="post"
                  data-confirm="Are you sure? You won't be able to see these codes again."
                  class="admin-btn admin-btn-primary admin-btn-sm"
                >
                  I've saved these codes
                </.link>
              </div>
            </div>
          </div>
        <% else %>
          <%= if @totp_setup do %>
            <%!-- Setup flow --%>
            <div class="admin-section-body">
              <p class="admin-section-desc admin-section-desc-flush">
                Add this account to your authenticator app, then enter the 6-digit code to verify.
              </p>

              <div class="admin-totp-setup">
                <%!-- QR code for scanning --%>
                <div class="admin-totp-qr">
                  <div class="admin-qr-code">
                    {raw(qr_code_svg(@totp_setup.uri))}
                  </div>
                  <p class="admin-help-text">
                    Scan with your authenticator app
                  </p>
                </div>

                <div class="admin-totp-divider">
                  <span>or copy the key</span>
                </div>

                <%!-- Copyable secret key --%>
                <div class="admin-totp-copy">
                  <div class="admin-copy-field">
                    <code
                      id="totp-secret"
                      class="admin-code-break"
                    >
                      {Base.encode32(@totp_setup.secret)}
                    </code>
                    <button
                      id="copy-totp-secret"
                      type="button"
                      phx-hook="Clipboard"
                      data-copy-target="totp-secret"
                      class="admin-btn admin-btn-outline admin-btn-sm"
                    >
                      <.icon name="hero-clipboard" class="size-4" />
                      <span>Copy</span>
                    </button>
                  </div>
                  <p class="admin-help-text">
                    Paste into your authenticator app as a manual entry (time-based/TOTP)
                  </p>
                </div>
              </div>

              <.form for={@totp_form} phx-submit="verify_totp" class="admin-stack">
                <.input
                  field={@totp_form[:code]}
                  type="text"
                  label="Verification code"
                  placeholder="000000"
                  inputmode="numeric"
                  pattern="[0-9]*"
                  autocomplete="one-time-code"
                  maxlength="6"
                />
                <div class="admin-cluster">
                  <.button phx-disable-with="Verifying...">Enable 2FA</.button>
                  <.link
                    href={~p"/admin/account/totp/cancel"}
                    method="post"
                    class="admin-btn admin-btn-outline"
                  >
                    Cancel
                  </.link>
                </div>
              </.form>
            </div>
          <% else %>
            <%!-- Enable/disable --%>
            <%= if @disable_totp_form do %>
              <%!-- Verification form for disabling --%>
              <div class="admin-section-body">
                <p class="admin-section-desc admin-section-desc-flush">
                  Enter a code from your authenticator app to confirm disabling 2FA.
                </p>
                <.form for={@disable_totp_form} phx-submit="confirm_disable_totp" class="admin-stack">
                  <.input
                    field={@disable_totp_form[:code]}
                    type="text"
                    label="Verification code"
                    placeholder="000000"
                    inputmode="numeric"
                    pattern="[0-9]*"
                    autocomplete="one-time-code"
                    maxlength="6"
                  />
                  <div class="admin-cluster">
                    <.button class="admin-btn-danger" phx-disable-with="Disabling...">
                      Disable 2FA
                    </.button>
                    <button
                      type="button"
                      phx-click="cancel_disable_totp"
                      class="admin-btn admin-btn-outline"
                    >
                      Cancel
                    </button>
                  </div>
                </.form>
              </div>
            <% else %>
              <p class="admin-section-desc">
                <%= if @totp_enabled do %>
                  Your account is protected with two-factor authentication.
                <% else %>
                  Add an extra layer of security by requiring a code from your authenticator app when logging in.
                <% end %>
              </p>
              <div class="admin-section-body">
                <%= if @totp_enabled do %>
                  <button
                    phx-click="start_disable_totp"
                    class="admin-btn admin-btn-outline admin-btn-sm"
                  >
                    Disable 2FA
                  </button>
                <% else %>
                  <.link
                    href={~p"/admin/account/totp/start"}
                    method="post"
                    class="admin-btn admin-btn-primary admin-btn-sm"
                  >
                    <.icon name="hero-shield-check-mini" class="size-4" /> Enable 2FA
                  </.link>
                <% end %>
              </div>
            <% end %>
          <% end %>
        <% end %>
      </section>
    </div>
    """
  end

  # -- Function components --

  attr :color, :string, required: true
  slot :inner_block, required: true

  defp status_pill(assigns) do
    modifier =
      case assigns.color do
        "green" -> "admin-status-pill-green"
        "amber" -> "admin-status-pill-amber"
        _ -> "admin-status-pill-zinc"
      end

    assigns = assign(assigns, :modifier, modifier)

    ~H"""
    <span class={["admin-status-pill", @modifier]}>
      {render_slot(@inner_block)}
    </span>
    """
  end

  defp qr_code_svg(uri) do
    uri
    |> EQRCode.encode()
    |> EQRCode.svg(width: 200)
  end
end
