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

  alias Berrypod.{Pages, Products, Settings}
  alias BerrypodWeb.ThemeHook

  # System pages that are always available
  @system_pages [
    %{href: "/", label: "Home"},
    %{href: "/about", label: "About"},
    %{href: "/contact", label: "Contact"},
    %{href: "/cart", label: "Cart"},
    %{href: "/search", label: "Search"},
    %{href: "/delivery", label: "Delivery"},
    %{href: "/privacy", label: "Privacy policy"},
    %{href: "/terms", label: "Terms & conditions"}
  ]

  @impl true
  def mount(_params, _session, socket) do
    header_items = load_nav("header_nav", ThemeHook.default_header_nav())
    footer_items = load_nav("footer_nav", ThemeHook.default_footer_nav())

    {:ok,
     socket
     |> assign(:page_title, "Navigation")
     |> assign(:header_items, header_items)
     |> assign(:footer_items, footer_items)
     |> assign(:dirty, false)
     |> assign(:save_status, :idle)
     |> assign(:save_error, nil)
     |> assign_link_options()}
  end

  defp assign_link_options(socket) do
    custom_pages = Pages.list_custom_pages()
    categories = Products.list_categories()

    # Build grouped link options
    link_options = [
      {"Pages", Enum.map(@system_pages, &{&1.label, &1.href})},
      {"Custom pages", Enum.map(custom_pages, fn p -> {p.title, "/#{p.slug}"} end)},
      {"Collections", Enum.map(categories, fn c -> {c.name, "/collections/#{c.slug}"} end)},
      {"Other", [{"External URL", "external"}]}
    ]

    # Filter out empty groups
    link_options = Enum.reject(link_options, fn {_label, opts} -> opts == [] end)

    socket
    |> assign(:link_options, link_options)
    |> assign(:custom_pages, custom_pages)
    |> assign(:categories, categories)
  end

  # ── Item manipulation ──────────────────────────────────────────

  @impl true
  def handle_event("nav_item_change", params, socket) do
    section = params["section"]
    index = String.to_integer(params["index"])
    label = params["label"]
    dest = params["dest"]
    external_url = params["external_url"]

    items = get_items(socket, section)
    item = Enum.at(items, index)

    if item do
      updated =
        cond do
          # User selected "External URL"
          dest == "external" ->
            item
            |> Map.put("label", label)
            |> Map.put("href", external_url || "")
            |> Map.put("external", true)

          # User selected an internal link
          dest != nil && dest != "" ->
            # Auto-fill label if it was empty and user just selected a destination
            auto_label =
              if label == "" || label == item["label"],
                do: label_for_href(dest, socket),
                else: label

            item
            |> Map.put("label", if(label == "", do: auto_label, else: label))
            |> Map.put("href", dest)
            |> Map.delete("external")

          # Just updating the label
          true ->
            Map.put(item, "label", label)
        end

      items = List.replace_at(items, index, updated)
      {:noreply, put_items(socket, section, items) |> clear_save_status()}
    else
      {:noreply, socket}
    end
  end

  def handle_event("remove_item", %{"section" => section, "index" => index_str}, socket) do
    index = String.to_integer(index_str)
    items = get_items(socket, section) |> List.delete_at(index)
    {:noreply, put_items(socket, section, items) |> clear_save_status()}
  end

  def handle_event(
        "move_item",
        %{"section" => section, "index" => index_str, "dir" => dir},
        socket
      ) do
    index = String.to_integer(index_str)
    items = get_items(socket, section)
    target = if dir == "up", do: index - 1, else: index + 1

    if target >= 0 and target < length(items) do
      item = Enum.at(items, index)
      items = items |> List.delete_at(index) |> List.insert_at(target, item)
      {:noreply, put_items(socket, section, items) |> clear_save_status()}
    else
      {:noreply, socket}
    end
  end

  def handle_event("add_item", %{"section" => section}, socket) do
    # Add a new empty item - user will pick destination from dropdown
    items = get_items(socket, section) ++ [%{"label" => "", "href" => ""}]
    {:noreply, put_items(socket, section, items) |> clear_save_status()}
  end

  def handle_event("save", _params, socket) do
    all_items = socket.assigns.header_items ++ socket.assigns.footer_items
    errors = validate_nav_items(all_items)

    if errors == [] do
      Settings.put_setting("header_nav", socket.assigns.header_items, "json")
      Settings.put_setting("footer_nav", socket.assigns.footer_items, "json")

      {:noreply,
       socket
       |> assign(:dirty, false)
       |> assign(:save_status, :saved)
       |> assign(:save_error, nil)}
    else
      {:noreply,
       socket
       |> assign(:save_status, :error)
       |> assign(:save_error, Enum.join(errors, ". "))}
    end
  end

  def handle_event("reset_defaults", _params, socket) do
    {:noreply,
     socket
     |> assign(:header_items, ThemeHook.default_header_nav())
     |> assign(:footer_items, ThemeHook.default_footer_nav())
     |> assign(:dirty, true)
     |> clear_save_status()}
  end

  # ── Private helpers for events ───────────────────────────────────

  defp label_for_href(href, socket) do
    # Check system pages
    case Enum.find(@system_pages, &(&1.href == href)) do
      %{label: label} ->
        label

      nil ->
        # Check custom pages
        case Enum.find(socket.assigns.custom_pages, &("/#{&1.slug}" == href)) do
          %{title: title} ->
            title

          nil ->
            # Check collections
            case Enum.find(socket.assigns.categories, &("/collections/#{&1.slug}" == href)) do
              %{name: name} -> name
              nil -> ""
            end
        end
    end
  end

  defp validate_nav_items(items) do
    items
    |> Enum.with_index()
    |> Enum.flat_map(fn {item, _idx} ->
      cond do
        # Empty label
        item["label"] == "" || item["label"] == nil ->
          ["All links need a label"]

        # Empty href
        item["href"] == "" || item["href"] == nil ->
          ["\"#{item["label"]}\" needs a destination"]

        # External URL validation
        item["external"] == true || is_external_url?(item["href"]) ->
          if valid_url?(item["href"]) do
            []
          else
            ["\"#{item["label"]}\" has an invalid URL"]
          end

        true ->
          []
      end
    end)
    |> Enum.uniq()
  end

  defp valid_url?(url) when is_binary(url) do
    case URI.parse(url) do
      %URI{scheme: scheme, host: host}
      when scheme in ["http", "https"] and is_binary(host) and host != "" ->
        true

      _ ->
        false
    end
  end

  defp valid_url?(_), do: false

  defp clear_save_status(socket) do
    socket
    |> assign(:save_status, :idle)
    |> assign(:save_error, nil)
  end

  # ── Render ─────────────────────────────────────────────────────

  @impl true
  def render(assigns) do
    ~H"""
    <div id="nav-editor" phx-hook="DirtyGuard" data-dirty={to_string(@dirty)}>
      <.header>
        Navigation
        <:subtitle>Configure the links in your shop header and footer.</:subtitle>
      </.header>

      <div class="admin-nav-layout">
        <p :if={@dirty} class="admin-badge admin-badge-warning">
          Unsaved changes
        </p>
        <.nav_section
          title="Header navigation"
          section="header"
          items={@header_items}
          link_options={@link_options}
        />

        <.nav_section
          title="Footer navigation"
          section="footer"
          items={@footer_items}
          link_options={@link_options}
        />

        <form
          action={~p"/admin/navigation"}
          method="post"
          phx-submit="save"
          class="admin-row admin-row-lg"
        >
          <input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
          <input type="hidden" name="header_nav" value={Jason.encode!(@header_items)} />
          <input type="hidden" name="footer_nav" value={Jason.encode!(@footer_items)} />
          <button
            type="submit"
            class="admin-btn admin-btn-primary"
            disabled={!@dirty}
            phx-disable-with="Saving..."
          >
            Save
          </button>
          <.inline_feedback status={@save_status} message={@save_error} />
        </form>
        <div class="admin-row">
          <button
            type="button"
            phx-click="reset_defaults"
            data-confirm="Reset navigation to defaults? Your changes will be lost."
            class="admin-btn admin-btn-ghost"
          >
            Reset to defaults
          </button>
        </div>
      </div>
    </div>
    """
  end

  defp nav_section(assigns) do
    ~H"""
    <section>
      <h3 class="admin-nav-section-heading">
        {@title}
      </h3>

      <div class="admin-stack admin-stack-sm">
        <div :if={@items != []} class="nav-editor-labels" aria-hidden="true">
          <span>Label</span>
          <span>Destination</span>
        </div>
        <.nav_item
          :for={{item, idx} <- Enum.with_index(@items)}
          item={item}
          idx={idx}
          section={@section}
          total={length(@items)}
          link_options={@link_options}
        />
      </div>

      <div :if={@items == []} class="admin-nav-empty">
        No items yet.
      </div>

      <div class="admin-nav-actions">
        <button
          phx-click="add_item"
          phx-value-section={@section}
          class="admin-btn admin-btn-sm admin-btn-outline"
        >
          <.icon name="hero-plus" class="size-4" /> Add link
        </button>
      </div>
    </section>
    """
  end

  defp nav_item(assigns) do
    # Determine if this is an external link
    is_external = assigns.item["external"] == true || is_external_url?(assigns.item["href"])
    selected_value = if is_external, do: "external", else: assigns.item["href"]

    # Check if external URL is valid (only if there's a value)
    url_invalid =
      is_external && assigns.item["href"] != "" && assigns.item["href"] != nil &&
        !valid_url?(assigns.item["href"])

    assigns =
      assigns
      |> assign(:is_external, is_external)
      |> assign(:selected_value, selected_value)
      |> assign(:url_invalid, url_invalid)

    ~H"""
    <form
      class="nav-editor-item"
      phx-change="nav_item_change"
      phx-value-section={@section}
      phx-value-index={@idx}
    >
      <div class="nav-editor-fields">
        <label class="sr-only" for={"nav-#{@section}-#{@idx}-label"}>Label</label>
        <input
          type="text"
          id={"nav-#{@section}-#{@idx}-label"}
          name="label"
          value={@item["label"]}
          placeholder="Label"
          class="admin-input nav-editor-input"
        />
        <label class="sr-only" for={"nav-#{@section}-#{@idx}-dest"}>Destination</label>
        <select
          id={"nav-#{@section}-#{@idx}-dest"}
          name="dest"
          class="admin-input nav-editor-select"
        >
          <option value="" disabled selected={@selected_value == ""}>
            Choose destination...
          </option>
          <%= for {group_label, options} <- @link_options do %>
            <optgroup label={group_label}>
              <%= for {label, href} <- options do %>
                <option value={href} selected={@selected_value == href}>
                  {label}
                </option>
              <% end %>
            </optgroup>
          <% end %>
        </select>
      </div>

      <%!-- External URL input (shown when "External URL" is selected) --%>
      <div :if={@is_external} class="nav-editor-external">
        <label class="sr-only" for={"nav-#{@section}-#{@idx}-url"}>URL</label>
        <input
          type="url"
          id={"nav-#{@section}-#{@idx}-url"}
          name="external_url"
          value={@item["href"]}
          placeholder="https://example.com"
          class={["admin-input nav-editor-input", @url_invalid && "admin-input-error"]}
        />
        <span :if={@url_invalid} class="nav-editor-error">
          <.icon name="hero-exclamation-circle" class="size-4" />
          Enter a valid URL starting with https://
        </span>
      </div>

      <div class="nav-editor-actions">
        <button
          type="button"
          phx-click="move_item"
          phx-value-section={@section}
          phx-value-index={@idx}
          phx-value-dir="up"
          disabled={@idx == 0}
          class="admin-btn admin-btn-xs admin-btn-ghost"
          aria-label="Move up"
        >
          <.icon name="hero-chevron-up" class="size-4" />
        </button>
        <button
          type="button"
          phx-click="move_item"
          phx-value-section={@section}
          phx-value-index={@idx}
          phx-value-dir="down"
          disabled={@idx == @total - 1}
          class="admin-btn admin-btn-xs admin-btn-ghost"
          aria-label="Move down"
        >
          <.icon name="hero-chevron-down" class="size-4" />
        </button>
        <button
          type="button"
          phx-click="remove_item"
          phx-value-section={@section}
          phx-value-index={@idx}
          class="admin-btn admin-btn-xs admin-btn-ghost"
          aria-label="Remove"
        >
          <.icon name="hero-x-mark" class="size-4" />
        </button>
      </div>
    </form>
    """
  end

  defp is_external_url?(nil), do: false
  defp is_external_url?(""), do: false

  defp is_external_url?(href) do
    String.starts_with?(href, "http://") || String.starts_with?(href, "https://")
  end

  # ── Helpers ────────────────────────────────────────────────────

  defp get_items(socket, "header"), do: socket.assigns.header_items
  defp get_items(socket, "footer"), do: socket.assigns.footer_items

  defp put_items(socket, "header", items),
    do: socket |> assign(:header_items, items) |> assign(:dirty, true)

  defp put_items(socket, "footer", items),
    do: socket |> assign(:footer_items, items) |> assign(:dirty, true)

  defp load_nav(key, default) do
    case Settings.get_setting(key) do
      items when is_list(items) -> items
      _ -> default
    end
  end
end
