defmodule BerrypodWeb.Setup.Recover do
  use BerrypodWeb, :live_view

  require Logger

  alias Berrypod.{Accounts, Setup}

  @impl true
  def mount(_params, _session, socket) do
    cond do
      get_user(socket) ->
        {:ok, push_navigate(socket, to: ~p"/admin")}

      not Accounts.has_admin?() ->
        {:ok, push_navigate(socket, to: ~p"/setup")}

      true ->
        Logger.warning("Account recovery requested. Setup secret: #{Setup.setup_secret()}")

        {:ok,
         socket
         |> assign(:page_title, "Account recovery")
         |> assign(:require_secret, Application.get_env(:berrypod, :env, :dev) == :prod)
         |> assign(:form, to_form(%{}, as: :recover))}
    end
  end

  defp get_user(socket) do
    case socket.assigns do
      %{current_scope: %{user: user}} when not is_nil(user) -> user
      _ -> nil
    end
  end

  @impl true
  def handle_event("recover", %{"recover" => params}, socket) do
    secret = params["secret"] || ""
    password = params["password"] || ""

    cond do
      socket.assigns.require_secret and
          not Plug.Crypto.secure_compare(secret, Setup.setup_secret()) ->
        {:noreply, put_flash(socket, :error, "Wrong setup secret")}

      String.length(password) < 12 ->
        {:noreply, put_flash(socket, :error, "Password must be at least 12 characters")}

      true ->
        user = Accounts.get_first_admin()

        case Accounts.update_user_password(user, %{password: password}) do
          {:ok, {_user, _tokens}} ->
            token = Accounts.generate_login_token(user)
            {:noreply, redirect(socket, to: ~p"/recover/login/#{token}")}

          {:error, changeset} ->
            message =
              changeset
              |> Ecto.Changeset.traverse_errors(fn {msg, _} -> msg end)
              |> Enum.flat_map(fn {_field, msgs} -> msgs end)
              |> Enum.join(", ")

            {:noreply, put_flash(socket, :error, message)}
        end
    end
  end

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash} current_scope={@current_scope}>
      <div class="setup-page">
        <div class="setup-header">
          <.header>
            Account recovery
            <:subtitle>
              Reset your admin password using the setup secret from your server logs.
            </:subtitle>
          </.header>
        </div>

        <div class="admin-alert admin-alert-info">
          <.icon name="hero-information-circle" class="size-6 shrink-0" />
          <p>A recovery secret has been printed to your server logs.</p>
        </div>

        <.form for={@form} phx-submit="recover">
          <.input
            :if={@require_secret}
            name="recover[secret]"
            value=""
            type="password"
            label="Setup secret"
            autocomplete="off"
            required
          />
          <.input
            name="recover[password]"
            value=""
            type="password"
            label="New password"
            autocomplete="new-password"
            required
          />
          <p class="setup-hint">Minimum 12 characters</p>
          <.button variant="primary" class="admin-btn-block">
            Reset password and log in
          </.button>
        </.form>

        <p class="setup-footer">
          <.link navigate={~p"/users/log-in"} class="admin-link">
            Back to login
          </.link>
        </p>
      </div>
    </Layouts.app>
    """
  end
end
