defmodule BerrypodWeb.BlockEditorComponents do
  @moduledoc """
  Shared UI components for the block editor.

  Used by both the admin page editor (`Admin.Pages.Editor`) and the
  live page editor sidebar (`PageEditorHook` + `PageRenderer`).

  All components accept an `event_prefix` attr to namespace phx-click
  events. The admin editor uses `""` (default), the live sidebar uses
  `"editor_"`.
  """

  use Phoenix.Component

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

  alias Berrypod.Pages.{BlockEditor, BlockTypes}
  alias BerrypodWeb.BlockThumbnails

  # ── Block card ─────────────────────────────────────────────────

  attr :block, :map, required: true
  attr :idx, :integer, required: true
  attr :total, :integer, required: true
  attr :expanded, :any, required: true
  attr :event_prefix, :string, default: ""

  def block_card(assigns) do
    block_type = BlockTypes.get(assigns.block["type"])
    has_settings = BlockEditor.has_settings?(assigns.block)
    expanded = MapSet.member?(assigns.expanded, assigns.block["id"])
    preview = block_preview(assigns.block, block_type)

    assigns =
      assigns
      |> assign(:block_type, block_type)
      |> assign(:has_settings, has_settings)
      |> assign(:is_expanded, expanded)
      |> assign(:preview, preview)

    ~H"""
    <div
      class={["block-card", @is_expanded && "block-card-expanded"]}
      role="listitem"
      aria-label={"#{@block_type && @block_type.name || @block["type"]}, position #{@idx + 1} of #{@total}"}
      id={"block-#{@block["id"]}"}
    >
      <div class="block-card-header">
        <span class="block-card-position">{@idx + 1}</span>

        <span class="block-card-icon">
          <.icon name={(@block_type && @block_type.icon) || "hero-puzzle-piece"} class="size-5" />
        </span>

        <div class="block-card-info">
          <span class="block-card-name">
            {(@block_type && @block_type.name) || @block["type"]}
          </span>
          <span :if={@preview} class="block-card-preview">{@preview}</span>
        </div>

        <span class="block-card-controls">
          <button
            :if={@has_settings}
            phx-click={"#{@event_prefix}toggle_expand"}
            phx-value-id={@block["id"]}
            class={[
              "admin-btn admin-btn-ghost admin-btn-icon admin-btn-sm",
              @is_expanded && "block-edit-btn-active"
            ]}
            aria-label={"Edit #{@block_type && @block_type.name} settings"}
            aria-expanded={to_string(@is_expanded)}
            aria-controls={"block-settings-#{@block["id"]}"}
            id={"block-edit-btn-#{@block["id"]}"}
          >
            <.icon name="hero-cog-6-tooth-mini" class="size-4" />
          </button>
          <button
            phx-click={"#{@event_prefix}move_up"}
            phx-value-id={@block["id"]}
            class="admin-btn admin-btn-ghost admin-btn-icon admin-btn-sm"
            aria-label={"Move #{@block_type && @block_type.name} up"}
            disabled={@idx == 0}
          >
            <.icon name="hero-chevron-up-mini" class="size-4" />
          </button>
          <button
            phx-click={"#{@event_prefix}move_down"}
            phx-value-id={@block["id"]}
            class="admin-btn admin-btn-ghost admin-btn-icon admin-btn-sm"
            aria-label={"Move #{@block_type && @block_type.name} down"}
            disabled={@idx == @total - 1}
          >
            <.icon name="hero-chevron-down-mini" class="size-4" />
          </button>
          <button
            phx-click={"#{@event_prefix}duplicate_block"}
            phx-value-id={@block["id"]}
            class="admin-btn admin-btn-ghost admin-btn-icon admin-btn-sm"
            aria-label={"Duplicate #{@block_type && @block_type.name}"}
          >
            <.icon name="hero-document-duplicate-mini" class="size-4" />
          </button>
          <button
            phx-click={"#{@event_prefix}remove_block"}
            phx-value-id={@block["id"]}
            class="admin-btn admin-btn-ghost admin-btn-icon admin-btn-sm block-remove-btn"
            aria-label={"Remove #{@block_type && @block_type.name}"}
            data-confirm={"Remove #{@block_type && @block_type.name}?"}
          >
            <.icon name="hero-trash-mini" class="size-4" />
          </button>
        </span>
      </div>

      <.block_settings_form
        :if={@is_expanded}
        block={@block}
        schema={@block_type.settings_schema}
        hint={@block_type[:hint]}
        event_prefix={@event_prefix}
      />
    </div>
    """
  end

  # ── Settings form ──────────────────────────────────────────────

  attr :block, :map, required: true
  attr :schema, :list, required: true
  attr :hint, :string, default: nil
  attr :event_prefix, :string, default: ""

  def block_settings_form(assigns) do
    settings = BlockEditor.settings_with_defaults(assigns.block)
    assigns = assign(assigns, :settings, settings)

    ~H"""
    <div class="block-card-settings" id={"block-settings-#{@block["id"]}"}>
      <p :if={@hint} class="block-settings-hint">
        <.icon name="hero-information-circle-mini" class="size-4" /> {@hint}
      </p>
      <form phx-change={"#{@event_prefix}update_block_settings"}>
        <input type="hidden" name="block_id" value={@block["id"]} />

        <div class="block-settings-fields">
          <.block_field
            :for={field <- @schema}
            field={field}
            value={@settings[field.key]}
            block_id={@block["id"]}
            event_prefix={@event_prefix}
          />
        </div>
      </form>
    </div>
    """
  end

  # ── Field renderers ────────────────────────────────────────────

  attr :field, :any, required: true
  attr :value, :any, required: true
  attr :block_id, :string, required: true
  attr :event_prefix, :string, default: ""

  def block_field(%{field: %{type: :select}} = assigns) do
    ~H"""
    <div class="admin-fieldset">
      <label>
        <span class="admin-label">{@field.label}</span>
        <select
          name={"block_settings[#{@field.key}]"}
          id={"block-#{@block_id}-#{@field.key}"}
          class="admin-select"
          phx-debounce="blur"
        >
          {Phoenix.HTML.Form.options_for_select(@field.options, to_string(@value))}
        </select>
      </label>
    </div>
    """
  end

  def block_field(%{field: %{type: :textarea}} = assigns) do
    ~H"""
    <div class="admin-fieldset">
      <label>
        <span class="admin-label">{@field.label}</span>
        <textarea
          name={"block_settings[#{@field.key}]"}
          id={"block-#{@block_id}-#{@field.key}"}
          class="admin-textarea"
          rows="3"
          phx-debounce="300"
        >{@value}</textarea>
      </label>
    </div>
    """
  end

  def block_field(%{field: %{type: :number}} = assigns) do
    ~H"""
    <div class="admin-fieldset">
      <label>
        <span class="admin-label">{@field.label}</span>
        <input
          type="number"
          name={"block_settings[#{@field.key}]"}
          id={"block-#{@block_id}-#{@field.key}"}
          value={@value}
          class="admin-input"
          phx-debounce="blur"
        />
      </label>
    </div>
    """
  end

  def block_field(%{field: %{type: :image}} = assigns) do
    image_id = assigns.value
    image = if is_binary(image_id) && image_id != "", do: Berrypod.Media.get_image(image_id)
    assigns = assign(assigns, :image, image)

    ~H"""
    <div class="admin-fieldset">
      <span class="admin-label">{@field.label}</span>
      <div class="image-field">
        <%= if @image do %>
          <div class="image-field-preview">
            <%= if @image.is_svg do %>
              <div class="image-field-svg">
                <.icon name="hero-code-bracket" class="size-6" />
              </div>
            <% else %>
              <img
                src={"/image_cache/#{@image.id}-thumb.jpg"}
                alt={@image.alt || @image.filename}
                class="image-field-thumb"
              />
            <% end %>
            <div class="image-field-info">
              <span class="image-field-filename">{@image.filename}</span>
              <span :if={@image.alt} class="image-field-alt">{@image.alt}</span>
            </div>
          </div>
          <div class="image-field-actions">
            <button
              type="button"
              phx-click={"#{@event_prefix}show_image_picker"}
              phx-value-block-id={@block_id}
              phx-value-field={@field.key}
              class="admin-btn admin-btn-outline admin-btn-xs"
            >
              Change
            </button>
            <button
              type="button"
              phx-click={"#{@event_prefix}clear_image"}
              phx-value-block-id={@block_id}
              phx-value-field={@field.key}
              class="admin-btn admin-btn-ghost admin-btn-xs image-field-remove-btn"
            >
              Remove
            </button>
          </div>
        <% else %>
          <button
            type="button"
            phx-click={"#{@event_prefix}show_image_picker"}
            phx-value-block-id={@block_id}
            phx-value-field={@field.key}
            class="admin-btn admin-btn-outline admin-btn-sm image-field-choose-btn"
          >
            <.icon name="hero-photo" class="size-4" /> Choose image
          </button>
        <% end %>
      </div>
    </div>
    """
  end

  def block_field(%{field: %{type: :repeater}} = assigns) do
    items = if is_list(assigns.value), do: assigns.value, else: []
    item_count = length(items)

    assigns =
      assigns
      |> assign(:items, items)
      |> assign(:item_count, item_count)

    ~H"""
    <div class="repeater-field">
      <span class="admin-label">{@field.label}</span>

      <ol class="repeater-items" aria-label={@field.label}>
        <li :for={{item, idx} <- Enum.with_index(@items)} class="repeater-item">
          <fieldset>
            <legend class="sr-only">
              Item {idx + 1}: {item["label"] || item[:label] || "New item"}
            </legend>

            <div class="repeater-item-fields">
              <.repeater_item_field
                :for={sub_field <- @field.item_schema}
                sub_field={sub_field}
                item={item}
                field_key={@field.key}
                block_id={@block_id}
                index={idx}
                event_prefix={@event_prefix}
              />
            </div>

            <div class="repeater-item-controls">
              <button
                type="button"
                phx-click={"#{@event_prefix}repeater_move"}
                phx-value-block-id={@block_id}
                phx-value-field={@field.key}
                phx-value-index={idx}
                phx-value-dir="up"
                class="admin-btn admin-btn-ghost admin-btn-icon admin-btn-xs"
                aria-label={"Move item #{idx + 1} up"}
                disabled={idx == 0}
              >
                <.icon name="hero-chevron-up-mini" class="size-4" />
              </button>
              <button
                type="button"
                phx-click={"#{@event_prefix}repeater_move"}
                phx-value-block-id={@block_id}
                phx-value-field={@field.key}
                phx-value-index={idx}
                phx-value-dir="down"
                class="admin-btn admin-btn-ghost admin-btn-icon admin-btn-xs"
                aria-label={"Move item #{idx + 1} down"}
                disabled={idx == @item_count - 1}
              >
                <.icon name="hero-chevron-down-mini" class="size-4" />
              </button>
              <button
                type="button"
                phx-click={"#{@event_prefix}repeater_remove"}
                phx-value-block-id={@block_id}
                phx-value-field={@field.key}
                phx-value-index={idx}
                class="admin-btn admin-btn-ghost admin-btn-icon admin-btn-xs repeater-remove-btn"
                aria-label={"Remove item #{idx + 1}"}
              >
                <.icon name="hero-x-mark-mini" class="size-4" />
              </button>
            </div>
          </fieldset>
        </li>
      </ol>

      <button
        type="button"
        phx-click={"#{@event_prefix}repeater_add"}
        phx-value-block-id={@block_id}
        phx-value-field={@field.key}
        class="admin-btn admin-btn-outline admin-btn-xs repeater-add-btn"
      >
        <.icon name="hero-plus-mini" class="size-4" /> Add item
      </button>
    </div>
    """
  end

  def block_field(assigns) do
    ~H"""
    <div class="admin-fieldset">
      <label>
        <span class="admin-label">{@field.label}</span>
        <input
          type="text"
          name={"block_settings[#{@field.key}]"}
          id={"block-#{@block_id}-#{@field.key}"}
          value={@value}
          class="admin-input"
          phx-debounce="300"
        />
      </label>
    </div>
    """
  end

  # ── Repeater item field ────────────────────────────────────────

  attr :sub_field, :any, required: true
  attr :item, :map, required: true
  attr :field_key, :string, required: true
  attr :block_id, :string, required: true
  attr :index, :integer, required: true
  attr :event_prefix, :string, default: ""

  def repeater_item_field(assigns) do
    value = assigns.item[assigns.sub_field.key] || ""
    assigns = assign(assigns, :value, value)

    ~H"""
    <label class="repeater-sub-field">
      <span class="sr-only">{@sub_field.label}</span>
      <input
        type="text"
        name={"block_settings[#{@field_key}][#{@index}][#{@sub_field.key}]"}
        id={"block-#{@block_id}-#{@field_key}-#{@index}-#{@sub_field.key}"}
        value={@value}
        placeholder={@sub_field.label}
        class="admin-input admin-input-sm"
        phx-debounce="300"
      />
    </label>
    """
  end

  # ── Block picker ───────────────────────────────────────────────

  attr :allowed_blocks, :any, required: true
  attr :filter, :string, required: true
  attr :event_prefix, :string, default: ""

  def block_picker(assigns) do
    filter = String.downcase(assigns.filter)

    filtered =
      assigns.allowed_blocks
      |> Enum.filter(fn {_type, def} ->
        filter == "" or String.contains?(String.downcase(def.name), filter) or
          String.contains?(String.downcase(Map.get(def, :description, "")), filter)
      end)
      |> Enum.sort_by(fn {_type, def} -> def.name end)

    assigns = assign(assigns, :filtered_blocks, filtered)

    ~H"""
    <div class="block-picker-overlay">
      <div class="block-picker" phx-click-away={"#{@event_prefix}hide_picker"}>
        <div class="block-picker-header">
          <h3>Add a block</h3>
          <button
            phx-click={"#{@event_prefix}hide_picker"}
            class="admin-btn admin-btn-ghost admin-btn-icon admin-btn-sm"
            aria-label="Close"
          >
            <.icon name="hero-x-mark" class="size-5" />
          </button>
        </div>

        <input
          type="text"
          placeholder="Filter blocks..."
          value={@filter}
          phx-keyup={"#{@event_prefix}filter_picker"}
          phx-key=""
          class="admin-input block-picker-search"
          autofocus
        />

        <div class="block-picker-grid">
          <button
            :for={{type, def} <- @filtered_blocks}
            phx-click={"#{@event_prefix}add_block"}
            phx-value-type={type}
            class="block-picker-item"
          >
            <BlockThumbnails.block_thumbnail type={type} />
            <.icon name={def.icon} class="size-5" />
            <span class="block-picker-item-name">{def.name}</span>
            <span :if={def[:description]} class="block-picker-item-desc">
              {def.description}
            </span>
          </button>

          <p :if={@filtered_blocks == []} class="block-picker-empty">
            No matching blocks.
          </p>
        </div>
      </div>
    </div>
    """
  end

  # ── Image picker ──────────────────────────────────────────────

  attr :images, :list, required: true
  attr :search, :string, required: true
  attr :event_prefix, :string, default: ""
  attr :upload, :any, default: nil

  def image_picker(assigns) do
    search = String.downcase(assigns.search)

    filtered =
      if search == "" do
        assigns.images
      else
        Enum.filter(assigns.images, fn img ->
          String.contains?(String.downcase(img.filename || ""), search) or
            String.contains?(String.downcase(img.alt || ""), search)
        end)
      end

    assigns = assign(assigns, :filtered_images, filtered)

    ~H"""
    <div class="image-picker-overlay">
      <div class="image-picker" phx-click-away={"#{@event_prefix}hide_image_picker"}>
        <div class="image-picker-header">
          <h3>Choose image</h3>
          <button
            phx-click={"#{@event_prefix}hide_image_picker"}
            class="admin-btn admin-btn-ghost admin-btn-icon admin-btn-sm"
            aria-label="Close"
          >
            <.icon name="hero-x-mark" class="size-5" />
          </button>
        </div>

        <input
          type="text"
          placeholder="Search images..."
          value={@search}
          phx-keyup={"#{@event_prefix}image_picker_search"}
          phx-key=""
          class="admin-input image-picker-search"
          autofocus
        />

        <div :if={@upload} class="image-picker-upload">
          <form phx-change="noop" phx-submit="noop">
            <label class="image-picker-upload-label">
              <.icon name="hero-arrow-up-tray" class="size-4" />
              <span>Upload new image</span>
              <.live_file_input upload={@upload} class="hidden" />
            </label>
          </form>
        </div>

        <div class="image-picker-grid">
          <button
            :for={image <- @filtered_images}
            type="button"
            phx-click={"#{@event_prefix}pick_image"}
            phx-value-image-id={image.id}
            class="image-picker-item"
          >
            <%= if image.is_svg do %>
              <div class="image-picker-item-svg">
                <.icon name="hero-code-bracket" class="size-6" />
              </div>
            <% else %>
              <img
                src={"/image_cache/#{image.id}-thumb.jpg"}
                alt={image.alt || image.filename}
                class="image-picker-item-thumb"
                loading="lazy"
              />
            <% end %>
            <span class="image-picker-item-name">{image.filename}</span>
            <span
              :if={!image.alt || image.alt == ""}
              class="image-picker-item-no-alt"
              title="Missing alt text"
            >
              <.icon name="hero-exclamation-triangle-mini" class="size-3" />
            </span>
          </button>

          <p :if={@filtered_images == []} class="image-picker-empty">
            No images found.
          </p>
        </div>
      </div>
    </div>
    """
  end

  # ── Block preview ──────────────────────────────────────────────

  # Extracts a short one-line preview from block settings for display in the card
  defp block_preview(_block, nil), do: nil

  defp block_preview(block, block_type) do
    settings = block["settings"] || %{}
    schema = block_type.settings_schema

    # Try text/textarea fields first, then selects, then repeaters
    find_text_preview(settings, schema) ||
      find_select_preview(settings, schema) ||
      find_repeater_preview(settings, schema)
  end

  defp find_text_preview(settings, schema) do
    schema
    |> Enum.filter(&(&1.type in [:text, :textarea]))
    |> Enum.find_value(fn field ->
      case settings[field.key] do
        val when is_binary(val) and val != "" ->
          truncate(val, 60)

        _ ->
          nil
      end
    end)
  end

  defp find_select_preview(settings, schema) do
    schema
    |> Enum.filter(&(&1.type == :select))
    |> Enum.find_value(fn field ->
      case settings[field.key] do
        val when is_binary(val) and val != "" -> "#{field.label}: #{val}"
        _ -> nil
      end
    end)
  end

  defp find_repeater_preview(settings, schema) do
    schema
    |> Enum.filter(&(&1.type == :repeater))
    |> Enum.find_value(fn field ->
      case settings[field.key] do
        items when is_list(items) and items != [] ->
          count = length(items)
          "#{count} #{if count == 1, do: "item", else: "items"}"

        _ ->
          nil
      end
    end)
  end

  defp truncate(str, max) do
    str = String.replace(str, ~r/\s+/, " ") |> String.trim()

    if String.length(str) > max do
      String.slice(str, 0, max) <> "..."
    else
      str
    end
  end
end
