defmodule BerrypodWeb.PageRenderer do
  @moduledoc """
  Generic page renderer for the page builder.

  One `render_page/1` renders ALL pages — no page-specific render functions.
  Each page is a flat list of blocks rendered in order. Block components
  dispatch to existing shop components.
  """

  use Phoenix.Component
  use BerrypodWeb.ShopComponents

  use Phoenix.VerifiedRoutes,
    endpoint: BerrypodWeb.Endpoint,
    router: BerrypodWeb.Router,
    statics: BerrypodWeb.static_paths()

  import BerrypodWeb.BlockEditorComponents
  import BerrypodWeb.CoreComponents, only: [icon: 1, external_link: 1]

  alias Berrypod.Cart
  alias BerrypodWeb.R

  # ── Public API ──────────────────────────────────────────────────

  @doc """
  Renders a full page from its block definition.

  Expects `@page` (with `:slug` and `:blocks`) plus all the standard
  layout assigns (theme_settings, cart_items, etc.).

  When `@editing` is true and `@editing_blocks` is set (admin using the
  live page editor), wraps the page in a sidebar + content layout.
  """
  def render_page(assigns) do
    assigns = assign(assigns, :block_assigns, block_assigns(assigns))

    if assigns[:is_admin] do
      render_page_with_rail(assigns)
    else
      render_page_normal(assigns)
    end
  end

  defp render_page_normal(assigns) do
    ~H"""
    <.shop_layout
      {layout_assigns(assigns)}
      active_page={@page.slug}
      error_page={@page.slug == "error"}
    >
      <main id="main-content" class={page_main_class(@page.slug)}>
        <div :for={block <- @page.blocks} :key={block["id"]} data-block-type={block["type"]}>
          {render_block(Map.merge(@block_assigns, %{block: block, page_slug: @page.slug}))}
        </div>
      </main>
    </.shop_layout>
    """
  end

  defp render_page_with_rail(assigns) do
    ~H"""
    <.shop_layout
      {layout_assigns(assigns)}
      active_page={@page.slug}
      error_page={@page.slug == "error"}
    >
      <main id="main-content" class={page_main_class(@page.slug)}>
        <%= if @editing && @editing_blocks do %>
          <div
            :for={block <- @editing_blocks}
            :key={block["id"]}
            data-block-type={block["type"]}
          >
            {render_block(Map.merge(@block_assigns, %{block: block, page_slug: @page.slug}))}
          </div>
        <% else %>
          <div :for={block <- @page.blocks} :key={block["id"]} data-block-type={block["type"]}>
            {render_block(Map.merge(@block_assigns, %{block: block, page_slug: @page.slug}))}
          </div>
        <% end %>
      </main>
    </.shop_layout>

    <%!-- Editor sheet for page/theme/settings editing --%>
    <.editor_sheet
      editing={@editing}
      theme_editing={Map.get(assigns, :theme_editing, false)}
      editor_dirty={@editor_dirty}
      theme_dirty={Map.get(assigns, :theme_dirty, false)}
      site_dirty={Map.get(assigns, :site_dirty, false)}
      settings_dirty={Map.get(assigns, :settings_dirty, false)}
      editor_sheet_state={assigns[:editor_sheet_state] || :collapsed}
      editor_save_status={@editor_save_status}
      editor_active_tab={Map.get(assigns, :editor_active_tab, :page)}
      editor_nav_blocked={Map.get(assigns, :editor_nav_blocked)}
      has_editable_page={@page != nil}
    >
      <.editor_panel_content
        editor_active_tab={Map.get(assigns, :editor_active_tab, :page)}
        page={@page}
        editing_blocks={@editing_blocks}
        editor_history={@editor_history}
        editor_future={@editor_future}
        editor_dirty={@editor_dirty}
        editor_live_region_message={@editor_live_region_message}
        editor_expanded={@editor_expanded}
        editor_show_picker={@editor_show_picker}
        editor_picker_filter={@editor_picker_filter}
        editor_allowed_blocks={@editor_allowed_blocks}
        editor_image_picker_block_id={@editor_image_picker_block_id}
        editor_image_picker_images={@editor_image_picker_images}
        editor_image_picker_search={@editor_image_picker_search}
        editor_at_defaults={Map.get(assigns, :editor_at_defaults, true)}
        theme_editor_settings={Map.get(assigns, :theme_editor_settings)}
        theme_editor_active_preset={Map.get(assigns, :theme_editor_active_preset)}
        theme_editor_presets={Map.get(assigns, :theme_editor_presets, [])}
        theme_editor_customise_open={Map.get(assigns, :theme_editor_customise_open, false)}
        theme_editor_logo_image={Map.get(assigns, :theme_editor_logo_image)}
        theme_editor_header_image={Map.get(assigns, :theme_editor_header_image)}
        theme_editor_icon_image={Map.get(assigns, :theme_editor_icon_image)}
        theme_editor_contrast_warning={Map.get(assigns, :theme_editor_contrast_warning, :ok)}
        uploads={Map.get(assigns, :uploads)}
        site_name={Map.get(assigns, :site_name, "")}
        settings_form={Map.get(assigns, :settings_form)}
        settings_dirty={Map.get(assigns, :settings_dirty, false)}
        settings_save_status={Map.get(assigns, :settings_save_status, :idle)}
        site_state={Map.get(assigns, :site_state)}
        site_nav_pages={Map.get(assigns, :site_nav_pages, [])}
      />
    </.editor_sheet>
    """
  end

  # Editor panel content dispatcher - shows content based on active tab
  attr :editor_active_tab, :atom, default: :page
  attr :page, :map, default: nil
  attr :editing_blocks, :list, default: nil
  attr :editor_history, :list, default: []
  attr :editor_future, :list, default: []
  attr :editor_dirty, :boolean, default: false
  attr :editor_live_region_message, :string, default: nil
  attr :editor_expanded, :any, default: nil
  attr :editor_show_picker, :boolean, default: false
  attr :editor_picker_filter, :string, default: ""
  attr :editor_allowed_blocks, :list, default: nil
  attr :editor_image_picker_block_id, :string, default: nil
  attr :editor_image_picker_images, :list, default: []
  attr :editor_image_picker_search, :string, default: ""
  attr :editor_at_defaults, :boolean, default: true
  attr :theme_editor_settings, :map, default: nil
  attr :theme_editor_active_preset, :atom, default: nil
  attr :theme_editor_presets, :list, default: []
  attr :theme_editor_customise_open, :boolean, default: false
  attr :theme_editor_logo_image, :map, default: nil
  attr :theme_editor_header_image, :map, default: nil
  attr :theme_editor_icon_image, :map, default: nil
  attr :theme_editor_contrast_warning, :atom, default: :ok
  attr :uploads, :map, default: nil
  attr :site_name, :string, default: ""
  attr :settings_form, :map, default: nil
  attr :settings_dirty, :boolean, default: false
  attr :settings_save_status, :atom, default: :idle
  attr :site_state, :any, default: nil
  attr :site_nav_pages, :list, default: []

  defp editor_panel_content(%{editor_active_tab: :page} = assigns) do
    ~H"""
    <.editor_sheet_content
      page={@page}
      editing_blocks={@editing_blocks}
      editor_history={@editor_history}
      editor_future={@editor_future}
      editor_dirty={@editor_dirty}
      editor_live_region_message={@editor_live_region_message}
      editor_expanded={@editor_expanded}
      editor_show_picker={@editor_show_picker}
      editor_picker_filter={@editor_picker_filter}
      editor_allowed_blocks={@editor_allowed_blocks}
      editor_image_picker_block_id={@editor_image_picker_block_id}
      editor_image_picker_images={@editor_image_picker_images}
      editor_image_picker_search={@editor_image_picker_search}
      editor_at_defaults={@editor_at_defaults}
      settings_form={@settings_form}
      settings_dirty={@settings_dirty}
      settings_save_status={@settings_save_status}
    />
    """
  end

  defp editor_panel_content(%{editor_active_tab: :theme} = assigns) do
    ~H"""
    <.theme_editor_content
      theme_editor_settings={@theme_editor_settings}
      theme_editor_active_preset={@theme_editor_active_preset}
      theme_editor_presets={@theme_editor_presets}
      theme_editor_customise_open={@theme_editor_customise_open}
      theme_editor_logo_image={@theme_editor_logo_image}
      theme_editor_header_image={@theme_editor_header_image}
      theme_editor_icon_image={@theme_editor_icon_image}
      theme_editor_contrast_warning={@theme_editor_contrast_warning}
      uploads={@uploads}
      site_name={@site_name}
    />
    """
  end

  defp editor_panel_content(%{editor_active_tab: :site} = assigns) do
    ~H"""
    <BerrypodWeb.ShopComponents.SiteEditor.site_editor
      site_state={@site_state}
      site_nav_pages={@site_nav_pages}
      site_name={@site_name}
      theme_settings={@theme_editor_settings}
      logo_image={@theme_editor_logo_image}
      header_image={@theme_editor_header_image}
      icon_image={@theme_editor_icon_image}
      contrast_warning={@theme_editor_contrast_warning}
      uploads={@uploads}
    />
    """
  end

  # Theme editor content - uses shared component
  attr :theme_editor_settings, :map, default: nil
  attr :theme_editor_active_preset, :atom, default: nil
  attr :theme_editor_presets, :list, default: []
  attr :theme_editor_customise_open, :boolean, default: false
  attr :theme_editor_logo_image, :map, default: nil
  attr :theme_editor_header_image, :map, default: nil
  attr :theme_editor_icon_image, :map, default: nil
  attr :theme_editor_contrast_warning, :atom, default: :ok
  attr :uploads, :map, default: nil
  attr :site_name, :string, default: ""

  defp theme_editor_content(assigns) do
    ~H"""
    <.compact_editor
      theme_settings={@theme_editor_settings}
      active_preset={@theme_editor_active_preset}
      presets={@theme_editor_presets}
      site_name={@site_name}
      customise_open={@theme_editor_customise_open}
      uploads={@uploads}
      logo_image={@theme_editor_logo_image}
      header_image={@theme_editor_header_image}
      icon_image={@theme_editor_icon_image}
      contrast_warning={@theme_editor_contrast_warning}
      event_prefix="theme_"
    />
    """
  end

  # Editor sheet content - the block list and editing controls
  attr :page, :map, default: nil
  attr :editing_blocks, :list, default: nil
  attr :editor_history, :list, default: []
  attr :editor_future, :list, default: []
  attr :editor_dirty, :boolean, default: false
  attr :editor_live_region_message, :string, default: nil
  attr :editor_expanded, :any, default: nil
  attr :editor_show_picker, :boolean, default: false
  attr :editor_picker_filter, :string, default: ""
  attr :editor_allowed_blocks, :list, default: nil
  attr :editor_image_picker_block_id, :string, default: nil
  attr :editor_image_picker_images, :list, default: []
  attr :editor_image_picker_search, :string, default: ""
  attr :editor_at_defaults, :boolean, default: true
  attr :settings_form, :map, default: nil
  attr :settings_dirty, :boolean, default: false
  attr :settings_save_status, :atom, default: :idle

  defp editor_sheet_content(assigns) do
    ~H"""
    <div id="editor-sheet-inner" phx-hook="EditorKeyboard" data-dirty={to_string(@editor_dirty)}>
      <%!-- Page title and undo/redo --%>
      <div class="editor-panel-page-header">
        <h3 class="editor-panel-page-title">{@page.title}</h3>
        <div class="editor-panel-undo-redo">
          <button
            phx-click="editor_undo"
            class={[
              "admin-btn admin-btn-sm admin-btn-ghost",
              @editor_history == [] && "opacity-30"
            ]}
            disabled={@editor_history == []}
            aria-label="Undo"
          >
            <.icon name="hero-arrow-uturn-left" class="size-4" />
          </button>
          <button
            phx-click="editor_redo"
            class={[
              "admin-btn admin-btn-sm admin-btn-ghost",
              @editor_future == [] && "opacity-30"
            ]}
            disabled={@editor_future == []}
            aria-label="Redo"
          >
            <.icon name="hero-arrow-uturn-right" class="size-4" />
          </button>
        </div>
      </div>

      <%!-- ARIA live region for screen reader announcements --%>
      <div aria-live="polite" aria-atomic="true" class="sr-only">
        {if @editor_live_region_message, do: @editor_live_region_message}
      </div>

      <%!-- Page settings (custom and system pages, not product/collection) --%>
      <.page_settings_section
        :if={@page[:type] in ["custom", "system"]}
        page={@page}
        form={@settings_form}
        dirty={@settings_dirty}
        save_status={@settings_save_status}
      />

      <%!-- Block list --%>
      <div class="block-list" role="list" aria-label="Page blocks">
        <.block_card
          :for={{block, idx} <- Enum.with_index(@editing_blocks || [])}
          block={block}
          idx={idx}
          total={length(@editing_blocks || [])}
          expanded={@editor_expanded || MapSet.new()}
          event_prefix="editor_"
        />

        <div :if={(@editing_blocks || []) == []} class="block-list-empty">
          <p>No blocks on this page yet.</p>
        </div>
      </div>

      <%!-- Add block button --%>
      <div class="block-actions">
        <button phx-click="editor_show_picker" class="admin-btn admin-btn-outline block-add-btn">
          <.icon name="hero-plus" class="size-4" /> Add block
        </button>
        <button
          phx-click="editor_reset_defaults"
          data-confirm="Reset this page to its default blocks? You can undo this."
          class="admin-btn admin-btn-sm admin-btn-ghost"
          disabled={@editor_at_defaults}
        >
          <.icon name="hero-arrow-path" class="size-4" /> Reset to defaults
        </button>
      </div>

      <%!-- Block picker modal --%>
      <.block_picker
        :if={@editor_show_picker}
        allowed_blocks={@editor_allowed_blocks}
        filter={@editor_picker_filter}
        event_prefix="editor_"
      />

      <%!-- Image picker modal --%>
      <.image_picker
        :if={@editor_image_picker_block_id}
        images={@editor_image_picker_images}
        search={@editor_image_picker_search}
        event_prefix="editor_"
      />
    </div>
    """
  end

  # Page settings section (collapsible) for custom and system pages
  attr :page, :map, required: true
  attr :form, :map, default: nil
  attr :dirty, :boolean, default: false
  attr :save_status, :atom, default: :idle

  defp page_settings_section(assigns) do
    form = assigns.form || %{}
    is_custom = assigns.page[:type] == "custom"
    page = assigns.page

    # Determine URL structure for this page
    url_info = compute_url_info(page, form)

    assigns =
      assigns
      |> assign(:is_custom, is_custom)
      |> assign(:url_info, url_info)
      |> assign(:form_title, form["title"] || page[:title] || "")
      |> assign(:form_slug, form["slug"] || page[:slug] || "")
      |> assign(:form_url_slug, form["url_slug"] || page[:url_slug] || "")
      |> assign(:form_meta, form["meta_description"] || page[:meta_description] || "")
      |> assign(:form_published, form_checked?(form, "published", page[:published]))
      |> assign(:form_show_in_nav, form_checked?(form, "show_in_nav", page[:show_in_nav]))
      |> assign(:form_nav_label, form["nav_label"] || page[:nav_label] || "")
      |> assign(
        :form_nav_position,
        form["nav_position"] || to_string(page[:nav_position] || 0)
      )

    ~H"""
    <details class="page-settings-details" open>
      <summary class="page-settings-summary">
        <.icon name="hero-cog-6-tooth-mini" class="size-4" />
        <span>Page settings</span>
        <.icon name="hero-chevron-down-mini" class="size-4 page-settings-chevron" />
      </summary>

      <form
        id="page-settings-form"
        class="page-settings-form"
        phx-change="settings_validate_page"
        phx-submit="settings_save_page"
      >
        <div class="page-settings-field">
          <label class="page-settings-label" for="page-settings-title">Title</label>
          <input
            type="text"
            id="page-settings-title"
            name="page[title]"
            value={@form_title}
            class="admin-input"
          />
        </div>

        <%!-- URL editor - different layouts based on page type --%>
        <.url_editor url_info={@url_info} is_custom={@is_custom} form_slug={@form_slug} />

        <div class="page-settings-field">
          <label class="page-settings-label" for="page-settings-meta">Meta description</label>
          <textarea
            id="page-settings-meta"
            name="page[meta_description]"
            rows="2"
            class="admin-input admin-textarea"
            placeholder="Brief description for search engines..."
          >{@form_meta}</textarea>
        </div>

        <%!-- Published and nav options only for custom pages --%>
        <div :if={@is_custom} class="page-settings-checks">
          <label class="admin-check-label">
            <input
              type="checkbox"
              name="page[published]"
              value="true"
              checked={@form_published}
              class="admin-checkbox admin-checkbox-sm"
            />
            <span>Published</span>
          </label>

          <label class="admin-check-label">
            <input
              type="checkbox"
              name="page[show_in_nav]"
              value="true"
              checked={@form_show_in_nav}
              class="admin-checkbox admin-checkbox-sm"
            />
            <span>Show in navigation</span>
          </label>
        </div>

        <div :if={@is_custom && @form_show_in_nav} class="page-settings-nav-options">
          <div class="page-settings-field page-settings-field-inline">
            <label class="page-settings-label" for="page-settings-nav-label">Nav label</label>
            <input
              type="text"
              id="page-settings-nav-label"
              name="page[nav_label]"
              value={@form_nav_label}
              class="admin-input"
              placeholder={@page.title}
            />
          </div>
          <div class="page-settings-field page-settings-field-sm">
            <label class="page-settings-label" for="page-settings-nav-position">Position</label>
            <input
              type="number"
              id="page-settings-nav-position"
              name="page[nav_position]"
              value={@form_nav_position}
              class="admin-input"
              min="0"
            />
          </div>
        </div>
      </form>
    </details>
    """
  end

  # URL editor component - renders the right UI based on page type
  attr :url_info, :map, required: true
  attr :is_custom, :boolean, required: true
  attr :form_slug, :string, required: true

  defp url_editor(%{url_info: %{type: :none}} = assigns) do
    # Home page - no URL field
    ~H""
  end

  defp url_editor(%{url_info: %{type: :simple}} = assigns) do
    # Simple single-segment URL (about, contact, cart, custom pages, etc.)
    ~H"""
    <div class="page-settings-field">
      <label class="page-settings-label" for="page-settings-url">URL</label>
      <div class="url-editor">
        <span class="url-editor-slash">/</span>
        <input
          type="text"
          id="page-settings-url"
          name={@url_info.field_name}
          value={@url_info.value}
          class="admin-input url-editor-input"
          pattern="[a-z0-9-]+"
          phx-debounce="500"
        />
      </div>
      <p :if={@url_info.hint} class="page-settings-hint">{@url_info.hint}</p>
    </div>
    """
  end

  defp url_editor(%{url_info: %{type: :prefixed}} = assigns) do
    # Prefixed URL (collections, products, orders)
    ~H"""
    <div class="page-settings-field">
      <label class="page-settings-label">URL</label>
      <div class="url-editor url-editor-segmented">
        <span class="url-editor-slash">/</span>
        <input
          type="text"
          id="page-settings-prefix"
          name="page[url_prefix]"
          value={@url_info.prefix}
          class="admin-input url-editor-input url-editor-prefix"
          pattern="[a-z0-9-]+"
          phx-debounce="500"
        />
        <span class="url-editor-slash">/</span>
        <span class="url-editor-fixed">{@url_info.suffix}</span>
      </div>
      <p :if={@url_info.hint} class="page-settings-hint">{@url_info.hint}</p>
    </div>
    """
  end

  defp url_editor(%{url_info: %{type: :fixed}} = assigns) do
    # Fixed URL (checkout/success, etc.)
    ~H"""
    <div class="page-settings-field">
      <label class="page-settings-label">URL</label>
      <div class="url-editor">
        <span class="url-editor-fixed-path">{@url_info.path}</span>
      </div>
    </div>
    """
  end

  # Compute URL info for a page based on its type
  # Uses generic placeholders for prefixed pages since we're editing the template
  defp compute_url_info(page, form) do
    slug = page[:slug]

    case slug do
      # Home - no URL field
      "home" ->
        %{type: :none}

      # Prefixed pages - show editable prefix + placeholder suffix
      "collection" ->
        prefix = form["url_prefix"] || R.prefix(:collections)

        %{
          type: :prefixed,
          prefix: prefix,
          prefix_type: :collections,
          suffix: ":slug",
          hint: "Changing the prefix affects all collection pages"
        }

      "pdp" ->
        prefix = form["url_prefix"] || R.prefix(:products)

        %{
          type: :prefixed,
          prefix: prefix,
          prefix_type: :products,
          suffix: ":id",
          hint: "Changing the prefix affects all product pages"
        }

      "orders" ->
        prefix = form["url_prefix"] || R.prefix(:orders)

        %{
          type: :prefixed,
          prefix: prefix,
          prefix_type: :orders,
          suffix: ":number",
          hint: "Changing the prefix affects all order pages"
        }

      "order_detail" ->
        prefix = form["url_prefix"] || R.prefix(:orders)

        %{
          type: :prefixed,
          prefix: prefix,
          prefix_type: :orders,
          suffix: ":number",
          hint: "Changing the prefix affects all order pages"
        }

      # Fixed paths
      "checkout_success" ->
        %{type: :fixed, path: "/checkout/success"}

      "error" ->
        %{type: :none}

      # Simple system pages and custom pages
      _ ->
        if page[:type] == "custom" do
          # Custom page - edit the slug directly
          value = form["slug"] || slug

          %{
            type: :simple,
            value: value,
            field_name: "page[slug]",
            hint: nil
          }
        else
          # System page - edit the url_slug override
          value = form["url_slug"] || page[:url_slug] || ""

          %{
            type: :simple,
            value: value,
            field_name: "page[url_slug]",
            hint:
              if(value == "",
                do: "Default: /#{slug}",
                else: "Old URL /#{slug} will redirect here"
              )
          }
        end
    end
  end

  defp form_checked?(form, key, page_value) when is_map(form) do
    case form[key] do
      "true" -> true
      "false" -> false
      nil -> page_value == true
      _ -> page_value == true
    end
  end

  # ── Block dispatch ──────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "hero"}} = assigns) do
    settings = assigns.block["settings"] || %{}
    variant = hero_variant(settings["variant"])

    # Page-context assigns (e.g. error_title, error_code) override block settings
    assigns =
      assigns
      |> assign(:title, assigns[:error_title] || settings["title"] || "")
      |> assign(:description, assigns[:error_description] || settings["description"] || "")
      |> assign(:variant, variant)
      |> assign(:background, hero_background(settings["variant"]))
      |> assign(:pre_title, assigns[:error_code] || settings["pre_title"])
      |> assign(:cta_text, settings["cta_text"])
      |> assign(:cta_href, settings["cta_href"])
      |> assign(:secondary_cta_text, settings["secondary_cta_text"])
      |> assign(:secondary_cta_href, settings["secondary_cta_href"])

    ~H"""
    <.hero_section
      title={@title}
      description={@description}
      variant={@variant}
      background={@background}
      pre_title={@pre_title}
      cta_text={@cta_text}
      cta_href={@cta_href}
      secondary_cta_text={@secondary_cta_text}
      secondary_cta_href={@secondary_cta_href}
      mode={@mode}
    />
    """
  end

  defp render_block(%{block: %{"type" => "category_nav"}} = assigns) do
    ~H"""
    <.category_nav categories={assigns[:categories] || []} mode={@mode} />
    """
  end

  defp render_block(%{block: %{"type" => "featured_products"}} = assigns) do
    settings = assigns.block["settings"] || %{}

    assigns =
      assigns
      |> assign(:section_title, settings["title"] || "Featured products")
      |> assign(:layout, settings["layout"] || "section")
      |> assign(:card_variant, card_variant(settings["card_variant"]))
      |> assign(:columns, grid_columns(settings["columns"]))

    ~H"""
    <%= if @layout == "grid" do %>
      <.product_grid columns={@columns} theme_settings={@theme_settings}>
        <%= for product <- assigns[:products] || [] do %>
          <.product_card
            product={product}
            theme_settings={@theme_settings}
            mode={@mode}
            variant={@card_variant}
          />
        <% end %>
      </.product_grid>
    <% else %>
      <.featured_products_section
        title={@section_title}
        products={assigns[:products] || []}
        theme_settings={@theme_settings}
        mode={@mode}
      />
    <% end %>
    """
  end

  defp render_block(%{block: %{"type" => "image_text"}} = assigns) do
    settings = assigns.block["settings"] || %{}
    image_url = resolve_block_image_url(settings["image_id"])

    assigns =
      assigns
      |> assign(:section_title, settings["title"] || "")
      |> assign(:section_description, settings["description"] || "")
      |> assign(:image_url, image_url)
      |> assign(:link_text, settings["link_text"])
      |> assign(:link_href, settings["link_href"])

    ~H"""
    <.image_text_section
      title={@section_title}
      description={@section_description}
      image_url={@image_url}
      link_text={@link_text}
      link_href={@link_href}
      mode={@mode}
    />
    """
  end

  defp render_block(%{block: %{"type" => "newsletter_card"}} = assigns) do
    settings = assigns.block["settings"] || %{}

    assigns =
      assigns
      |> assign(:title, settings["title"] || "Newsletter")
      |> assign(:description, settings["description"] || "")
      |> assign(:button_text, settings["button_text"] || "Subscribe")
      |> assign(:newsletter_state, assigns[:newsletter_state] || :idle)
      |> assign(:newsletter_enabled, assigns[:newsletter_enabled] || false)

    ~H"""
    <.newsletter_card
      title={@title}
      description={@description}
      button_text={@button_text}
      newsletter_state={@newsletter_state}
      newsletter_enabled={@newsletter_enabled}
    />
    """
  end

  defp render_block(%{block: %{"type" => "social_links_card"}} = assigns) do
    ~H"<.social_links_card links={assigns[:social_links] || []} />"
  end

  defp render_block(%{block: %{"type" => "info_card"}} = assigns) do
    settings = assigns.block["settings"] || %{}

    items =
      (settings["items"] || [])
      |> Enum.map(fn item ->
        %{label: item["label"] || item[:label], value: item["value"] || item[:value]}
      end)

    assigns =
      assigns
      |> assign(:card_title, settings["title"] || "")
      |> assign(:items, items)

    ~H"""
    <.info_card title={@card_title} items={@items} />
    """
  end

  defp render_block(%{block: %{"type" => "trust_badges"}} = assigns) do
    ~H"""
    <.trust_badges :if={@theme_settings.pdp_trust_badges} />
    """
  end

  defp render_block(%{block: %{"type" => "reviews_section"}} = assigns) do
    ~H"""
    <.reviews_section
      :if={@theme_settings.pdp_reviews}
      reviews={assigns[:reviews] || []}
      average_rating={assigns[:average_rating]}
      total_count={assigns[:review_count]}
      product={assigns[:product]}
      email_session={assigns[:email_session]}
      existing_review={assigns[:existing_review]}
      review_form={assigns[:review_form]}
      review_status={assigns[:review_status]}
    />
    """
  end

  # ── PDP blocks ──────────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "breadcrumb"}} = assigns) do
    ~H"""
    <.breadcrumb
      :if={assigns[:product]}
      items={breadcrumb_items(assigns[:product])}
      mode={@mode}
    />
    """
  end

  defp render_block(%{block: %{"type" => "product_hero"}} = assigns) do
    ~H"""
    <%= if assigns[:product] do %>
      <div class="pdp-grid">
        <.product_gallery images={assigns[:gallery_images] || []} product_name={@product.title} />

        <div>
          <.product_info product={@product} display_price={assigns[:display_price]} />

          <form action="/cart/add" method="post" phx-submit="add_to_cart">
            <input type="hidden" name="_csrf_token" value={Phoenix.Controller.get_csrf_token()} />
            <input
              type="hidden"
              name="variant_id"
              value={assigns[:selected_variant] && assigns[:selected_variant].id}
            />

            <%= for option_type <- assigns[:option_types] || [] do %>
              <.variant_selector
                option_type={option_type}
                selected={(assigns[:selected_options] || %{})[option_type.name]}
                available={(assigns[:available_options] || %{})[option_type.name] || []}
                mode={@mode}
                option_urls={(assigns[:option_urls] || %{})[option_type.name] || %{}}
              />
            <% end %>

            <div
              :if={(assigns[:option_types] || []) == []}
              class="pdp-variant-fallback"
            >
              One size
            </div>

            <.quantity_selector quantity={assigns[:quantity] || 1} in_stock={@product.in_stock} />

            <p
              :if={assigns[:product_discontinued]}
              class="variant-unavailable-msg"
            >
              This product is no longer available
            </p>

            <p
              :if={
                !assigns[:product_discontinued] && assigns[:selected_variant] &&
                  !assigns[:selected_variant].is_available
              }
              class="variant-unavailable-msg"
            >
              This option is currently unavailable
            </p>

            <.add_to_cart_button
              mode={@mode}
              text={
                if assigns[:product_discontinued], do: "No longer available", else: "Add to basket"
              }
              disabled={
                assigns[:product_discontinued] ||
                  (assigns[:selected_variant] && !assigns[:selected_variant].is_available)
              }
            />
          </form>
        </div>
      </div>
    <% end %>
    """
  end

  defp render_block(%{block: %{"type" => "product_details"}} = assigns) do
    ~H"""
    <.product_details :if={assigns[:product]} product={@product} />
    """
  end

  defp render_block(%{block: %{"type" => "related_products"}} = assigns) do
    ~H"""
    <.related_products_section
      :if={@theme_settings.pdp_related_products && assigns[:related_products] != []}
      products={assigns[:related_products] || []}
      theme_settings={@theme_settings}
      mode={@mode}
    />
    """
  end

  # ── Collection blocks ───────────────────────────────────────────

  defp render_block(%{block: %{"type" => "collection_header"}} = assigns) do
    count =
      if assigns[:pagination] do
        assigns.pagination.total_count
      else
        length(assigns[:products] || [])
      end

    assigns = assign(assigns, :product_count, count)

    ~H"""
    <.collection_header
      title={assigns[:collection_title] || "All Products"}
      product_count={@product_count}
    />
    """
  end

  defp render_block(%{block: %{"type" => "filter_bar"}} = assigns) do
    current_slug =
      case assigns[:current_category] do
        :sale -> "sale"
        nil -> nil
        cat -> cat.slug
      end

    assigns =
      assigns
      |> assign(:current_slug, current_slug)
      |> assign(:current_sort, assigns[:current_sort] || "featured")
      |> assign(:sort_options, assigns[:sort_options] || [])

    ~H"""
    <div class="page-container">
      <div class="filter-bar">
        <nav
          aria-label="Collection filters"
          id="collection-filters"
          phx-hook="CollectionFilters"
          class="collection-filters"
        >
          <ul class="collection-filter-pills">
            <li>
              <.link
                patch={collection_path("all", @current_sort)}
                aria-current={@current_slug == nil && "page"}
                class={["collection-filter-pill", @current_slug == nil && "active"]}
              >
                All
              </.link>
            </li>
            <li>
              <.link
                patch={collection_path("sale", @current_sort)}
                aria-current={@current_slug == "sale" && "page"}
                class={["collection-filter-pill", @current_slug == "sale" && "active"]}
              >
                Sale
              </.link>
            </li>
            <%= for category <- assigns[:categories] || [] do %>
              <li>
                <.link
                  patch={collection_path(category.slug, @current_sort)}
                  aria-current={@current_slug == category.slug && "page"}
                  class={["collection-filter-pill", @current_slug == category.slug && "active"]}
                >
                  {category.name}
                </.link>
              </li>
            <% end %>
          </ul>
        </nav>

        <form
          action={R.collection(@current_slug || "all")}
          method="get"
          phx-change="sort_changed"
        >
          <.shop_select
            name="sort"
            options={@sort_options}
            selected={@current_sort}
            aria-label="Sort products"
          />
          <noscript>
            <button type="submit" class="themed-button collection-sort-submit">Sort</button>
          </noscript>
        </form>
      </div>
    </div>
    """
  end

  defp render_block(%{block: %{"type" => "product_grid"}} = assigns) do
    show_category = assigns[:current_category] in [nil, :sale]
    collection_slug = assigns[:collection_slug] || "all"
    current_sort = assigns[:current_sort] || "featured"
    sort_params = if current_sort != "featured", do: %{"sort" => current_sort}, else: %{}

    assigns =
      assigns
      |> assign(:show_category, show_category)
      |> assign(:collection_slug, collection_slug)
      |> assign(:sort_params, sort_params)

    ~H"""
    <div class="page-container">
      <.product_grid theme_settings={@theme_settings}>
        <%= for product <- assigns[:products] || [] do %>
          <.product_card
            product={product}
            theme_settings={@theme_settings}
            mode={@mode}
            variant={:default}
            show_category={@show_category}
          />
        <% end %>
      </.product_grid>

      <.shop_pagination
        :if={assigns[:pagination] && assigns[:pagination].total_pages > 1}
        page={assigns[:pagination]}
        base_path={R.collection(@collection_slug)}
        params={@sort_params}
      />

      <%= if (assigns[:products] || []) == [] do %>
        <div class="collection-empty">
          <p>No products found in this collection.</p>
          <.link patch={R.collection("all")} class="collection-empty-link">
            View all products
          </.link>
        </div>
      <% end %>
    </div>
    """
  end

  # ── Cart blocks ─────────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "cart_items"}} = assigns) do
    ~H"""
    <.page_title text="Your basket" />
    <%= if @cart_items == [] do %>
      <.cart_empty_state mode={@mode} />
    <% else %>
      <ul role="list" aria-label="Cart items" class="cart-page-list">
        <%= for item <- @cart_items do %>
          <li>
            <.shop_card class="cart-page-card">
              <.cart_item_row item={item} size={:default} show_quantity_controls mode={@mode} />
            </.shop_card>
          </li>
        <% end %>
      </ul>
    <% end %>
    """
  end

  defp render_block(%{block: %{"type" => "order_summary"}} = assigns) do
    ~H"""
    <%= if @cart_items != [] do %>
      <.order_summary
        subtotal={assigns[:cart_page_subtotal] || 0}
        shipping_estimate={assigns[:shipping_estimate]}
        country_code={assigns[:country_code] || "GB"}
        available_countries={assigns[:available_countries] || []}
        stripe_connected={assigns[:stripe_connected] || false}
        mode={@mode}
      />
    <% end %>
    """
  end

  # ── Contact blocks ──────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "contact_form"}} = assigns) do
    settings = assigns.block["settings"] || %{}
    assigns = assign(assigns, :email, settings["email"])

    ~H"""
    <.contact_form email={@email} />
    """
  end

  defp render_block(%{block: %{"type" => "order_tracking_card"}} = assigns) do
    ~H"""
    <.order_tracking_card tracking_state={assigns[:tracking_state] || :idle} />
    """
  end

  # ── Content blocks ──────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "content_body"}} = assigns) do
    settings = assigns.block["settings"] || %{}
    content = settings["content"] || ""
    {image_src, source_width, image_alt} = resolve_content_image(settings)

    assigns =
      assigns
      |> assign(:image_src, image_src)
      |> assign(:image_source_width, source_width || 1200)
      |> assign(:image_alt, image_alt)
      |> assign(:content, content)

    ~H"""
    <div class="content-body">
      <%= if @image_src && @image_src != "" do %>
        <div class="content-image">
          <.responsive_image
            src={@image_src}
            source_width={@image_source_width}
            alt={@image_alt}
            sizes="(max-width: 800px) 100vw, 800px"
            class="content-hero-image"
          />
        </div>
      <% end %>

      <%= if @content != "" do %>
        <%!-- Inline content from block settings — split on blank lines for paragraphs --%>
        <div class="rich-text">
          <p :for={para <- String.split(@content, ~r/\n{2,}/, trim: true)} class="rich-text-paragraph">
            {para}
          </p>
        </div>
      <% else %>
        <%!-- Structured rich text from LiveView (content pages) --%>
        <.rich_text blocks={assigns[:content_blocks] || []} />
      <% end %>
    </div>
    """
  end

  # ── Checkout success ────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "checkout_result"}} = assigns) do
    ~H"""
    <%= if assigns[:order] && assigns[:order].payment_status == "paid" do %>
      <div class="checkout-header">
        <div class="checkout-icon">
          <svg
            width="32"
            height="32"
            fill="none"
            viewBox="0 0 24 24"
            stroke-width="2.5"
            stroke="currentColor"
          >
            <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
          </svg>
        </div>
        <h1 class="checkout-heading">Thank you for your order</h1>
        <p class="checkout-meta">Order <strong>{assigns[:order].order_number}</strong></p>
        <%= if assigns[:order].customer_email do %>
          <p class="checkout-meta">
            A confirmation will be sent to <strong>{assigns[:order].customer_email}</strong>
          </p>
        <% end %>
      </div>

      <.shop_card class="checkout-card">
        <h2 class="checkout-heading">Order details</h2>
        <ul class="checkout-items">
          <%= for item <- assigns[:order].items do %>
            <li class="checkout-item">
              <div>
                <p class="checkout-item-name">{item.product_name}</p>
                <%= if item.variant_title do %>
                  <p class="checkout-item-detail">{item.variant_title}</p>
                <% end %>
                <p class="checkout-item-detail">Qty: {item.quantity}</p>
              </div>
              <span class="checkout-item-price">
                {Cart.format_price(item.unit_price * item.quantity)}
              </span>
            </li>
          <% end %>
        </ul>
        <div class="checkout-total-border">
          <div class="checkout-total">
            <span class="checkout-total-label">Total</span>
            <span class="checkout-total-amount">{Cart.format_price(assigns[:order].total)}</span>
          </div>
        </div>
      </.shop_card>

      <%= if assigns[:order].shipping_address != %{} do %>
        <.shop_card class="checkout-card">
          <h2 class="checkout-heading">Shipping to</h2>
          <div class="checkout-shipping-address">
            <p>{assigns[:order].shipping_address["name"]}</p>
            <p>{assigns[:order].shipping_address["line1"]}</p>
            <%= if assigns[:order].shipping_address["line2"] do %>
              <p>{assigns[:order].shipping_address["line2"]}</p>
            <% end %>
            <p>
              {assigns[:order].shipping_address["city"]}, {assigns[:order].shipping_address[
                "postal_code"
              ]}
            </p>
            <p>{assigns[:order].shipping_address["country"]}</p>
          </div>
        </.shop_card>
      <% end %>

      <div class="checkout-actions">
        <.shop_link_button href={R.collection("all")} class="checkout-cta">
          Continue shopping
        </.shop_link_button>
      </div>
    <% else %>
      <div class="checkout-header">
        <div class="checkout-pending-icon">
          <span class="checkout-pending-spinner">
            <svg
              width="32"
              height="32"
              fill="none"
              viewBox="0 0 24 24"
              stroke-width="1.5"
              stroke="currentColor"
            >
              <path
                stroke-linecap="round"
                stroke-linejoin="round"
                d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
              />
            </svg>
          </span>
        </div>
        <h1 class="checkout-heading">Processing your payment</h1>
        <p class="checkout-pending-text">
          Please wait while we confirm your payment. This usually takes a few seconds.
        </p>
        <p class="checkout-pending-hint">
          If this page doesn't update, please <.link patch={R.contact()} class="checkout-contact-link">contact us</.link>.
        </p>
      </div>
    <% end %>
    """
  end

  # ── Orders ──────────────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "order_card"}} = assigns) do
    ~H"""
    <div class="orders-header">
      <h1 class="orders-page-title">Your orders</h1>
      <%= if assigns[:lookup_email] do %>
        <p class="orders-email-label">
          Orders for <strong>{assigns[:lookup_email]}</strong>
        </p>
      <% end %>
    </div>

    <%= cond do %>
      <% is_nil(assigns[:orders]) -> %>
        <div class="orders-empty">
          <p>This link has expired or is invalid.</p>
          <p class="orders-empty-hint">
            Head back to the <.link patch={R.contact()}>contact page</.link> to request a new one.
          </p>
        </div>
      <% assigns[:orders] == [] -> %>
        <div class="orders-empty">
          <p>No orders found for that email address.</p>
          <p class="orders-empty-hint">
            If something doesn't look right, <.link patch={R.contact()}>get in touch</.link>.
          </p>
        </div>
      <% true -> %>
        <div class="orders-list">
          <%= for order <- assigns[:orders] do %>
            <.link patch={R.order(order.order_number)} class="order-summary-card">
              <div class="order-summary-top">
                <div>
                  <p class="order-summary-number">{order.order_number}</p>
                  <p class="order-summary-date">
                    {Calendar.strftime(order.inserted_at, "%-d %B %Y")}
                  </p>
                </div>
                <span class={"order-status-badge order-status-badge-#{order.fulfilment_status}"}>
                  {format_order_status(order.fulfilment_status)}
                </span>
              </div>
              <ul class="order-summary-items">
                <%= for item <- Enum.take(order.items, 2) do %>
                  <li class="order-summary-item">
                    {item.quantity}&times; {item.product_name}
                    <%= if item.variant_title && item.variant_title != "" do %>
                      <span class="order-summary-variant">&middot; {item.variant_title}</span>
                    <% end %>
                  </li>
                <% end %>
                <%= if length(order.items) > 2 do %>
                  <li class="order-summary-more">+{length(order.items) - 2} more</li>
                <% end %>
              </ul>
              <div class="order-summary-footer">
                <span class="order-summary-total">{Cart.format_price(order.total)}</span>
                <span class="order-summary-arrow">&rarr;</span>
              </div>
            </.link>
          <% end %>
        </div>
    <% end %>
    """
  end

  # ── Order detail ────────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "order_detail_card"}} = assigns) do
    ~H"""
    <%= if assigns[:order] do %>
      <div class="order-detail-header">
        <.link patch={R.orders()} class="order-detail-back">&larr; Back to orders</.link>
        <h1 class="checkout-heading" style="margin-top: 1.5rem;">{assigns[:order].order_number}</h1>
        <p class="checkout-meta">{Calendar.strftime(assigns[:order].inserted_at, "%-d %B %Y")}</p>
        <span class={"order-status-badge order-status-badge-#{assigns[:order].fulfilment_status} order-status-badge-lg"}>
          {format_order_status(assigns[:order].fulfilment_status)}
        </span>
      </div>

      <%= if assigns[:order].tracking_number || assigns[:order].tracking_url do %>
        <.shop_card class="order-detail-tracking-card">
          <div class="order-detail-tracking">
            <svg
              width="20"
              height="20"
              fill="none"
              viewBox="0 0 24 24"
              stroke-width="1.5"
              stroke="currentColor"
            >
              <path
                stroke-linecap="round"
                stroke-linejoin="round"
                d="M8.25 18.75a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m3 0h6m-9 0H3.375a1.125 1.125 0 0 1-1.125-1.125V14.25m17.25 4.5a1.5 1.5 0 0 1-3 0m3 0a1.5 1.5 0 0 0-3 0m3 0h1.125c.621 0 1.129-.504 1.09-1.124a17.902 17.902 0 0 0-3.213-9.193 2.056 2.056 0 0 0-1.58-.86H14.25M16.5 18.75h-2.25m0-11.177v-.958c0-.568-.422-1.048-.987-1.106a48.554 48.554 0 0 0-10.026 0 1.106 1.106 0 0 0-.987 1.106v7.635m12-6.677v6.677m0 4.5v-4.5m0 0h-12"
              />
            </svg>
            <div>
              <p class="order-detail-tracking-label">Shipment tracking</p>
              <%= if assigns[:order].tracking_number do %>
                <p class="order-detail-tracking-number">{assigns[:order].tracking_number}</p>
              <% end %>
            </div>
            <%= if assigns[:order].tracking_url do %>
              <.external_link
                href={assigns[:order].tracking_url}
                class="order-detail-tracking-btn themed-button"
              >
                Track parcel
              </.external_link>
            <% end %>
          </div>
        </.shop_card>
      <% end %>

      <.shop_card class="checkout-card">
        <h2 class="checkout-heading">Items ordered</h2>
        <ul class="checkout-items">
          <%= for item <- assigns[:order].items do %>
            <% info = (assigns[:thumbnails] || %{})[item.variant_id] %>
            <li class="checkout-item">
              <%= if info && info.thumb do %>
                <img src={info.thumb} alt={item.product_name} class="checkout-item-thumb" />
              <% end %>
              <div class="checkout-item-details">
                <%= if info && info.slug do %>
                  <.link
                    patch={R.product(info.slug)}
                    class="checkout-item-name checkout-item-link"
                  >
                    {item.product_name}
                  </.link>
                <% else %>
                  <p class="checkout-item-name">{item.product_name}</p>
                <% end %>
                <%= if item.variant_title && item.variant_title != "" do %>
                  <p class="checkout-item-detail">{item.variant_title}</p>
                <% end %>
                <p class="checkout-item-detail">Qty: {item.quantity}</p>
                <%= if info && info.slug do %>
                  <%= if info[:existing_review] do %>
                    <.link
                      href={"/reviews/#{info.existing_review.id}/edit?product=#{info.slug}"}
                      class="checkout-item-review-link"
                    >
                      Edit your review
                    </.link>
                  <% else %>
                    <.link
                      patch={R.product(info.slug) <> "#reviews"}
                      class="checkout-item-review-link"
                    >
                      Write a review
                    </.link>
                  <% end %>
                <% end %>
              </div>
              <span class="checkout-item-price">
                {Cart.format_price(item.unit_price * item.quantity)}
              </span>
            </li>
          <% end %>
        </ul>
        <div class="checkout-total-border">
          <div class="checkout-total">
            <span class="checkout-total-label">Total</span>
            <span class="checkout-total-amount">{Cart.format_price(assigns[:order].total)}</span>
          </div>
        </div>
      </.shop_card>

      <%= if assigns[:order].shipping_address != %{} do %>
        <.shop_card class="checkout-card">
          <h2 class="checkout-heading">Shipping to</h2>
          <div class="checkout-shipping-address">
            <p>{assigns[:order].shipping_address["name"]}</p>
            <p>{assigns[:order].shipping_address["line1"]}</p>
            <%= if assigns[:order].shipping_address["line2"] do %>
              <p>{assigns[:order].shipping_address["line2"]}</p>
            <% end %>
            <p>
              {assigns[:order].shipping_address["city"]}, {assigns[:order].shipping_address[
                "postal_code"
              ]}
            </p>
            <p>{assigns[:order].shipping_address["country"]}</p>
          </div>
        </.shop_card>
      <% end %>

      <div class="checkout-actions">
        <.shop_link_button href={R.collection("all")}>Continue shopping</.shop_link_button>
      </div>
    <% end %>
    """
  end

  # ── Search ──────────────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "search_results"}} = assigns) do
    ~H"""
    <.page_title text="Search" />

    <form action={R.search()} method="get" phx-submit="search_submit" class="search-page-form">
      <input
        type="search"
        name="q"
        value={assigns[:search_page_query] || ""}
        placeholder="Search products..."
        class="themed-input"
      />
      <button type="submit" class="themed-button">Search</button>
    </form>

    <%= if (assigns[:search_page_results] || []) != [] do %>
      <p class="search-page-count">
        {length(assigns[:search_page_results])} {if length(assigns[:search_page_results]) == 1,
          do: "result",
          else: "results"} for "{assigns[:search_page_query]}"
      </p>
      <.product_grid theme_settings={@theme_settings}>
        <%= for product <- assigns[:search_page_results] do %>
          <.product_card
            product={product}
            theme_settings={@theme_settings}
            mode={@mode}
            variant={:default}
          />
        <% end %>
      </.product_grid>
    <% else %>
      <%= if (assigns[:search_page_query] || "") != "" do %>
        <div class="collection-empty">
          <p>No products found for &ldquo;{assigns[:search_page_query]}&rdquo;</p>
          <.link patch={R.collection("all")} class="collection-empty-link">Browse all products</.link>
        </div>
      <% end %>
    <% end %>
    """
  end

  # ── Utility blocks ──────────────────────────────────────────────

  defp render_block(%{block: %{"type" => "spacer"}} = assigns) do
    size = get_in(assigns.block, ["settings", "size"]) || "medium"
    assigns = assign(assigns, :size, size)

    ~H"""
    <div class="block-spacer" data-size={@size} aria-hidden="true"></div>
    """
  end

  defp render_block(%{block: %{"type" => "divider"}} = assigns) do
    style = get_in(assigns.block, ["settings", "style"]) || "line"
    assigns = assign(assigns, :style, style)

    ~H"""
    <hr class="block-divider" data-style={@style} />
    """
  end

  defp render_block(%{block: %{"type" => "button"}} = assigns) do
    settings = assigns.block["settings"] || %{}

    assigns =
      assigns
      |> assign(:text, settings["text"] || "Learn more")
      |> assign(:href, settings["href"] || "/")
      |> assign(:btn_style, settings["style"] || "primary")
      |> assign(:alignment, settings["alignment"] || "centre")

    ~H"""
    <div class="block-button" data-align={@alignment}>
      <.link
        patch={@href}
        class={if @btn_style == "outline", do: "themed-button-outline", else: "themed-button"}
      >
        {@text}
      </.link>
    </div>
    """
  end

  defp render_block(%{block: %{"type" => "video_embed"}} = assigns) do
    settings = assigns.block["settings"] || %{}
    url = settings["url"] || ""
    caption = settings["caption"] || ""
    aspect_ratio = settings["aspect_ratio"] || "16:9"

    {provider, embed_url} = parse_video_url(url)

    assigns =
      assigns
      |> assign(:embed_url, embed_url)
      |> assign(:provider, provider)
      |> assign(:caption, caption)
      |> assign(:aspect_ratio, aspect_ratio)
      |> assign(:raw_url, url)

    ~H"""
    <div class="page-container">
      <div :if={@provider != :unknown} class="video-embed" data-ratio={@aspect_ratio}>
        <iframe
          src={@embed_url}
          frameborder="0"
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
          allowfullscreen
          loading="lazy"
          title={if @caption != "", do: @caption, else: "Embedded video"}
        >
        </iframe>
      </div>
      <p :if={@provider == :unknown && @raw_url != ""} class="video-embed-fallback">
        <.external_link href={@raw_url}>
          {if @caption != "", do: @caption, else: "Watch video"}
        </.external_link>
      </p>
      <p :if={@caption != "" && @provider != :unknown} class="video-embed-caption">{@caption}</p>
    </div>
    """
  end

  # ── Fallback ────────────────────────────────────────────────────

  defp render_block(assigns) do
    ~H"""
    <%!-- Unknown block type: {assigns.block["type"]} --%>
    """
  end

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

  @doc "Returns a CSS class for the page's `<main>` element."
  def page_main_class("contact"), do: "page-container contact-main"
  def page_main_class("cart"), do: "page-container"
  def page_main_class("checkout_success"), do: "page-container checkout-main"
  def page_main_class("orders"), do: "page-container orders-main"
  def page_main_class("order_detail"), do: "page-container order-detail-main"
  def page_main_class("error"), do: "error-main"
  def page_main_class("collection"), do: nil
  def page_main_class("pdp"), do: "page-container"
  def page_main_class("search"), do: "page-container"
  def page_main_class("about"), do: "content-page"
  def page_main_class("delivery"), do: "content-page"
  def page_main_class("privacy"), do: "content-page"
  def page_main_class("terms"), do: "content-page"
  def page_main_class(_), do: nil

  # Extracts the assigns that individual blocks need (everything except page metadata).
  # Preserves __changed__ so assign/3 works inside render_block functions.
  defp block_assigns(assigns) do
    Map.drop(assigns, [:page, :block_assigns])
  end

  defp hero_variant("page"), do: :page
  defp hero_variant("sunken"), do: :default
  defp hero_variant("error"), do: :error
  defp hero_variant(_), do: :default

  defp hero_background("sunken"), do: :sunken
  defp hero_background(_), do: :base

  defp card_variant("minimal"), do: :minimal
  defp card_variant("compact"), do: :compact
  defp card_variant("default"), do: :default
  defp card_variant(_), do: :featured

  defp grid_columns("fixed-4"), do: :fixed_4
  defp grid_columns(_), do: nil

  defp breadcrumb_items(%{category: cat, title: title}) when not is_nil(cat) do
    slug = cat |> String.downcase() |> String.replace(" ", "-")

    [
      %{label: cat, page: "collection", href: R.collection(slug)},
      %{label: title, current: true}
    ]
  end

  defp breadcrumb_items(%{title: title}) do
    [%{label: title, current: true}]
  end

  defp breadcrumb_items(_), do: []

  defp collection_path(slug, "featured"), do: R.collection(slug)
  defp collection_path(slug, sort), do: R.collection(slug) <> "?sort=#{sort}"

  # Resolves an image_id to a full URL for blocks that need a single URL (e.g. background-image).
  defp resolve_block_image_url(image_id) do
    case resolve_image(image_id) do
      nil ->
        ""

      image ->
        if image.is_svg do
          "/image_cache/#{image.id}.webp"
        else
          width =
            image.source_width
            |> Berrypod.Images.Optimizer.applicable_widths()
            |> List.last()

          "/image_cache/#{image.id}-#{width || 400}.webp"
        end
    end
  end

  # Resolves image_id for content_body blocks, returning {base_path, source_width, alt}.
  # base_path has no width/extension suffix — responsive_image adds those.
  defp resolve_content_image(settings) do
    case resolve_image(settings["image_id"]) do
      nil ->
        {nil, nil, ""}

      image ->
        {"/image_cache/#{image.id}", image.source_width, image.alt || image.filename}
    end
  end

  defp resolve_image(nil), do: nil
  defp resolve_image(""), do: nil
  defp resolve_image(image_id), do: Berrypod.Media.get_image(image_id)

  @youtube_re ~r/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/
  @vimeo_re ~r/vimeo\.com\/(?:video\/)?(\d+)/

  defp parse_video_url(url) when is_binary(url) do
    cond do
      match = Regex.run(@youtube_re, url) ->
        {:youtube, "https://www.youtube-nocookie.com/embed/#{Enum.at(match, 1)}"}

      match = Regex.run(@vimeo_re, url) ->
        {:vimeo, "https://player.vimeo.com/video/#{Enum.at(match, 1)}?dnt=1"}

      true ->
        {:unknown, nil}
    end
  end

  defp parse_video_url(_), do: {:unknown, nil}

  def format_order_status("unfulfilled"), do: "Being prepared"
  def format_order_status("submitted"), do: "Sent to printer"
  def format_order_status("processing"), do: "In production"
  def format_order_status("shipped"), do: "On its way"
  def format_order_status("delivered"), do: "Delivered"
  def format_order_status("failed"), do: "Issue — contact us"
  def format_order_status("cancelled"), do: "Cancelled"
  def format_order_status(status), do: status
end
