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

  alias Berrypod.{Accounts, KeyValidation, Products, Settings, Setup}
  alias Berrypod.Providers.Provider
  alias Berrypod.Stripe.Setup, as: StripeSetup

  # Steps in the guided flow (after account creation):
  # :intro - explains what's needed
  # :provider - connect print provider
  # :stripe - connect Stripe payments
  # :email - set up email provider

  @guided_steps [:intro, :provider, :stripe, :email]

  # ── Mount ──

  @impl true
  def mount(_params, _session, socket) do
    setup = Setup.setup_status()

    cond do
      setup.site_live ->
        {:ok, push_navigate(socket, to: ~p"/")}

      setup.setup_complete ->
        {:ok, push_navigate(socket, to: ~p"/admin")}

      setup.admin_created and is_nil(get_user(socket)) ->
        {:ok, push_navigate(socket, to: ~p"/users/log-in")}

      true ->
        {:ok, mount_setup(socket, setup)}
    end
  end

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

  defp mount_setup(socket, setup) do
    logged_in? = get_user(socket) != nil
    provider_conn = Products.get_first_provider_connection()

    # Determine which guided step to show (for logged-in users)
    guided_step = determine_guided_step(setup)

    socket
    |> assign(:page_title, "Set up your shop")
    |> assign(:setup, setup)
    |> assign(:logged_in?, logged_in?)
    |> assign(:guided_step, guided_step)
    |> assign(:guided_steps, @guided_steps)
    # Secret gate
    |> assign(:require_secret?, Setup.require_setup_secret?())
    |> assign(:secret_verified, false)
    |> assign(:secret_form, to_form(%{"secret" => ""}, as: :secret))
    # Account (card 1)
    |> assign(
      :account_form,
      to_form(
        %{"email" => "", "password" => "", "password_confirmation" => "", "shop_name" => ""},
        as: :account
      )
    )
    # Provider (card 2)
    |> assign(:providers, Provider.all())
    |> assign(:selected_provider, nil)
    |> assign(:provider_form, to_form(%{"api_key" => ""}, as: :provider))
    |> assign(:provider_connecting, false)
    |> assign(:provider_conn, provider_conn)
    # Stripe (card 3)
    |> assign(:stripe_form, to_form(%{"api_key" => ""}, as: :stripe))
    |> assign(:stripe_connecting, false)
  end

  # Determine which guided step to show based on what's already configured
  defp determine_guided_step(setup) do
    cond do
      # Show intro on first visit after account creation
      not setup.provider_connected and not setup.stripe_connected and
          not setup.email_configured ->
        :intro

      # Resume at first incomplete step
      not setup.provider_connected ->
        :provider

      not setup.stripe_connected ->
        :stripe

      not setup.email_configured ->
        :email

      # All done - will redirect to dashboard
      true ->
        :email
    end
  end

  # ── Events: Secret gate ──

  @impl true
  def handle_event("verify_secret", %{"secret" => %{"secret" => secret}}, socket) do
    if Plug.Crypto.secure_compare(secret, Setup.setup_secret()) do
      {:noreply, assign(socket, secret_verified: true)}
    else
      {:noreply, put_flash(socket, :error, "Wrong setup secret")}
    end
  end

  # ── Events: Guided flow navigation ──

  def handle_event("start_setup", _params, socket) do
    {:noreply, assign(socket, :guided_step, :provider)}
  end

  def handle_event("go_to_step", %{"step" => step}, socket) do
    step = String.to_existing_atom(step)

    if step in @guided_steps do
      {:noreply, assign(socket, :guided_step, step)}
    else
      {:noreply, socket}
    end
  end

  def handle_event("skip_step", _params, socket) do
    next_step = next_guided_step(socket.assigns.guided_step)
    handle_step_completion(socket, next_step)
  end

  def handle_event("go_back", _params, socket) do
    prev_step = prev_guided_step(socket.assigns.guided_step)
    {:noreply, assign(socket, :guided_step, prev_step)}
  end

  # ── Events: Account ──

  def handle_event("validate_account", %{"account" => params}, socket) do
    errors = validate_account_fields(params)

    form =
      to_form(params,
        as: :account,
        errors: errors,
        action: if(errors != [], do: :validate)
      )

    {:noreply, assign(socket, :account_form, form)}
  end

  def handle_event("create_account", %{"account" => params}, socket) do
    email = params["email"]
    password = params["password"]
    password_confirmation = params["password_confirmation"]
    shop_name = String.trim(params["shop_name"] || "")

    errors = validate_account_fields(params)

    cond do
      errors != [] ->
        form = to_form(params, as: :account, errors: errors, action: :validate)
        {:noreply, assign(socket, :account_form, form)}

      password != password_confirmation ->
        form =
          to_form(params,
            as: :account,
            errors: [password_confirmation: {"Passwords don't match", []}],
            action: :validate
          )

        {:noreply, assign(socket, :account_form, form)}

      true ->
        Settings.put_setting("site_name", shop_name, "string")

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

          {:error, :admin_already_exists} ->
            {:noreply,
             socket
             |> put_flash(:error, "An admin account already exists")
             |> push_navigate(to: ~p"/setup")}

          {:error, changeset} ->
            form = to_form(params, as: :account, errors: changeset.errors, action: :validate)

            {:noreply,
             socket
             |> assign(:account_form, form)
             |> put_flash(:error, "Could not create account")}
        end
    end
  end

  # ── Events: Provider ──

  def handle_event("select_provider", %{"provider_select" => %{"type" => type}}, socket) do
    {:noreply,
     socket
     |> assign(:selected_provider, type)
     |> assign(:provider_form, to_form(%{"api_key" => ""}, as: :provider))}
  end

  def handle_event("connect_provider", %{"provider" => %{"api_key" => api_key}}, socket) do
    type = socket.assigns.selected_provider

    case KeyValidation.validate_provider_key(api_key, type) do
      {:error, message} ->
        form =
          to_form(%{"api_key" => api_key},
            as: :provider,
            errors: [api_key: {message, []}],
            action: :validate
          )

        {:noreply, assign(socket, :provider_form, form)}

      {:ok, api_key} ->
        socket = assign(socket, :provider_connecting, true)

        case Products.connect_provider(api_key, type) do
          {:ok, connection} ->
            setup = Setup.setup_status()

            {:noreply,
             socket
             |> assign(:provider_connecting, false)
             |> assign(:provider_conn, connection)
             |> assign(:setup, setup)
             |> assign(:guided_step, :stripe)
             |> put_flash(:info, "Connected! Product sync started in the background.")}

          {:error, :no_api_key} ->
            form =
              to_form(%{"api_key" => api_key},
                as: :provider,
                errors: [api_key: {"Please enter your API token", []}],
                action: :validate
              )

            {:noreply,
             socket
             |> assign(:provider_connecting, false)
             |> assign(:provider_form, form)}

          {:error, _reason} ->
            form =
              to_form(%{"api_key" => api_key},
                as: :provider,
                errors: [api_key: {"Could not connect. Check your API key and try again", []}],
                action: :validate
              )

            {:noreply,
             socket
             |> assign(:provider_connecting, false)
             |> assign(:provider_form, form)}
        end
    end
  end

  # Validate provider key on blur for fast feedback
  def handle_event("validate_provider", %{"provider" => %{"api_key" => api_key}}, socket) do
    type = socket.assigns.selected_provider

    form =
      case KeyValidation.validate_provider_key(api_key, type) do
        {:ok, _} ->
          to_form(%{"api_key" => api_key}, as: :provider)

        {:error, message} ->
          to_form(%{"api_key" => api_key},
            as: :provider,
            errors: [api_key: {message, []}],
            action: :validate
          )
      end

    {:noreply, assign(socket, :provider_form, form)}
  end

  # ── Events: Stripe ──

  # Validate Stripe key on blur for fast feedback
  def handle_event("validate_stripe", %{"stripe" => %{"api_key" => api_key}}, socket) do
    form =
      case KeyValidation.validate_stripe_key(api_key) do
        {:ok, _} ->
          to_form(%{"api_key" => api_key}, as: :stripe)

        {:error, message} ->
          to_form(%{"api_key" => api_key},
            as: :stripe,
            errors: [api_key: {message, []}],
            action: :validate
          )
      end

    {:noreply, assign(socket, :stripe_form, form)}
  end

  def handle_event("connect_stripe", %{"stripe" => %{"api_key" => api_key}}, socket) do
    case KeyValidation.validate_stripe_key(api_key) do
      {:error, message} ->
        form =
          to_form(%{"api_key" => api_key},
            as: :stripe,
            errors: [api_key: {message, []}],
            action: :validate
          )

        {:noreply, assign(socket, :stripe_form, form)}

      {:ok, api_key} ->
        socket = assign(socket, :stripe_connecting, true)

        case StripeSetup.connect(api_key) do
          {:ok, _result} ->
            setup = Setup.setup_status()

            {:noreply,
             socket
             |> assign(:stripe_connecting, false)
             |> assign(:setup, setup)
             |> assign(:guided_step, :email)
             |> put_flash(:info, "Stripe connected")}

          {:error, message} ->
            form =
              to_form(%{"api_key" => api_key},
                as: :stripe,
                errors: [api_key: {"Stripe connection failed: #{message}", []}],
                action: :validate
              )

            {:noreply,
             socket
             |> assign(:stripe_connecting, false)
             |> assign(:stripe_form, form)}
        end
    end
  end

  # ── Navigation helpers ──

  defp next_guided_step(current) do
    case current do
      :intro -> :provider
      :provider -> :stripe
      :stripe -> :email
      :email -> :done
    end
  end

  defp prev_guided_step(current) do
    case current do
      :provider -> :intro
      :stripe -> :provider
      :email -> :stripe
      _ -> current
    end
  end

  defp handle_step_completion(socket, :done) do
    {:noreply,
     socket
     |> put_flash(:info, "Setup complete! Here's your launch checklist.")
     |> push_navigate(to: ~p"/admin")}
  end

  defp handle_step_completion(socket, next_step) do
    {:noreply, assign(socket, :guided_step, next_step)}
  end

  # ── Render ──

  @impl true
  def render(assigns) do
    ~H"""
    <Layouts.flash_group flash={@flash} />
    <div class="setup-page">
      <div class="setup-header">
        <h1 class="setup-title">Set up your shop</h1>
        <%= if @require_secret? and not @secret_verified do %>
          <p class="setup-subtitle">
            Enter the setup secret from your server logs to get started.
          </p>
        <% else %>
          <p class="setup-subtitle">
            Connect your accounts to get going.
          </p>
        <% end %>
      </div>

      <%!-- Secret gate (prod only, before admin exists) --%>
      <%= if @require_secret? and not @secret_verified do %>
        <div class="setup-sections">
          <div class="setup-card">
            <div class="setup-card-body">
              <p class="setup-hint">
                Find the setup secret in your server logs or set the <code>SETUP_SECRET</code>
                environment variable.
              </p>
              <.form for={@secret_form} phx-submit="verify_secret">
                <.input
                  field={@secret_form[:secret]}
                  type="password"
                  label="Setup secret"
                  autocomplete="off"
                  required
                  phx-mounted={JS.focus()}
                />
                <div class="setup-actions">
                  <.button phx-disable-with="Checking...">Continue</.button>
                </div>
              </.form>
            </div>
          </div>
        </div>
      <% else %>
        <%!-- Account creation (before login) --%>
        <div :if={not @logged_in?} class="setup-sections">
          <.section_card
            title="Set up your account"
            number={1}
            done={false}
            summary={nil}
          >
            <p class="setup-hint">Name your shop and create the admin account.</p>
            <.form for={@account_form} phx-submit="create_account" phx-change="validate_account">
              <.input
                field={@account_form[:shop_name]}
                type="text"
                label="Shop name"
                placeholder="e.g. Acme Prints"
                autocomplete="off"
                required
                phx-mounted={JS.focus()}
              />
              <p class="setup-field-note">You can change this later</p>
              <.input
                field={@account_form[:email]}
                type="email"
                label="Email address"
                autocomplete="email"
                required
              />
              <.input
                field={@account_form[:password]}
                type="password"
                label="Password"
                placeholder="12 characters minimum"
                autocomplete="new-password"
                required
              />
              <.input
                field={@account_form[:password_confirmation]}
                type="password"
                label="Confirm password"
                autocomplete="new-password"
                required
              />
              <div class="setup-actions">
                <.button phx-disable-with="Creating account...">Create account</.button>
              </div>
            </.form>
          </.section_card>
        </div>

        <%!-- Guided setup flow (after login) --%>
        <div :if={@logged_in?} class="setup-guided">
          <%!-- Progress bar --%>
          <.progress_bar
            current_step={@guided_step}
            setup={@setup}
          />

          <%!-- Intro screen --%>
          <div :if={@guided_step == :intro} class="setup-intro">
            <div class="setup-intro-content">
              <p class="setup-intro-lead">
                Berrypod connects your print-on-demand products to your own online shop.
                To get fully set up, you'll need three things:
              </p>
              <ul class="setup-intro-list">
                <li>
                  <.icon name="hero-cube" class="setup-intro-icon" />
                  <div>
                    <strong>A print provider account</strong>
                    <span>(like Printify or Printful) to make and ship your products</span>
                  </div>
                </li>
                <li>
                  <.icon name="hero-credit-card" class="setup-intro-icon" />
                  <div>
                    <strong>A Stripe account</strong>
                    <span>to accept payments from customers</span>
                  </div>
                </li>
                <li>
                  <.icon name="hero-envelope" class="setup-intro-icon" />
                  <div>
                    <strong>An email provider account</strong>
                    <span>so your shop can send order confirmations and shipping updates</span>
                  </div>
                </li>
              </ul>
              <p class="setup-intro-note">
                Don't worry if you don't have all of these yet — you can skip any step and set it up later.
              </p>
            </div>
            <div class="setup-actions setup-actions-center">
              <.button phx-click="start_setup">
                Let's get started <span aria-hidden="true">&rarr;</span>
              </.button>
            </div>
          </div>

          <%!-- Step 1: Provider --%>
          <div :if={@guided_step == :provider} class="setup-step">
            <.step_header
              number={1}
              title="Connect a print provider"
              done={@setup.provider_connected}
            />
            <div class="setup-step-body">
              <.provider_section
                providers={@providers}
                selected={@selected_provider}
                form={@provider_form}
                connecting={@provider_connecting}
              />
            </div>
            <.step_nav
              step={:provider}
              can_skip={true}
              skip_note="You won't be able to import products until you connect a provider."
            />
          </div>

          <%!-- Step 2: Stripe --%>
          <div :if={@guided_step == :stripe} class="setup-step">
            <.step_header
              number={2}
              title="Connect Stripe for payments"
              done={@setup.stripe_connected}
            />
            <div class="setup-step-body">
              <.stripe_section
                form={@stripe_form}
                connecting={@stripe_connecting}
                focus={true}
              />
            </div>
            <.step_nav
              step={:stripe}
              can_skip={true}
              skip_note="Customers won't be able to checkout until you connect Stripe."
            />
          </div>

          <%!-- Step 3: Email --%>
          <div :if={@guided_step == :email} class="setup-step">
            <.step_header
              number={3}
              title="Set up email"
              done={@setup.email_configured}
            />
            <div class="setup-step-body">
              <p class="setup-hint">
                Configure an email provider so your shop can send order confirmations,
                shipping updates, and newsletters.
              </p>
              <%= if @setup.email_configured do %>
                <div class="setup-step-done">
                  <.icon name="hero-check-circle" class="setup-step-done-icon" />
                  <p>Email is already configured.</p>
                </div>
                <div class="setup-actions">
                  <.button phx-click="skip_step">
                    Continue <span aria-hidden="true">&rarr;</span>
                  </.button>
                </div>
              <% else %>
                <p class="setup-hint">
                  <.link navigate={~p"/admin/settings/email"} class="setup-link">
                    Set up email in settings <span aria-hidden="true">&rarr;</span>
                  </.link>
                </p>
              <% end %>
            </div>
            <.step_nav
              step={:email}
              can_skip={true}
              skip_note="Order confirmations and shipping updates won't be sent."
              finish_button={true}
            />
          </div>
        </div>
      <% end %>
    </div>
    """
  end

  # ── Section card component ──

  attr :title, :string, required: true
  attr :number, :integer, required: true
  attr :done, :boolean, required: true
  attr :summary, :any, default: nil

  slot :inner_block, required: true

  defp section_card(assigns) do
    ~H"""
    <div class={["setup-card", @done && "setup-card-done"]}>
      <div class="setup-card-header">
        <span class={["setup-card-number", @done && "setup-card-number-done"]}>
          <%= if @done do %>
            <.icon name="hero-check-mini" class="size-4" />
          <% else %>
            {@number}
          <% end %>
        </span>
        <h2 class="setup-card-title">{@title}</h2>
      </div>

      <%= if @done and @summary do %>
        <p class="setup-card-summary">{@summary}</p>
      <% else %>
        <div :if={!@done} class="setup-card-body">
          {render_slot(@inner_block)}
        </div>
      <% end %>
    </div>
    """
  end

  # ── Progress bar component ──

  attr :current_step, :atom, required: true
  attr :setup, :map, required: true

  defp progress_bar(assigns) do
    steps = [
      %{key: :provider, label: "Provider", done: assigns.setup.provider_connected},
      %{key: :stripe, label: "Payments", done: assigns.setup.stripe_connected},
      %{key: :email, label: "Email", done: assigns.setup.email_configured}
    ]

    current_index =
      case assigns.current_step do
        :intro -> -1
        :provider -> 0
        :stripe -> 1
        :email -> 2
        _ -> -1
      end

    assigns = assign(assigns, steps: steps, current_index: current_index)

    ~H"""
    <nav class="setup-progress" aria-label="Setup progress">
      <ol class="setup-progress-list">
        <%= for {step, index} <- Enum.with_index(@steps) do %>
          <li class={[
            "setup-progress-item",
            step.done && "setup-progress-done",
            index == @current_index && "setup-progress-current"
          ]}>
            <button
              type="button"
              phx-click="go_to_step"
              phx-value-step={step.key}
              class="setup-progress-button"
              aria-current={index == @current_index && "step"}
              disabled={@current_step == :intro}
            >
              <span class="setup-progress-indicator">
                <%= if step.done do %>
                  <.icon name="hero-check-mini" class="size-4" />
                <% else %>
                  {index + 1}
                <% end %>
              </span>
              <span class="setup-progress-label">{step.label}</span>
            </button>
          </li>
        <% end %>
      </ol>
    </nav>
    """
  end

  # ── Step header component ──

  attr :number, :integer, required: true
  attr :title, :string, required: true
  attr :done, :boolean, default: false

  defp step_header(assigns) do
    ~H"""
    <div class="setup-step-header">
      <span class={["setup-step-number", @done && "setup-step-number-done"]}>
        <%= if @done do %>
          <.icon name="hero-check-mini" class="size-5" />
        <% else %>
          {@number}
        <% end %>
      </span>
      <h2 class="setup-step-title">{@title}</h2>
    </div>
    """
  end

  # ── Step navigation component ──

  attr :step, :atom, required: true
  attr :can_skip, :boolean, default: false
  attr :skip_note, :string, default: nil
  attr :finish_button, :boolean, default: false

  defp step_nav(assigns) do
    ~H"""
    <div class="setup-step-nav">
      <div class="setup-step-nav-left">
        <%= if @step != :provider do %>
          <button type="button" phx-click="go_back" class="setup-step-back">
            <.icon name="hero-arrow-left-mini" class="size-4" /> Back
          </button>
        <% end %>
      </div>
      <div class="setup-step-nav-right">
        <%= if @can_skip do %>
          <div class="setup-step-skip">
            <button type="button" phx-click="skip_step" class="setup-step-skip-btn">
              <%= if @finish_button do %>
                Finish setup
              <% else %>
                Skip for now
              <% end %>
            </button>
            <%= if @skip_note do %>
              <p class="setup-step-skip-note">{@skip_note}</p>
            <% end %>
          </div>
        <% end %>
      </div>
    </div>
    """
  end

  # ── Provider section ──

  attr :providers, :list, required: true
  attr :selected, :string, default: nil
  attr :form, :any, required: true
  attr :connecting, :boolean, required: true

  defp provider_section(assigns) do
    assigns = assign(assigns, :provider_options, provider_card_options(assigns.providers))

    ~H"""
    <div>
      <p class="setup-hint">Choose a print-on-demand provider and connect your API key.</p>

      <.form for={%{}} as={:provider_select} phx-change="select_provider">
        <.card_radio_group
          name="provider_select[type]"
          value={@selected}
          legend="Print provider"
          options={@provider_options}
        />
      </.form>

      <%!-- API key form for selected provider --%>
      <div :if={@selected} class="setup-provider-form">
        <% provider_info = Enum.find(@providers, &(&1.type == @selected)) %>
        <p class="setup-hint">
          {provider_info.setup_hint}.
          <.external_link href={provider_info.setup_url} class="setup-link">
            Open {provider_info.name}
          </.external_link>
        </p>

        <.form for={@form} phx-submit="connect_provider" phx-change="validate_provider">
          <.input
            field={@form[:api_key]}
            type="text"
            label="API token"
            placeholder="Paste your token here"
            autocomplete="off"
            phx-debounce="blur"
          />

          <div class="setup-actions">
            <.button type="submit" disabled={@connecting}>
              {if @connecting, do: "Connecting...", else: "Connect"}
            </.button>
          </div>
        </.form>
      </div>
    </div>
    """
  end

  # ── Stripe section ──

  attr :form, :any, required: true
  attr :connecting, :boolean, required: true
  attr :focus, :boolean, default: false

  defp stripe_section(assigns) do
    ~H"""
    <div>
      <p class="setup-hint">
        Enter your Stripe secret key to accept payments.
        <.external_link href="https://dashboard.stripe.com/apikeys" class="setup-link">
          Open Stripe dashboard
        </.external_link>
      </p>

      <.form for={@form} phx-submit="connect_stripe" phx-change="validate_stripe">
        <.input
          field={@form[:api_key]}
          type="text"
          label="Secret key"
          autocomplete="off"
          placeholder="sk_test_... or sk_live_..."
          phx-mounted={@focus && JS.focus()}
          phx-debounce="blur"
        />
        <div class="setup-actions">
          <.button phx-disable-with="Connecting...">
            {if @connecting, do: "Connecting...", else: "Connect"}
          </.button>
        </div>
      </.form>
    </div>
    """
  end

  # ── Helpers ──

  defp provider_card_options(providers) do
    Enum.map(providers, fn provider ->
      option = %{
        value: provider.type,
        name: provider.name,
        description: provider.tagline
      }

      if provider.status == :coming_soon do
        Map.merge(option, %{badge: "Coming soon", disabled: true})
      else
        option
      end
    end)
  end

  # Live validation for account creation form
  defp validate_account_fields(params) do
    errors = []

    shop_name = String.trim(params["shop_name"] || "")
    email = String.trim(params["email"] || "")
    password = params["password"] || ""
    password_confirmation = params["password_confirmation"] || ""

    errors =
      if shop_name == "" and params["shop_name"] != "" do
        [{:shop_name, {"Shop name can't be blank", []}} | errors]
      else
        errors
      end

    errors =
      if email != "" and not valid_email?(email) do
        [{:email, {"Please enter a valid email address", []}} | errors]
      else
        errors
      end

    errors =
      if password != "" and String.length(password) < 12 do
        [{:password, {"Password must be at least 12 characters", []}} | errors]
      else
        errors
      end

    errors =
      if password_confirmation != "" and password != password_confirmation do
        [{:password_confirmation, {"Passwords don't match", []}} | errors]
      else
        errors
      end

    Enum.reverse(errors)
  end

  defp valid_email?(email) do
    # Basic email format check
    String.match?(email, ~r/^[^\s@]+@[^\s@]+\.[^\s@]+$/)
  end
end
