defmodule BerrypodWeb.ShopComponents.Layout do
  use Phoenix.Component

  import BerrypodWeb.ShopComponents.Cart
  import BerrypodWeb.ShopComponents.Content

  alias BerrypodWeb.R

  @doc """
  Renders the announcement bar.

  The bar displays promotional messaging at the top of the page.
  It uses CSS custom properties for theming.

  ## Attributes

  * `theme_settings` - Required. The theme settings map.
  * `message` - The announcement message to display.
  * `link` - Optional URL to link the announcement to.
  * `style` - Visual style: "info", "sale", or "warning".

  ## Examples

      <.announcement_bar theme_settings={@theme_settings} message="Free shipping!" />
      <.announcement_bar theme_settings={@theme_settings} message="20% off!" link="/sale" style="sale" />
  """
  attr :theme_settings, :map, required: true
  attr :message, :string, default: ""
  attr :link, :string, default: ""
  attr :style, :string, default: "info"

  def announcement_bar(assigns) do
    # Use default message if none provided
    message =
      if assigns.message in ["", nil] do
        "Sample announcement – e.g. free delivery, sales, or new drops"
      else
        assigns.message
      end

    assigns = assign(assigns, :display_message, message)

    ~H"""
    <div class={"announcement-bar announcement-bar--#{@style}"}>
      <%= if @link != "" do %>
        <a href={@link} class="announcement-bar-link">
          <p>{@display_message}</p>
        </a>
      <% else %>
        <p>{@display_message}</p>
      <% end %>
    </div>
    """
  end

  @doc """
  Renders the skip link for keyboard navigation accessibility.

  This is a standard accessibility pattern that allows keyboard users
  to skip directly to the main content.
  """
  def skip_link(assigns) do
    ~H"""
    <a href="#main-content" class="skip-link">
      Skip to main content
    </a>
    """
  end

  # Keys accepted by shop_layout — used by layout_assigns/1 so page templates
  # can spread assigns without listing each one explicitly.
  @layout_keys ~w(theme_settings generated_css site_name logo_image header_image mode cart_items cart_count
    cart_subtotal cart_total cart_drawer_open cart_status active_page error_page is_admin
    search_query search_results search_open categories shipping_estimate
    country_code available_countries editing theme_editing editor_current_path editor_sidebar_open
    editor_active_tab editor_sheet_state editor_dirty editor_save_status
    header_nav_items footer_nav_items social_links announcement_text announcement_link announcement_style
    newsletter_enabled newsletter_state stripe_connected)a

  @doc """
  Extracts the assigns relevant to `shop_layout` from a full assigns map.

  Page templates can use this instead of listing every attr explicitly:

      <.shop_layout {layout_assigns(assigns)} active_page="home">
        ...
      </.shop_layout>
  """
  def layout_assigns(assigns) do
    base = Map.take(assigns, @layout_keys)

    # When site editor is active, use in-memory values for live preview
    # The site_state assigns is the editor's working copy, while announcement_*,
    # nav items, and social_links are the database-loaded values from theme_hook
    # Only override when site_editing is true (editor has loaded site state)
    if assigns[:site_editing] && assigns[:site_state] do
      state = assigns[:site_state]
      # Convert raw structs to shop format
      social_links = format_social_links_for_shop(state.social_links || [])
      header_nav = format_nav_items_for_shop(state.header_nav || [])
      footer_nav = format_nav_items_for_shop(state.footer_nav || [])

      base
      |> Map.put(:announcement_text, state.announcement_text)
      |> Map.put(:announcement_link, state.announcement_link)
      |> Map.put(:announcement_style, state.announcement_style)
      |> Map.put(:social_links, social_links)
      |> Map.put(:header_nav_items, header_nav)
      |> Map.put(:footer_nav_items, footer_nav)
    else
      base
    end
  end

  # Convert raw SocialLink structs to the format expected by shop components
  # Filters out links with empty URLs (incomplete entries still being edited)
  # Using String.to_atom is safe here because platforms are validated by the schema
  defp format_social_links_for_shop(links) do
    links
    |> Enum.reject(fn link -> is_nil(link.url) or link.url == "" end)
    |> Enum.map(fn link ->
      platform = if is_binary(link.platform), do: link.platform, else: to_string(link.platform)

      %{
        platform: String.to_atom(platform),
        url: link.url,
        label: platform_display_label(platform)
      }
    end)
  end

  # Convert raw NavItem structs to the format expected by shop components
  # Filters out items with empty labels (incomplete entries still being edited)
  defp format_nav_items_for_shop(items) do
    items
    |> Enum.reject(fn item -> is_nil(item.label) or item.label == "" end)
    |> Enum.map(fn item ->
      slug = extract_slug_from_url(item.url)

      base = %{
        "label" => item.label,
        "href" => item.url,
        "slug" => slug
      }

      # Add active_slugs for Shop nav item to highlight on collection and pdp pages
      if slug == "collection" do
        Map.put(base, "active_slugs", ["collection", "pdp"])
      else
        base
      end
    end)
  end

  # Extract a slug from a URL for nav item matching
  defp extract_slug_from_url(url) when is_binary(url) do
    cond do
      url == "/" ->
        "home"

      true ->
        # Extract first segment and check if it's a known prefix
        first_segment = url |> String.trim_leading("/") |> String.split("/") |> List.first() || ""

        case R.prefix_type_from_segment(first_segment) do
          :collections -> "collection"
          :products -> "pdp"
          _ -> first_segment
        end
    end
  end

  defp extract_slug_from_url(_), do: ""

  # Social
  defp platform_display_label("instagram"), do: "Instagram"
  defp platform_display_label("threads"), do: "Threads"
  defp platform_display_label("facebook"), do: "Facebook"
  defp platform_display_label("twitter"), do: "Twitter"
  defp platform_display_label("snapchat"), do: "Snapchat"
  defp platform_display_label("linkedin"), do: "LinkedIn"

  # Video & streaming
  defp platform_display_label("youtube"), do: "YouTube"
  defp platform_display_label("twitch"), do: "Twitch"
  defp platform_display_label("vimeo"), do: "Vimeo"
  defp platform_display_label("kick"), do: "Kick"
  defp platform_display_label("rumble"), do: "Rumble"

  # Music & podcasts
  defp platform_display_label("spotify"), do: "Spotify"
  defp platform_display_label("soundcloud"), do: "SoundCloud"
  defp platform_display_label("bandcamp"), do: "Bandcamp"
  defp platform_display_label("applepodcasts"), do: "Podcasts"

  # Creative
  defp platform_display_label("pinterest"), do: "Pinterest"
  defp platform_display_label("behance"), do: "Behance"
  defp platform_display_label("dribbble"), do: "Dribbble"
  defp platform_display_label("tumblr"), do: "Tumblr"
  defp platform_display_label("medium"), do: "Medium"

  # Support & sales
  defp platform_display_label("patreon"), do: "Patreon"
  defp platform_display_label("kofi"), do: "Ko-fi"
  defp platform_display_label("etsy"), do: "Etsy"
  defp platform_display_label("gumroad"), do: "Gumroad"
  defp platform_display_label("substack"), do: "Substack"

  # Federated
  defp platform_display_label("mastodon"), do: "Mastodon"
  defp platform_display_label("pixelfed"), do: "Pixelfed"
  defp platform_display_label("bluesky"), do: "Bluesky"
  defp platform_display_label("peertube"), do: "PeerTube"
  defp platform_display_label("lemmy"), do: "Lemmy"
  defp platform_display_label("matrix"), do: "Matrix"

  # Developer
  defp platform_display_label("github"), do: "GitHub"
  defp platform_display_label("gitlab"), do: "GitLab"
  defp platform_display_label("codeberg"), do: "Codeberg"
  defp platform_display_label("sourcehut"), do: "SourceHut"
  defp platform_display_label("reddit"), do: "Reddit"

  # Messaging
  defp platform_display_label("discord"), do: "Discord"
  defp platform_display_label("telegram"), do: "Telegram"
  defp platform_display_label("signal"), do: "Signal"
  defp platform_display_label("whatsapp"), do: "WhatsApp"

  # Other
  defp platform_display_label("linktree"), do: "Linktree"
  defp platform_display_label("rss"), do: "RSS"
  defp platform_display_label("website"), do: "Website"
  defp platform_display_label("custom"), do: "Link"
  defp platform_display_label(other), do: String.capitalize(other)

  @doc """
  Wraps page content in the standard shop shell: container, header, footer,
  cart drawer, search modal, and mobile bottom nav.

  Templates pass their unique `<main>` content as the inner block.
  The `error_page` flag disables the CartPersist hook and mobile bottom nav.
  """
  attr :theme_settings, :map, required: true
  attr :site_name, :string, required: true
  attr :logo_image, :any, required: true
  attr :header_image, :any, required: true
  attr :mode, :atom, required: true
  attr :cart_items, :list, required: true
  attr :cart_count, :integer, required: true
  attr :cart_subtotal, :string, required: true
  attr :cart_total, :string, default: nil
  attr :cart_drawer_open, :boolean, default: false
  attr :cart_status, :string, default: nil
  attr :active_page, :string, required: true
  attr :error_page, :boolean, default: false
  attr :is_admin, :boolean, default: false
  attr :editing, :boolean, default: false
  attr :editor_current_path, :string, default: nil
  attr :editor_sidebar_open, :boolean, default: true
  attr :search_query, :string, default: ""
  attr :search_results, :list, default: []
  attr :search_open, :boolean, default: false
  attr :shipping_estimate, :integer, default: nil
  attr :country_code, :string, default: "GB"
  attr :available_countries, :list, default: []
  attr :header_nav_items, :list, default: []
  attr :footer_nav_items, :list, default: []
  attr :social_links, :list, default: []
  attr :announcement_text, :string, default: ""
  attr :announcement_link, :string, default: ""
  attr :announcement_style, :string, default: "info"
  attr :newsletter_enabled, :boolean, default: false
  attr :newsletter_state, :atom, default: :idle
  attr :stripe_connected, :boolean, default: true
  attr :generated_css, :string, default: nil

  slot :inner_block, required: true

  def shop_layout(assigns) do
    ~H"""
    <div
      id={unless @error_page, do: "shop-container"}
      phx-hook={unless @error_page, do: "CartPersist"}
      class="shop-container themed"
      data-bottom-nav={!@error_page || nil}
      data-mood={@theme_settings.mood}
      data-typography={@theme_settings.typography}
      data-shape={@theme_settings.shape}
      data-density={@theme_settings.density}
      data-grid={@theme_settings.grid_columns}
      data-header={@theme_settings.header_layout}
      data-sticky={to_string(@theme_settings.sticky_header)}
      data-layout={@theme_settings.layout_width}
      data-shadow={@theme_settings.card_shadow}
      data-button-style={@theme_settings.button_style}
    >
      <%!-- Live-updatable theme CSS (overrides static version in head) --%>
      <%= if @generated_css do %>
        {Phoenix.HTML.raw("<style id=\"theme-css-live\">#{@generated_css}</style>")}
      <% end %>

      <.skip_link />

      <%= if @theme_settings.announcement_bar do %>
        <.announcement_bar
          theme_settings={@theme_settings}
          message={@announcement_text}
          link={@announcement_link}
          style={@announcement_style}
        />
      <% end %>

      <.shop_header
        theme_settings={@theme_settings}
        site_name={@site_name}
        logo_image={@logo_image}
        header_image={@header_image}
        active_page={@active_page}
        mode={@mode}
        cart_count={@cart_count}
        is_admin={@is_admin}
        header_nav_items={@header_nav_items}
      />

      {render_slot(@inner_block)}

      <.shop_footer
        theme_settings={@theme_settings}
        site_name={@site_name}
        mode={@mode}
        categories={assigns[:categories] || []}
        footer_nav_items={@footer_nav_items}
        social_links={@social_links}
        newsletter_enabled={@newsletter_enabled}
        newsletter_state={@newsletter_state}
      />

      <.cart_drawer
        cart_items={@cart_items}
        subtotal={@cart_subtotal}
        total={@cart_total}
        cart_count={@cart_count}
        mode={@mode}
        open={@cart_drawer_open}
        cart_status={@cart_status}
        shipping_estimate={@shipping_estimate}
        country_code={@country_code}
        available_countries={@available_countries}
        stripe_connected={@stripe_connected}
      />

      <.search_modal
        hint_text={~s(Try a search – e.g. "mountain" or "notebook")}
        search_query={@search_query}
        search_results={@search_results}
        search_open={@search_open}
      />

      <.mobile_nav_drawer
        :if={!@error_page}
        active_page={@active_page}
        mode={@mode}
        items={@header_nav_items}
        categories={assigns[:categories] || []}
      />
    </div>
    """
  end

  @doc """
  Renders a mobile bottom navigation bar.

  This component provides thumb-friendly navigation for mobile devices,
  following modern UX best practices. It's hidden on larger screens where
  the standard header navigation is used.

  ## Attributes

  * `active_page` - Required. The current page identifier (e.g., "home", "collection", "about", "contact").
  * `mode` - Optional. Either `:live` (default) for real navigation or
    `:preview` for theme preview mode with phx-click handlers.
  * `cart_count` - Optional. Number of items in cart for badge display. Default: 0.

  ## Examples

      <.mobile_bottom_nav active_page="home" />
      <.mobile_bottom_nav active_page="collection" mode={:preview} />
  """
  attr :active_page, :string, required: true
  attr :mode, :atom, default: :live
  attr :cart_count, :integer, default: 0
  attr :items, :list, default: []

  def mobile_bottom_nav(assigns) do
    ~H"""
    <nav
      class="mobile-bottom-nav"
      aria-label="Main navigation"
    >
      <ul>
        <.mobile_nav_item
          :for={item <- @items}
          icon={mobile_icon(item["slug"])}
          label={item["label"]}
          page={item["slug"] || ""}
          href={item["href"]}
          active_page={@active_page}
          active_pages={item["active_slugs"]}
          mode={@mode}
        />
      </ul>
    </nav>
    """
  end

  attr :icon, :atom, required: true
  attr :label, :string, required: true
  attr :page, :string, required: true
  attr :href, :string, required: true
  attr :active_page, :string, required: true
  attr :active_pages, :list, default: nil
  attr :mode, :atom, default: :live

  defp mobile_nav_item(assigns) do
    active_pages = assigns.active_pages || [assigns.page]
    is_current = assigns.active_page in active_pages
    assigns = assign(assigns, :is_current, is_current)

    ~H"""
    <li>
      <%= if @mode == :preview do %>
        <a
          href="#"
          phx-click="change_preview_page"
          phx-value-page={@page}
          class="mobile-nav-link"
          aria-current={if @is_current, do: "page", else: nil}
        >
          <.nav_icon icon={@icon} />
          <span>{@label}</span>
        </a>
      <% else %>
        <.link
          patch={@href}
          class="mobile-nav-link"
          aria-current={if @is_current, do: "page", else: nil}
        >
          <.nav_icon icon={@icon} />
          <span>{@label}</span>
        </.link>
      <% end %>
    </li>
    """
  end

  defp mobile_icon("home"), do: :home
  defp mobile_icon("collection"), do: :shop
  defp mobile_icon("about"), do: :about
  defp mobile_icon("contact"), do: :contact
  defp mobile_icon(_), do: :page

  defp nav_icon(%{icon: :home} = assigns) do
    ~H"""
    <svg
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2z"></path>
      <polyline points="9 22 9 12 15 12 15 22"></polyline>
    </svg>
    """
  end

  defp nav_icon(%{icon: :shop} = assigns) do
    ~H"""
    <svg
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <rect x="3" y="3" width="7" height="7"></rect>
      <rect x="14" y="3" width="7" height="7"></rect>
      <rect x="14" y="14" width="7" height="7"></rect>
      <rect x="3" y="14" width="7" height="7"></rect>
    </svg>
    """
  end

  defp nav_icon(%{icon: :about} = assigns) do
    ~H"""
    <svg
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <circle cx="12" cy="12" r="10"></circle>
      <line x1="12" y1="16" x2="12" y2="12"></line>
      <line x1="12" y1="8" x2="12.01" y2="8"></line>
    </svg>
    """
  end

  defp nav_icon(%{icon: :contact} = assigns) do
    ~H"""
    <svg
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
      <polyline points="22,6 12,13 2,6"></polyline>
    </svg>
    """
  end

  defp nav_icon(%{icon: :page} = assigns) do
    ~H"""
    <svg
      width="24"
      height="24"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="2"
      stroke-linecap="round"
      stroke-linejoin="round"
    >
      <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
      <polyline points="14 2 14 8 20 8"></polyline>
    </svg>
    """
  end

  @doc """
  Renders the search modal overlay with live search results.

  ## Attributes

  * `hint_text` - Hint text shown when no query is entered.
  * `search_query` - Current search query string.
  * `search_results` - List of Product structs matching the query.
  """
  attr :hint_text, :string, default: nil
  attr :search_query, :string, default: ""
  attr :search_results, :list, default: []
  attr :search_open, :boolean, default: false

  def search_modal(assigns) do
    alias Berrypod.Cart
    alias Berrypod.Products.{Product, ProductImage}

    assigns =
      assign(
        assigns,
        :results_with_images,
        assigns.search_results
        |> Enum.with_index()
        |> Enum.map(fn {product, idx} ->
          image = Product.primary_image(product)
          %{product: product, image_url: ProductImage.url(image, 400), idx: idx}
        end)
      )

    ~H"""
    <div
      id="search-modal"
      class="search-modal"
      style={"display: #{if @search_open, do: "flex", else: "none"};"}
      phx-hook="SearchModal"
      phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
    >
      <div
        class="search-panel"
        onclick="event.stopPropagation()"
      >
        <div class="search-bar">
          <svg
            class="search-icon"
            width="20"
            height="20"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
          >
            <circle cx="11" cy="11" r="8"></circle>
            <path d="M21 21l-4.35-4.35"></path>
          </svg>
          <input
            type="text"
            id="search-input"
            name="query"
            class="search-input"
            placeholder="Search products..."
            value={@search_query}
            phx-keyup="search"
            phx-debounce="150"
            autocomplete="off"
            role="combobox"
            aria-expanded={to_string(@search_results != [])}
            aria-controls="search-results-list"
            aria-autocomplete="list"
          />
          <div
            class="search-kbd"
            aria-hidden="true"
          >
            <kbd>⌘</kbd><kbd>K</kbd>
          </div>
          <button
            type="button"
            class="search-close"
            phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
            aria-label="Close search"
          >
            <svg
              width="20"
              height="20"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <line x1="18" y1="6" x2="6" y2="18"></line>
              <line x1="6" y1="6" x2="18" y2="18"></line>
            </svg>
          </button>
        </div>

        <div class="search-results">
          <%= cond do %>
            <% @search_results != [] -> %>
              <ul id="search-results-list" role="listbox" aria-label="Search results">
                <li
                  :for={item <- @results_with_images}
                  id={"search-result-#{item.idx}"}
                  role="option"
                  aria-selected="false"
                >
                  <.link
                    patch={R.product(item.product.slug || item.product.id)}
                    class="search-result"
                    phx-click={Phoenix.LiveView.JS.dispatch("close-search", to: "#search-modal")}
                  >
                    <div
                      :if={item.image_url}
                      class="search-result-thumb"
                    >
                      <img
                        src={item.image_url}
                        alt={item.product.title}
                        loading="lazy"
                      />
                    </div>
                    <div class="search-result-details">
                      <p class="search-result-title">
                        {item.product.title}
                      </p>
                      <p class="search-result-meta">
                        {item.product.category}
                        <span>
                          {Cart.format_price(item.product.cheapest_price)}
                        </span>
                      </p>
                    </div>
                  </.link>
                </li>
              </ul>
            <% String.length(@search_query) >= 2 -> %>
              <div class="search-hint">
                <p>No products found for "{@search_query}"</p>
              </div>
            <% @hint_text != nil -> %>
              <div class="search-hint">
                <p>{@hint_text}</p>
              </div>
            <% true -> %>
          <% end %>
        </div>
      </div>
    </div>
    """
  end

  @doc """
  Renders the mobile navigation drawer.

  A slide-out drawer containing the main navigation links for mobile users.
  Triggered by the hamburger menu button in the header.
  """
  attr :active_page, :string, required: true
  attr :mode, :atom, default: :live
  attr :items, :list, default: []
  attr :categories, :list, default: []

  def mobile_nav_drawer(assigns) do
    ~H"""
    <div
      id="mobile-nav-drawer"
      class="mobile-nav-drawer"
      phx-hook="MobileNavDrawer"
    >
      <div
        class="mobile-nav-backdrop"
        phx-click={Phoenix.LiveView.JS.dispatch("close-mobile-nav", to: "#mobile-nav-drawer")}
      >
      </div>
      <nav class="mobile-nav-panel" aria-label="Main navigation">
        <div class="mobile-nav-header">
          <span class="mobile-nav-title">Menu</span>
          <button
            type="button"
            class="mobile-nav-close"
            phx-click={Phoenix.LiveView.JS.dispatch("close-mobile-nav", to: "#mobile-nav-drawer")}
            aria-label="Close menu"
          >
            <svg
              width="24"
              height="24"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="2"
            >
              <line x1="18" y1="6" x2="6" y2="18"></line>
              <line x1="6" y1="6" x2="18" y2="18"></line>
            </svg>
          </button>
        </div>

        <ul class="mobile-nav-links">
          <li :for={item <- @items}>
            <%= if @mode == :preview do %>
              <a
                href="#"
                phx-click="change_preview_page"
                phx-value-page={item["slug"]}
                class="mobile-nav-link"
                aria-current={@active_page in (item["active_slugs"] || [item["slug"]]) && "page"}
              >
                {item["label"]}
              </a>
            <% else %>
              <.link
                patch={item["href"]}
                class="mobile-nav-link"
                aria-current={@active_page in (item["active_slugs"] || [item["slug"]]) && "page"}
                phx-click={Phoenix.LiveView.JS.dispatch("close-mobile-nav", to: "#mobile-nav-drawer")}
              >
                {item["label"]}
              </.link>
            <% end %>
          </li>
        </ul>

        <%= if @categories != [] do %>
          <div class="mobile-nav-section">
            <span class="mobile-nav-section-title">Shop by category</span>
            <ul class="mobile-nav-links">
              <li :for={category <- @categories}>
                <%= if @mode == :preview do %>
                  <a
                    href="#"
                    phx-click="change_preview_page"
                    phx-value-page="collection"
                    class="mobile-nav-link"
                  >
                    {category.name}
                  </a>
                <% else %>
                  <.link
                    patch={R.collection(category.slug)}
                    class="mobile-nav-link"
                    phx-click={
                      Phoenix.LiveView.JS.dispatch("close-mobile-nav", to: "#mobile-nav-drawer")
                    }
                  >
                    {category.name}
                  </.link>
                <% end %>
              </li>
            </ul>
          </div>
        <% end %>
      </nav>
    </div>
    """
  end

  @doc """
  Renders the shop footer with newsletter signup and links.

  ## Attributes

  * `theme_settings` - Required. The theme settings map.
  * `mode` - Optional. Either `:live` (default) for real navigation or
    `:preview` for theme preview mode with phx-click handlers.

  ## Examples

      <.shop_footer theme_settings={@theme_settings} />
      <.shop_footer theme_settings={@theme_settings} mode={:preview} />
  """
  attr :theme_settings, :map, required: true
  attr :site_name, :string, required: true
  attr :mode, :atom, default: :live
  attr :categories, :list, default: []
  attr :footer_nav_items, :list, default: []
  attr :social_links, :list, default: []
  attr :newsletter_enabled, :boolean, default: false
  attr :newsletter_state, :atom, default: :idle

  def shop_footer(assigns) do
    assigns = assign(assigns, :current_year, Date.utc_today().year)

    ~H"""
    <footer class="shop-footer">
      <div class="shop-footer-inner">
        <div class="footer-grid">
          <.newsletter_card
            variant={:inline}
            newsletter_enabled={@newsletter_enabled}
            newsletter_state={@newsletter_state}
          />

          <div class="footer-links">
            <div>
              <h4 class="footer-heading">
                Shop
              </h4>
              <ul class="footer-nav">
                <%= if @mode == :preview do %>
                  <li>
                    <a
                      href="#"
                      phx-click="change_preview_page"
                      phx-value-page="collection"
                      class="footer-link"
                    >
                      All products
                    </a>
                  </li>
                  <%= for category <- @categories do %>
                    <li>
                      <a
                        href="#"
                        phx-click="change_preview_page"
                        phx-value-page="collection"
                        class="footer-link"
                      >
                        {category.name}
                      </a>
                    </li>
                  <% end %>
                <% else %>
                  <li>
                    <.link
                      patch={R.collection("all")}
                      class="footer-link"
                    >
                      All products
                    </.link>
                  </li>
                  <%= for category <- @categories do %>
                    <li>
                      <.link
                        patch={R.collection(category.slug)}
                        class="footer-link"
                      >
                        {category.name}
                      </.link>
                    </li>
                  <% end %>
                <% end %>
              </ul>
            </div>
            <div>
              <h4 class="footer-heading">
                Help
              </h4>
              <ul class="footer-nav">
                <li :for={item <- @footer_nav_items}>
                  <%= if @mode == :preview do %>
                    <a
                      href="#"
                      phx-click="change_preview_page"
                      phx-value-page={item["slug"]}
                      class="footer-link"
                    >
                      {item["label"]}
                    </a>
                  <% else %>
                    <.link patch={item["href"]} class="footer-link">
                      {item["label"]}
                    </.link>
                  <% end %>
                </li>
              </ul>
            </div>
          </div>
        </div>
        
    <!-- Bottom Bar -->
        <div class="footer-bottom">
          <p class="footer-copyright">
            © {@current_year} {@site_name}
          </p>
          <.social_links links={@social_links} />
        </div>
      </div>
    </footer>
    """
  end

  @doc """
  Renders the shop header with logo, navigation, and actions.

  ## Attributes

  * `theme_settings` - Required. The theme settings map.
  * `logo_image` - Optional. The logo image struct (with id, is_svg fields).
  * `header_image` - Optional. The header background image struct.
  * `active_page` - Optional. Current page for nav highlighting.
  * `mode` - Optional. Either `:live` (default) or `:preview`.
  * `cart_count` - Optional. Number of items in cart. Defaults to 0.

  ## Examples

      <.shop_header theme_settings={@theme_settings} />
      <.shop_header theme_settings={@theme_settings} mode={:preview} cart_count={2} />
  """
  attr :theme_settings, :map, required: true
  attr :site_name, :string, required: true
  attr :logo_image, :map, default: nil
  attr :header_image, :map, default: nil
  attr :active_page, :string, default: nil
  attr :mode, :atom, default: :live
  attr :cart_count, :integer, default: 0
  attr :is_admin, :boolean, default: false
  attr :header_nav_items, :list, default: []

  def shop_header(assigns) do
    ~H"""
    <header class="shop-header">
      <%= if @theme_settings.header_background_enabled && @header_image do %>
        <div style={header_background_style(@theme_settings, @header_image)} />
      <% end %>

      <%!-- Hamburger menu button (mobile only) --%>
      <button
        type="button"
        class="header-hamburger"
        phx-click={Phoenix.LiveView.JS.dispatch("open-mobile-nav", to: "#mobile-nav-drawer")}
        aria-label="Open menu"
      >
        <svg
          width="24"
          height="24"
          viewBox="0 0 24 24"
          fill="none"
          stroke="currentColor"
          stroke-width="2"
        >
          <line x1="3" y1="6" x2="21" y2="6"></line>
          <line x1="3" y1="12" x2="21" y2="12"></line>
          <line x1="3" y1="18" x2="21" y2="18"></line>
        </svg>
      </button>

      <div class="shop-logo">
        <.logo_content
          theme_settings={@theme_settings}
          site_name={@site_name}
          logo_image={@logo_image}
          active_page={@active_page}
          mode={@mode}
        />
      </div>

      <nav class="shop-nav">
        <.nav_item
          :for={item <- @header_nav_items}
          label={item["label"]}
          href={item["href"]}
          page={item["slug"] || ""}
          active_page={@active_page}
          active_pages={item["active_slugs"]}
          mode={@mode}
        />
      </nav>

      <div class="shop-actions">
        <%!-- Admin cog: always visible for admins, links to admin dashboard --%>
        <.link
          :if={@is_admin}
          href="/admin"
          class="header-icon-btn"
          aria-label="Admin dashboard"
        >
          <.admin_cog_svg />
        </.link>
        <a
          href={R.search()}
          phx-click={Phoenix.LiveView.JS.dispatch("open-search", to: "#search-modal")}
          class="header-icon-btn"
          aria-label="Search"
        >
          <svg
            width="20"
            height="20"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
          >
            <circle cx="11" cy="11" r="8"></circle>
            <path d="M21 21l-4.35-4.35"></path>
          </svg>
        </a>
        <a
          href={R.cart()}
          phx-click={open_cart_drawer_js()}
          class="header-icon-btn"
          aria-label="Cart"
        >
          <svg
            width="20"
            height="20"
            viewBox="0 0 24 24"
            fill="none"
            stroke="currentColor"
            stroke-width="2"
          >
            <path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"></path>
            <line x1="3" y1="6" x2="21" y2="6"></line>
            <path d="M16 10a4 4 0 01-8 0"></path>
          </svg>
          <%= if @cart_count > 0 do %>
            <span class="cart-badge">
              {@cart_count}
            </span>
          <% end %>
          <span class="sr-only">Cart ({@cart_count})</span>
        </a>
      </div>
    </header>
    """
  end

  defp logo_url(logo_image, %{logo_recolor: true, logo_color: color}) when logo_image.is_svg do
    clean_color = String.trim_leading(color, "#")
    "/images/#{logo_image.id}/recolored/#{clean_color}"
  end

  defp logo_url(logo_image, _), do: "/image_cache/#{logo_image.id}.webp"

  # Logo content that links to home, except when already on home page.
  # This follows accessibility best practices - current page should not be a link.
  attr :theme_settings, :map, required: true
  attr :site_name, :string, required: true
  attr :logo_image, :map, default: nil
  attr :active_page, :string, default: nil
  attr :mode, :atom, default: :live

  defp logo_content(assigns) do
    is_home = assigns.active_page == "home"
    assigns = assign(assigns, :is_home, is_home)

    ~H"""
    <%= if @is_home do %>
      <.logo_inner theme_settings={@theme_settings} site_name={@site_name} logo_image={@logo_image} />
    <% else %>
      <%= if @mode == :preview do %>
        <a
          href="#"
          phx-click="change_preview_page"
          phx-value-page="home"
          class="shop-logo-link"
        >
          <.logo_inner
            theme_settings={@theme_settings}
            site_name={@site_name}
            logo_image={@logo_image}
          />
        </a>
      <% else %>
        <.link patch="/" class="shop-logo-link">
          <.logo_inner
            theme_settings={@theme_settings}
            site_name={@site_name}
            logo_image={@logo_image}
          />
        </.link>
      <% end %>
    <% end %>
    """
  end

  attr :theme_settings, :map, required: true
  attr :site_name, :string, required: true
  attr :logo_image, :map, default: nil

  defp logo_inner(assigns) do
    # Show logo if enabled and image exists
    show_logo = assigns.theme_settings.show_logo && assigns.logo_image

    # Show site name if enabled, or as fallback when logo should show but image is missing
    show_site_name =
      assigns.theme_settings.show_site_name ||
        (assigns.theme_settings.show_logo && !assigns.logo_image)

    assigns =
      assigns
      |> assign(:show_logo, show_logo)
      |> assign(:show_site_name, show_site_name)

    ~H"""
    <%= if @show_logo do %>
      <img
        src={logo_url(@logo_image, @theme_settings)}
        alt={@site_name}
        class="shop-logo-img"
        style={"height: #{@theme_settings.logo_size}px;"}
      />
    <% end %>
    <%= if @show_site_name do %>
      <span class="shop-logo-text">
        {@site_name}
      </span>
    <% end %>
    """
  end

  defp header_background_style(settings, header_image) do
    "position: absolute; top: 0; left: 0; right: 0; bottom: 0; " <>
      "background-image: url('/image_cache/#{header_image.id}.webp'); " <>
      "background-size: #{settings.header_zoom}%; " <>
      "background-position: #{settings.header_position_x}% #{settings.header_position_y}%; " <>
      "background-repeat: no-repeat; z-index: 0;"
  end

  # Navigation item that renders as a span (not a link) when on the current page.
  # This follows accessibility best practices - current page should not be a link.
  attr :label, :string, required: true
  attr :page, :string, required: true
  attr :active_page, :string, required: true
  attr :href, :string, default: nil
  attr :mode, :atom, default: :live
  attr :active_pages, :list, default: nil

  defp nav_item(assigns) do
    # Allow matching multiple pages (e.g., "Shop" is active for both collection and pdp)
    active_pages = assigns.active_pages || [assigns.page]
    is_current = assigns.active_page in active_pages
    assigns = assign(assigns, :is_current, is_current)

    ~H"""
    <%= if @is_current do %>
      <span class="nav-link" aria-current="page">
        {@label}
      </span>
    <% else %>
      <%= if @mode == :preview do %>
        <a
          href="#"
          phx-click="change_preview_page"
          phx-value-page={@page}
          class="nav-link"
        >
          {@label}
        </a>
      <% else %>
        <.link patch={@href} class="nav-link">
          {@label}
        </.link>
      <% end %>
    <% end %>
    """
  end

  defp edit_pencil_svg(assigns) do
    ~H"""
    <svg
      width="20"
      height="20"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="1.5"
    >
      <path
        stroke-linecap="round"
        stroke-linejoin="round"
        d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L6.832 19.82a4.5 4.5 0 0 1-1.897 1.13l-2.685.8.8-2.685a4.5 4.5 0 0 1 1.13-1.897L16.863 4.487Zm0 0L19.5 7.125"
      />
    </svg>
    """
  end

  defp open_cart_drawer_js do
    Phoenix.LiveView.JS.push("open_cart_drawer")
  end

  # ── Editor sheet ────────────────────────────────────────────────────

  @doc """
  Renders the unified editor sheet for page/theme/settings editing.

  The sheet is anchored to the bottom edge on mobile (<768px) and the right edge
  on desktop (≥768px). It has three states on mobile (collapsed, partial, full)
  and two states on desktop (collapsed, open).

  ## Attributes

  * `editing` - Whether page edit mode is active.
  * `theme_editing` - Whether theme edit mode is active.
  * `editor_dirty` - Whether there are unsaved page changes.
  * `editor_sheet_state` - Current state (:collapsed, :partial, :full, or :open).
  * `editor_active_tab` - Current tab (:page, :theme, :settings).
  * `has_editable_page` - Whether the current page has editable blocks.

  ## Slots

  * `inner_block` - The editor content (block list, settings, etc.).
  """
  attr :editing, :boolean, default: false
  attr :theme_editing, :boolean, default: false
  attr :editor_dirty, :boolean, default: false
  attr :theme_dirty, :boolean, default: false
  attr :site_dirty, :boolean, default: false
  attr :settings_dirty, :boolean, default: false
  attr :editor_sheet_state, :atom, default: :collapsed
  attr :editor_save_status, :atom, default: :idle
  attr :editor_active_tab, :atom, default: :page
  attr :editor_nav_blocked, :string, default: nil
  attr :has_editable_page, :boolean, default: false

  slot :inner_block

  def editor_sheet(assigns) do
    # Determine panel title based on active tab
    title =
      case assigns.editor_active_tab do
        :page -> "Page"
        :theme -> "Theme"
        :site -> "Site"
        :settings -> "Settings"
      end

    # Any editing mode active
    any_editing = assigns.editing || assigns.theme_editing

    # Any tab has unsaved changes
    any_dirty =
      assigns.editor_dirty || assigns.theme_dirty || assigns.site_dirty || assigns.settings_dirty

    assigns =
      assigns
      |> assign(:title, title)
      |> assign(:any_editing, any_editing)
      |> assign(:any_dirty, any_dirty)

    ~H"""
    <%!-- Floating action button: always visible when panel is closed --%>
    <button
      :if={@editor_sheet_state == :collapsed}
      type="button"
      phx-click={if @any_editing, do: "editor_set_sheet_state", else: "editor_set_tab"}
      phx-value-state={if @any_editing, do: "open", else: nil}
      phx-value-tab={
        if @any_editing, do: nil, else: if(@has_editable_page, do: "page", else: "theme")
      }
      class="editor-fab"
      aria-label={if @any_editing, do: "Show editor", else: "Edit"}
    >
      <.edit_pencil_svg />
      <span>{if @any_editing, do: "Show editor", else: "Edit"}</span>
      <span :if={@any_editing && @any_dirty} class="editor-fab-dirty" aria-label="Unsaved changes" />
    </button>

    <%!-- Overlay to catch taps outside the panel --%>
    <div
      :if={@editor_sheet_state == :open}
      class="editor-overlay"
      phx-click="editor_set_sheet_state"
      phx-value-state="collapsed"
      aria-hidden="true"
    />

    <%!-- Editor panel: slides in/out --%>
    <aside
      id="editor-panel"
      class="editor-panel"
      role="region"
      aria-label="Site editor"
      aria-hidden={to_string(@editor_sheet_state == :collapsed)}
      data-state={@editor_sheet_state}
      data-editing={to_string(@any_editing)}
      data-dirty={to_string(@any_dirty)}
      phx-hook="EditorSheet"
    >
      <%!-- Drag handle for mobile resizing --%>
      <div class="editor-panel-drag-handle" data-drag-handle>
        <div class="editor-panel-drag-handle-bar" />
      </div>

      <div class="editor-panel-header">
        <div class="editor-panel-header-left">
          <span class="editor-panel-title">{@title}</span>
          <span :if={@any_dirty} class="editor-panel-dirty" aria-live="polite">
            <span class="editor-panel-dirty-dot" aria-hidden="true" />
            <span>Unsaved</span>
          </span>
        </div>
        <div class="editor-panel-header-actions">
          <button
            :if={@editor_save_status == :saved}
            type="button"
            class="admin-btn admin-btn-sm admin-btn-ghost"
            disabled
          >
            Saved ✓
          </button>
          <button
            :if={@editor_save_status != :saved}
            type="button"
            phx-click="editor_save_all"
            class={["admin-btn admin-btn-sm", @any_dirty && "admin-btn-primary"]}
            disabled={!@any_dirty}
          >
            Save
          </button>
          <button
            type="button"
            phx-click="editor_set_sheet_state"
            phx-value-state="collapsed"
            class="editor-panel-close"
            aria-label="Close editor"
          >
            <svg
              xmlns="http://www.w3.org/2000/svg"
              fill="none"
              viewBox="0 0 24 24"
              stroke-width="2"
              stroke="currentColor"
              width="20"
              height="20"
            >
              <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
            </svg>
          </button>
        </div>
      </div>

      <%!-- Tab bar --%>
      <div class="editor-tabs" role="tablist">
        <button
          type="button"
          role="tab"
          phx-click="editor_set_tab"
          phx-value-tab="page"
          class={["editor-tab", @editor_active_tab == :page && "editor-tab-active"]}
          aria-selected={to_string(@editor_active_tab == :page)}
          disabled={!@has_editable_page}
          title={
            if @has_editable_page, do: "Edit page blocks", else: "This page has no editable blocks"
          }
        >
          Page
          <span
            :if={@editor_dirty}
            class="editor-tab-dirty-dot"
            aria-label="unsaved changes"
          />
        </button>
        <button
          type="button"
          role="tab"
          phx-click="editor_set_tab"
          phx-value-tab="theme"
          class={["editor-tab", @editor_active_tab == :theme && "editor-tab-active"]}
          aria-selected={to_string(@editor_active_tab == :theme)}
        >
          Theme
          <span
            :if={@theme_dirty}
            class="editor-tab-dirty-dot"
            aria-label="unsaved changes"
          />
        </button>
        <button
          type="button"
          role="tab"
          phx-click="editor_set_tab"
          phx-value-tab="site"
          class={["editor-tab", @editor_active_tab == :site && "editor-tab-active"]}
          aria-selected={to_string(@editor_active_tab == :site)}
        >
          Site
          <span
            :if={@site_dirty}
            class="editor-tab-dirty-dot"
            aria-label="unsaved changes"
          />
        </button>
      </div>

      <div class="editor-panel-content">
        {render_slot(@inner_block)}
      </div>
    </aside>

    <%!-- Live region for screen reader announcements --%>
    <div id="editor-live-region" class="sr-only" aria-live="polite" aria-atomic="true" />

    <%!-- Navigation warning modal --%>
    <dialog :if={@editor_nav_blocked} class="editor-nav-modal" open>
      <div class="editor-nav-modal-content">
        <h3>Unsaved changes</h3>
        <p>You have unsaved changes that will be lost if you leave.</p>
        <div class="editor-nav-modal-actions">
          <button
            type="button"
            phx-click="editor_save_and_navigate"
            class="admin-btn admin-btn-sm admin-btn-primary"
          >
            Save and go
          </button>
          <button
            type="button"
            phx-click="editor_discard_and_navigate"
            class="admin-btn admin-btn-sm admin-btn-danger"
          >
            Don't save
          </button>
          <button
            type="button"
            phx-click="editor_cancel_navigate"
            class="admin-btn admin-btn-sm"
          >
            Stay here
          </button>
        </div>
      </div>
    </dialog>
    """
  end

  # ── Admin rail (deprecated) ────────────────────────────────────────

  @doc """
  Renders the admin rail with edit and admin icons.

  This thin vertical bar appears on the left edge of the page for logged-in admins.
  The edit button toggles the page editor, and the cog links to the admin dashboard.
  """
  attr :editing, :boolean, default: false
  attr :editor_dirty, :boolean, default: false
  attr :editor_sidebar_open, :boolean, default: true
  slot :editor_sidebar
  slot :inner_block, required: true

  def admin_rail(assigns) do
    ~H"""
    <div
      class="admin-rail-layout"
      data-editing={to_string(@editing)}
      data-sidebar-open={to_string(@editor_sidebar_open)}
    >
      <aside class="admin-rail" aria-label="Admin tools">
        <button
          type="button"
          phx-click="editor_toggle_editing"
          class={["admin-rail-btn", @editing && "admin-rail-btn-active"]}
          aria-label={if @editing, do: "Close editor", else: "Edit page"}
          aria-pressed={to_string(@editing)}
        >
          <.edit_pencil_svg />
          <span
            :if={@editing && @editor_dirty}
            class="admin-rail-dirty-dot"
            aria-label="Unsaved changes"
          />
        </button>

        <.link href="/admin" class="admin-rail-btn" aria-label="Admin dashboard">
          <.admin_cog_svg />
        </.link>
      </aside>

      <aside :if={@editing} class="admin-rail-sidebar" aria-label="Page editor">
        {render_slot(@editor_sidebar)}
      </aside>

      <%!-- Backdrop to close sidebar on mobile --%>
      <div
        :if={@editing && @editor_sidebar_open}
        class="admin-rail-backdrop"
        phx-click="editor_toggle_sidebar"
        aria-hidden="true"
      />

      <div class="admin-rail-content">
        {render_slot(@inner_block)}
      </div>
    </div>
    """
  end

  def admin_cog_svg(assigns) do
    ~H"""
    <svg
      width="20"
      height="20"
      viewBox="0 0 24 24"
      fill="none"
      stroke="currentColor"
      stroke-width="1.5"
    >
      <path
        stroke-linecap="round"
        stroke-linejoin="round"
        d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a7.723 7.723 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.47 6.47 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a6.932 6.932 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.086.22-.128.332-.183.582-.495.644-.869l.214-1.28Z"
      />
      <path
        stroke-linecap="round"
        stroke-linejoin="round"
        d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
      />
    </svg>
    """
  end
end
