defmodule SimpleshopThemeWeb.Admin.Dashboard do
  use SimpleshopThemeWeb, :live_view

  alias SimpleshopTheme.{Cart, Orders, Products, Settings, Setup}
  alias SimpleshopTheme.Products.ProviderConnection
  alias SimpleshopTheme.Providers
  alias SimpleshopTheme.Stripe.Setup, as: StripeSetup

  @impl true
  def mount(_params, _session, socket) do
    status = Setup.setup_status()
    status_counts = Orders.count_orders_by_status()
    paid_count = Map.get(status_counts, "paid", 0)
    recent_orders = Orders.list_orders(status: "paid") |> Enum.take(5)

    conn = Products.get_provider_connection_by_type("printify")

    if conn && connected?(socket) do
      Phoenix.PubSub.subscribe(SimpleshopTheme.PubSub, "sync:#{conn.id}")
    end

    active_step = determine_active_step(status)

    {:ok,
     socket
     |> assign(:page_title, "Dashboard")
     |> assign(:setup, status)
     |> assign(:active_step, active_step)
     # Printify state
     |> assign(:printify_conn, conn)
     |> assign(:printify_form, to_form(%{"api_key" => ""}, as: :printify))
     |> assign(:printify_testing, false)
     |> assign(:printify_test_result, nil)
     |> assign(:printify_saving, false)
     |> assign(:pending_api_key, nil)
     |> assign(:sync_status, conn && conn.sync_status)
     # Stripe state
     |> assign(:stripe_form, to_form(%{"api_key" => ""}, as: :stripe))
     |> assign(:stripe_connecting, false)
     |> assign(:stripe_api_key_hint, Settings.secret_hint("stripe_api_key"))
     # Celebration
     |> assign(:just_went_live, false)
     # Stats
     |> assign(:paid_count, paid_count)
     |> assign(:revenue, Orders.total_revenue())
     |> assign(:recent_orders, recent_orders)}
  end

  # -- Step determination --

  defp determine_active_step(status) do
    cond do
      !status.printify_connected -> :printify
      !status.products_synced -> :printify
      !status.stripe_connected -> :stripe
      !status.site_live -> :go_live
      true -> :complete
    end
  end

  # -- Events: Printify --

  @impl true
  def handle_event("validate_printify", %{"printify" => params}, socket) do
    {:noreply, assign(socket, pending_api_key: params["api_key"])}
  end

  def handle_event("test_printify", _params, socket) do
    api_key = socket.assigns.pending_api_key

    if api_key in [nil, ""] do
      {:noreply, assign(socket, printify_test_result: {:error, :no_api_key})}
    else
      socket = assign(socket, printify_testing: true, printify_test_result: nil)

      temp_conn = %ProviderConnection{
        provider_type: "printify",
        api_key_encrypted: encrypt_api_key(api_key)
      }

      result = Providers.test_connection(temp_conn)

      {:noreply, assign(socket, printify_testing: false, printify_test_result: result)}
    end
  end

  def handle_event("connect_printify", %{"printify" => %{"api_key" => api_key}}, socket) do
    if api_key == "" do
      {:noreply, put_flash(socket, :error, "Please enter your Printify API token")}
    else
      socket = assign(socket, printify_saving: true)

      params =
        %{"api_key" => api_key, "provider_type" => "printify"}
        |> maybe_add_shop_config(socket.assigns.printify_test_result)
        |> maybe_add_name(socket.assigns.printify_test_result)

      case Products.create_provider_connection(params) do
        {:ok, connection} ->
          Products.enqueue_sync(connection)

          if connected?(socket) do
            Phoenix.PubSub.subscribe(SimpleshopTheme.PubSub, "sync:#{connection.id}")
          end

          status = %{socket.assigns.setup | printify_connected: true}

          {:noreply,
           socket
           |> assign(:printify_saving, false)
           |> assign(:printify_conn, connection)
           |> assign(:sync_status, "syncing")
           |> assign(:setup, status)
           |> put_flash(:info, "Connected to Printify! Syncing products...")}

        {:error, _changeset} ->
          {:noreply,
           socket
           |> assign(:printify_saving, false)
           |> put_flash(:error, "Failed to save connection")}
      end
    end
  end

  def handle_event("retry_sync", _params, socket) do
    conn = socket.assigns.printify_conn

    if conn do
      Products.enqueue_sync(conn)
      {:noreply, assign(socket, sync_status: "syncing")}
    else
      {:noreply, socket}
    end
  end

  # -- Events: Stripe --

  def handle_event("connect_stripe", %{"stripe" => %{"api_key" => api_key}}, socket) do
    if api_key == "" do
      {:noreply, put_flash(socket, :error, "Please enter your Stripe secret key")}
    else
      socket = assign(socket, stripe_connecting: true)

      case StripeSetup.connect(api_key) do
        {:ok, _result} ->
          status = %{socket.assigns.setup | stripe_connected: true, can_go_live: true}

          {:noreply,
           socket
           |> assign(:stripe_connecting, false)
           |> assign(:setup, status)
           |> assign(:stripe_api_key_hint, Settings.secret_hint("stripe_api_key"))
           |> assign(:active_step, :go_live)
           |> put_flash(:info, "Stripe connected")}

        {:error, message} ->
          {:noreply,
           socket
           |> assign(:stripe_connecting, false)
           |> put_flash(:error, "Stripe connection failed: #{message}")}
      end
    end
  end

  # -- Events: Go live --

  def handle_event("go_live", _params, socket) do
    {:ok, _} = Settings.set_site_live(true)
    status = %{socket.assigns.setup | site_live: true}

    {:noreply,
     socket
     |> assign(:setup, status)
     |> assign(:just_went_live, true)}
  end

  # -- Events: Step navigation --

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

    new_active =
      if socket.assigns.active_step == step do
        determine_active_step(socket.assigns.setup)
      else
        step
      end

    {:noreply, assign(socket, active_step: new_active)}
  end

  # -- PubSub: Sync progress --

  @impl true
  def handle_info({:sync_status, "completed", product_count}, socket) do
    status = %{
      socket.assigns.setup
      | products_synced: true,
        product_count: product_count
    }

    active_step = if status.stripe_connected, do: :go_live, else: :stripe

    {:noreply,
     socket
     |> assign(:setup, status)
     |> assign(:sync_status, "completed")
     |> assign(:active_step, active_step)
     |> put_flash(:info, "#{product_count} products synced")}
  end

  def handle_info({:sync_status, "failed"}, socket) do
    {:noreply,
     socket
     |> assign(:sync_status, "failed")
     |> put_flash(:error, "Product sync failed — try again")}
  end

  def handle_info({:sync_status, status}, socket) do
    {:noreply, assign(socket, sync_status: status)}
  end

  # -- Render --

  @impl true
  def render(assigns) do
    ~H"""
    <.header>
      Dashboard
    </.header>

    <%!-- Celebration state --%>
    <.celebration :if={@just_went_live} />

    <%!-- Setup stepper (when not live and not celebrating) --%>
    <.setup_stepper
      :if={!@setup.site_live and !@just_went_live}
      setup={@setup}
      active_step={@active_step}
      printify_conn={@printify_conn}
      printify_form={@printify_form}
      printify_testing={@printify_testing}
      printify_test_result={@printify_test_result}
      printify_saving={@printify_saving}
      sync_status={@sync_status}
      stripe_form={@stripe_form}
      stripe_connecting={@stripe_connecting}
      stripe_api_key_hint={@stripe_api_key_hint}
    />

    <%!-- Stats --%>
    <div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mt-6">
      <.stat_card
        label="Orders"
        value={@paid_count}
        icon="hero-shopping-bag"
        href={~p"/admin/orders"}
      />
      <.stat_card
        label="Revenue"
        value={format_revenue(@revenue)}
        icon="hero-banknotes"
        href={~p"/admin/orders"}
      />
      <.stat_card
        label="Products"
        value={@setup.product_count}
        icon="hero-cube"
        href={~p"/admin/settings"}
      />
    </div>

    <%!-- Recent orders --%>
    <section class="mt-8">
      <div class="flex items-center justify-between mb-4">
        <h2 class="text-lg font-semibold">Recent orders</h2>
        <.link
          navigate={~p"/admin/orders"}
          class="text-sm text-base-content/60 hover:text-base-content"
        >
          View all &rarr;
        </.link>
      </div>

      <%= if @recent_orders == [] do %>
        <div class="rounded-lg border border-base-200 p-8 text-center text-base-content/60">
          <.icon name="hero-inbox" class="size-10 mx-auto mb-3 text-base-content/30" />
          <p class="font-medium">No orders yet</p>
          <p class="text-sm mt-1">Orders will appear here once customers check out.</p>
        </div>
      <% else %>
        <div class="overflow-x-auto">
          <table class="w-full text-sm">
            <thead>
              <tr class="border-b border-base-200 text-left text-base-content/60">
                <th class="pb-2 font-medium">Order</th>
                <th class="pb-2 font-medium">Date</th>
                <th class="pb-2 font-medium">Customer</th>
                <th class="pb-2 font-medium text-right">Total</th>
                <th class="pb-2 font-medium">Fulfilment</th>
              </tr>
            </thead>
            <tbody>
              <tr
                :for={order <- @recent_orders}
                class="border-b border-base-200 hover:bg-base-200/50 cursor-pointer"
                phx-click={JS.navigate(~p"/admin/orders/#{order}")}
              >
                <td class="py-2.5 font-medium">{order.order_number}</td>
                <td class="py-2.5 text-base-content/60">{format_date(order.inserted_at)}</td>
                <td class="py-2.5 text-base-content/60">{order.customer_email || "—"}</td>
                <td class="py-2.5 text-right">{Cart.format_price(order.total)}</td>
                <td class="py-2.5"><.fulfilment_pill status={order.fulfilment_status} /></td>
              </tr>
            </tbody>
          </table>
        </div>
      <% end %>
    </section>
    """
  end

  # ==========================================================================
  # Setup stepper components
  # ==========================================================================

  attr :setup, :map, required: true
  attr :active_step, :atom, required: true
  attr :printify_conn, :any, required: true
  attr :printify_form, :any, required: true
  attr :printify_testing, :boolean, required: true
  attr :printify_test_result, :any, required: true
  attr :printify_saving, :boolean, required: true
  attr :sync_status, :string, required: true
  attr :stripe_form, :any, required: true
  attr :stripe_connecting, :boolean, required: true
  attr :stripe_api_key_hint, :string, required: true

  defp setup_stepper(assigns) do
    ~H"""
    <div class="mt-6">
      <ol class="relative" aria-label="Setup steps">
        <%!-- Step 1: Printify --%>
        <.setup_step
          step={:printify}
          number={1}
          title="Connect to Printify"
          active_step={@active_step}
          done={@setup.printify_connected and @setup.products_synced}
          last={false}
          next_done={@setup.stripe_connected}
        >
          <:summary :if={@setup.printify_connected and @setup.products_synced}>
            Connected &middot; {@setup.product_count} products synced
          </:summary>
          <:content>
            <.printify_step_content
              setup={@setup}
              printify_conn={@printify_conn}
              printify_form={@printify_form}
              printify_testing={@printify_testing}
              printify_test_result={@printify_test_result}
              printify_saving={@printify_saving}
              sync_status={@sync_status}
            />
          </:content>
        </.setup_step>

        <%!-- Step 2: Stripe --%>
        <.setup_step
          step={:stripe}
          number={2}
          title="Connect Stripe"
          active_step={@active_step}
          done={@setup.stripe_connected}
          last={false}
          next_done={@setup.site_live}
        >
          <:summary :if={@setup.stripe_connected}>
            Connected &middot; {@stripe_api_key_hint}
          </:summary>
          <:content>
            <.stripe_step_content
              stripe_form={@stripe_form}
              stripe_connecting={@stripe_connecting}
            />
          </:content>
        </.setup_step>

        <%!-- Step 3: Go live --%>
        <.setup_step
          step={:go_live}
          number={3}
          title="Go live"
          active_step={@active_step}
          done={@setup.site_live}
          last={true}
          next_done={false}
        >
          <:content>
            <.go_live_step_content setup={@setup} />
          </:content>
        </.setup_step>
      </ol>
    </div>
    """
  end

  attr :step, :atom, required: true
  attr :number, :integer, required: true
  attr :title, :string, required: true
  attr :active_step, :atom, required: true
  attr :done, :boolean, required: true
  attr :last, :boolean, required: true
  attr :next_done, :boolean, required: true

  slot :summary
  slot :content, required: true

  defp setup_step(assigns) do
    is_active = assigns.active_step == assigns.step
    is_clickable = assigns.done

    assigns =
      assigns
      |> assign(:is_active, is_active)
      |> assign(:is_clickable, is_clickable)

    ~H"""
    <li class="relative pl-10 pb-8 last:pb-0" aria-current={@is_active && "step"}>
      <%!-- Connector line --%>
      <div
        :if={!@last}
        class={[
          "absolute left-[0.9375rem] top-8 -bottom-0 w-0.5",
          if(@done, do: "bg-green-500", else: "bg-base-300")
        ]}
        aria-hidden="true"
      />

      <%!-- Step circle --%>
      <div class={[
        "absolute left-0 top-0 flex size-8 items-center justify-center rounded-full text-sm font-semibold ring-4 ring-base-100",
        cond do
          @done -> "bg-green-500 text-white"
          @is_active -> "bg-base-content text-white"
          true -> "bg-base-200 text-base-content/40"
        end
      ]}>
        <%= if @done do %>
          <.icon name="hero-check-mini" class="size-5" />
        <% else %>
          {@number}
        <% end %>
      </div>

      <%!-- Step header --%>
      <%= if @is_clickable do %>
        <button
          type="button"
          class="flex w-full items-center gap-2 text-left"
          phx-click="toggle_step"
          phx-value-step={@step}
          aria-expanded={to_string(@is_active)}
        >
          <h3 class="text-sm font-semibold text-base-content">{@title}</h3>
          <.icon
            name={if @is_active, do: "hero-chevron-up-mini", else: "hero-chevron-down-mini"}
            class="size-4 text-base-content/40"
          />
        </button>
      <% else %>
        <h3 class={[
          "text-sm font-semibold",
          if(@is_active, do: "text-base-content", else: "text-base-content/40")
        ]}>
          {@title}
        </h3>
      <% end %>

      <%!-- Collapsed summary for completed steps --%>
      <p :if={@done and !@is_active and @summary != []} class="text-sm text-base-content/60 mt-0.5">
        {render_slot(@summary)}
      </p>

      <%!-- Expanded content --%>
      <div :if={@is_active} class="mt-3">
        {render_slot(@content)}
      </div>
    </li>
    """
  end

  # -- Printify step content --

  attr :setup, :map, required: true
  attr :printify_conn, :any, required: true
  attr :printify_form, :any, required: true
  attr :printify_testing, :boolean, required: true
  attr :printify_test_result, :any, required: true
  attr :printify_saving, :boolean, required: true
  attr :sync_status, :string, required: true

  defp printify_step_content(assigns) do
    ~H"""
    <%!-- Not yet connected: show form --%>
    <div :if={!@setup.printify_connected}>
      <p class="text-sm text-base-content/60 mb-4">
        Connect your Printify account to import products.
        Get an API token from <a
          href="https://printify.com/app/account/connections"
          target="_blank"
          rel="noopener"
          class="text-base-content underline"
        >
          Printify &rarr; Account &rarr; Connections
        </a>.
      </p>

      <.form
        for={@printify_form}
        phx-change="validate_printify"
        phx-submit="connect_printify"
      >
        <.input
          field={@printify_form[:api_key]}
          type="password"
          label="Printify API token"
          placeholder="Paste your token here"
          autocomplete="off"
        />

        <div class="flex flex-col sm:flex-row gap-2 mt-3">
          <button
            type="button"
            phx-click="test_printify"
            disabled={@printify_testing}
            class="inline-flex items-center justify-center gap-1.5 rounded-md bg-base-200 px-3 py-2 text-sm font-medium text-base-content hover:bg-base-300 ring-1 ring-base-300 ring-inset disabled:opacity-50"
          >
            <.icon
              name={if @printify_testing, do: "hero-arrow-path", else: "hero-signal"}
              class={if @printify_testing, do: "size-4 animate-spin", else: "size-4"}
            />
            {if @printify_testing, do: "Checking...", else: "Check connection"}
          </button>
          <.button type="submit" disabled={@printify_saving or @printify_testing}>
            {if @printify_saving, do: "Connecting...", else: "Connect to Printify"}
          </.button>
        </div>

        <.printify_test_feedback :if={@printify_test_result} result={@printify_test_result} />
      </.form>
    </div>

    <%!-- Connected, syncing --%>
    <div
      :if={@setup.printify_connected and @sync_status == "syncing"}
      class="flex items-center gap-2 text-sm"
    >
      <.icon name="hero-arrow-path" class="size-4 animate-spin text-base-content/40" />
      <span class="text-base-content/60">Syncing products from Printify...</span>
    </div>

    <%!-- Connected, sync failed --%>
    <div :if={@setup.printify_connected and @sync_status == "failed"}>
      <p class="text-sm text-red-600 mb-2">Product sync failed.</p>
      <button
        type="button"
        phx-click="retry_sync"
        class="inline-flex items-center gap-1.5 rounded-md bg-base-200 px-3 py-2 text-sm font-medium text-base-content hover:bg-base-300 ring-1 ring-base-300 ring-inset"
      >
        <.icon name="hero-arrow-path" class="size-4" /> Try again
      </button>
    </div>

    <%!-- Connected, synced (shown when user expands a completed step) --%>
    <div :if={@setup.printify_connected and @setup.products_synced}>
      <p class="text-sm text-base-content/60">
        {@setup.product_count} products synced from Printify.
      </p>
    </div>
    """
  end

  attr :result, :any, required: true

  defp printify_test_feedback(assigns) do
    ~H"""
    <div class="mt-2 text-sm">
      <%= case @result do %>
        <% {:ok, info} -> %>
          <span class="text-green-600 flex items-center gap-1">
            <.icon name="hero-check-circle" class="size-4" /> Connected to {info.shop_name}
          </span>
        <% {:error, reason} -> %>
          <span class="text-red-600 flex items-center gap-1">
            <.icon name="hero-x-circle" class="size-4" />
            {format_printify_error(reason)}
          </span>
      <% end %>
    </div>
    """
  end

  # -- Stripe step content --

  attr :stripe_form, :any, required: true
  attr :stripe_connecting, :boolean, required: true

  defp stripe_step_content(assigns) do
    ~H"""
    <div>
      <p class="text-sm text-base-content/60 mb-4">
        Enter your Stripe secret key to accept payments.
        Find it in your
        <a
          href="https://dashboard.stripe.com/apikeys"
          target="_blank"
          rel="noopener"
          class="text-base-content underline"
        >
          Stripe dashboard
        </a>
        under Developers &rarr; API keys.
      </p>

      <.form for={@stripe_form} phx-submit="connect_stripe">
        <.input
          field={@stripe_form[:api_key]}
          type="password"
          label="Secret key"
          autocomplete="off"
          placeholder="sk_test_... or sk_live_..."
        />
        <p class="text-xs text-base-content/60 mt-1">
          Starts with <code>sk_test_</code> or <code>sk_live_</code>. Encrypted at rest.
        </p>
        <div class="mt-3">
          <.button phx-disable-with="Connecting...">
            {if @stripe_connecting, do: "Connecting...", else: "Connect Stripe"}
          </.button>
        </div>
      </.form>
    </div>
    """
  end

  # -- Go live step content --

  attr :setup, :map, required: true

  defp go_live_step_content(assigns) do
    ~H"""
    <div>
      <p class="text-sm text-base-content/60 mb-4">
        Your shop is ready. Visitors currently see a "coming soon" page &mdash;
        hit the button to make it live.
      </p>
      <button
        phx-click="go_live"
        disabled={!@setup.can_go_live}
        class="inline-flex items-center gap-2 rounded-md bg-green-600 px-4 py-2.5 text-sm font-semibold text-white shadow-xs hover:bg-green-500 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
      >
        <.icon name="hero-rocket-launch" class="size-5" /> Go live
      </button>
    </div>
    """
  end

  # -- Celebration --

  defp celebration(assigns) do
    ~H"""
    <div class="mt-6 rounded-lg border border-green-200 bg-green-50 p-6 text-center">
      <.icon name="hero-check-badge" class="size-12 mx-auto text-green-600 mb-3" />
      <h2 class="text-lg font-semibold text-green-900">Your shop is live!</h2>
      <p class="text-sm text-green-700 mt-1 mb-4">
        Customers can now browse and buy from your shop.
      </p>
      <div class="flex flex-col sm:flex-row gap-2 justify-center">
        <.link
          navigate={~p"/"}
          class="inline-flex items-center justify-center gap-1.5 rounded-md bg-green-600 px-3 py-2 text-sm font-semibold text-white hover:bg-green-500"
        >
          <.icon name="hero-arrow-top-right-on-square-mini" class="size-4" /> View your shop
        </.link>
        <.link
          navigate={~p"/admin/theme"}
          class="inline-flex items-center justify-center gap-1.5 rounded-md bg-base-100 px-3 py-2 text-sm font-medium text-base-content ring-1 ring-base-300 ring-inset hover:bg-base-200/50"
        >
          <.icon name="hero-paint-brush-mini" class="size-4" /> Customise theme
        </.link>
      </div>
    </div>
    """
  end

  # ==========================================================================
  # Stats components
  # ==========================================================================

  attr :label, :string, required: true
  attr :value, :any, required: true
  attr :icon, :string, required: true
  attr :href, :string, required: true

  defp stat_card(assigns) do
    ~H"""
    <.link
      navigate={@href}
      class="rounded-lg border border-base-200 p-4 hover:border-base-300 transition-colors"
    >
      <div class="flex items-center gap-3">
        <div class="rounded-lg bg-base-200 p-2">
          <.icon name={@icon} class="size-5 text-base-content/60" />
        </div>
        <div>
          <p class="text-2xl font-bold">{@value}</p>
          <p class="text-sm text-base-content/60">{@label}</p>
        </div>
      </div>
    </.link>
    """
  end

  defp fulfilment_pill(assigns) do
    {color, label} =
      case assigns.status do
        "unfulfilled" -> {"bg-base-200 text-base-content/60", "unfulfilled"}
        "submitted" -> {"bg-blue-50 text-blue-700", "submitted"}
        "processing" -> {"bg-amber-50 text-amber-700", "processing"}
        "shipped" -> {"bg-purple-50 text-purple-700", "shipped"}
        "delivered" -> {"bg-green-50 text-green-700", "delivered"}
        "failed" -> {"bg-red-50 text-red-700", "failed"}
        _ -> {"bg-base-200 text-base-content/60", assigns.status || "—"}
      end

    assigns = assign(assigns, color: color, label: label)

    ~H"""
    <span class={["inline-flex rounded-full px-2 py-0.5 text-xs font-medium", @color]}>
      {@label}
    </span>
    """
  end

  # ==========================================================================
  # Helpers
  # ==========================================================================

  defp format_revenue(amount_pence) when is_integer(amount_pence) do
    Cart.format_price(amount_pence)
  end

  defp format_revenue(_), do: "£0.00"

  defp format_date(datetime) do
    Calendar.strftime(datetime, "%d %b %Y")
  end

  defp encrypt_api_key(api_key) do
    case SimpleshopTheme.Vault.encrypt(api_key) do
      {:ok, encrypted} -> encrypted
      _ -> nil
    end
  end

  defp maybe_add_shop_config(params, {:ok, %{shop_id: shop_id}}) do
    config = Map.get(params, "config", %{}) |> Map.put("shop_id", to_string(shop_id))
    Map.put(params, "config", config)
  end

  defp maybe_add_shop_config(params, _), do: params

  defp maybe_add_name(params, {:ok, %{shop_name: shop_name}}) when is_binary(shop_name) do
    Map.put_new(params, "name", shop_name)
  end

  defp maybe_add_name(params, _), do: Map.put_new(params, "name", "Printify")

  defp format_printify_error(:no_api_key), do: "Please enter your API token"
  defp format_printify_error(:unauthorized), do: "That token doesn't seem to be valid"
  defp format_printify_error(:timeout), do: "Couldn't reach Printify — try again"
  defp format_printify_error({:http_error, _code}), do: "Something went wrong — try again"
  defp format_printify_error(error) when is_binary(error), do: error
  defp format_printify_error(_), do: "Connection failed — check your token and try again"
end
