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

  alias Berrypod.Products
  alias Berrypod.Products.{Product, ProductImage}
  alias Berrypod.Cart

  @impl true
  def mount(_params, _session, socket) do
    connections = Products.list_provider_connections()
    categories = Products.list_all_categories()

    socket =
      socket
      |> assign(:page_title, "Products")
      |> assign(:connections, connections)
      |> assign(:categories, categories)
      |> assign(:provider_filter, "all")
      |> assign(:category_filter, "all")
      |> assign(:visibility_filter, "all")
      |> assign(:stock_filter, "all")
      |> assign(:sort, "newest")

    {:ok, socket}
  end

  @impl true
  def handle_params(params, _uri, socket) do
    page_num = Berrypod.Pagination.parse_page(params)

    opts =
      build_filter_opts(
        socket.assigns.provider_filter,
        socket.assigns.category_filter,
        socket.assigns.visibility_filter,
        socket.assigns.stock_filter,
        socket.assigns.sort
      )

    page = Products.list_products_admin_paginated([page: page_num] ++ opts)

    socket =
      socket
      |> assign(:pagination, page)
      |> assign(:product_count, page.total_count)
      |> stream(:products, page.items, reset: true)

    {:noreply, socket}
  end

  @impl true
  def handle_event("filter", params, socket) do
    socket =
      socket
      |> assign(:provider_filter, params["provider"] || socket.assigns.provider_filter)
      |> assign(:category_filter, params["category"] || socket.assigns.category_filter)
      |> assign(:visibility_filter, params["visibility"] || socket.assigns.visibility_filter)
      |> assign(:stock_filter, params["stock"] || socket.assigns.stock_filter)
      |> assign(:sort, params["sort"] || socket.assigns.sort)

    {:noreply, push_patch(socket, to: ~p"/admin/products")}
  end

  @impl true
  def handle_event("toggle_visibility", %{"id" => id}, socket) do
    product =
      Products.get_product_with_preloads(id)

    case Products.toggle_visibility(product) do
      {:ok, updated} ->
        updated = %{
          updated
          | provider_connection: product.provider_connection,
            images: product.images,
            variants: product.variants
        }

        {:noreply, stream_insert(socket, :products, updated)}

      {:error, _changeset} ->
        {:noreply, put_flash(socket, :error, "Could not update visibility")}
    end
  end

  @impl true
  def render(assigns) do
    ~H"""
    <.header>
      Products
      <:subtitle>{@product_count} products</:subtitle>
    </.header>

    <form phx-change="filter" class="admin-filter-row-end">
      <.filter_select
        :if={length(@connections) > 1}
        name="provider"
        label="Provider"
        value={@provider_filter}
        options={[{"All providers", "all"}] ++ Enum.map(@connections, &{&1.name, &1.id})}
      />
      <.filter_select
        :if={@categories != []}
        name="category"
        label="Category"
        value={@category_filter}
        options={[{"All categories", "all"}] ++ Enum.map(@categories, &{&1, &1})}
      />
      <.filter_select
        name="visibility"
        label="Visibility"
        value={@visibility_filter}
        options={[{"All", "all"}, {"Visible", "visible"}, {"Hidden", "hidden"}]}
      />
      <.filter_select
        name="stock"
        label="Stock"
        value={@stock_filter}
        options={[{"All", "all"}, {"In stock", "in_stock"}, {"Out of stock", "out_of_stock"}]}
      />
      <.filter_select
        name="sort"
        label="Sort"
        value={@sort}
        options={[
          {"Newest", "newest"},
          {"Name A\u2013Z", "name_asc"},
          {"Name Z\u2013A", "name_desc"},
          {"Price: low\u2013high", "price_asc"},
          {"Price: high\u2013low", "price_desc"}
        ]}
      />
    </form>

    <.table
      :if={@product_count > 0}
      id="products"
      rows={@streams.products}
      row_item={fn {_id, product} -> product end}
      row_click={fn {_id, product} -> JS.navigate(~p"/admin/products/#{product}") end}
    >
      <:col :let={product} label="">
        <.product_thumbnail product={product} />
      </:col>
      <:col :let={product} label="Product">
        <div class="admin-product-name">
          <.link navigate={~p"/admin/products/#{product}"} class="admin-link">
            {product.title}
          </.link>
        </div>
        <.provider_badge :if={product.provider_connection} connection={product.provider_connection} />
      </:col>
      <:col :let={product} label="Category">
        {product.category || "—"}
      </:col>
      <:col :let={product} label="Price">
        <span :if={product.on_sale} class="admin-sale-tag">Sale</span>
        {Cart.format_price(product.cheapest_price)}
      </:col>
      <:col :let={product} label="Stock">
        <.stock_badge in_stock={product.in_stock} />
      </:col>
      <:col :let={product} label="Variants">
        {length(product.variants)}
      </:col>
      <:col :let={product} label="Visible">
        <button
          phx-click="toggle_visibility"
          phx-value-id={product.id}
          aria-pressed={to_string(product.visible)}
          aria-label={"Toggle visibility for #{product.title}"}
          class={[
            "admin-btn admin-btn-ghost admin-btn-sm",
            if(product.visible, do: "admin-icon-positive", else: "admin-icon-muted")
          ]}
        >
          <.icon :if={product.visible} name="hero-eye" class="size-5" />
          <.icon :if={!product.visible} name="hero-eye-slash" class="size-5" />
        </button>
      </:col>
    </.table>

    <.admin_pagination :if={@product_count > 0} page={@pagination} patch={~p"/admin/products"} />

    <div :if={@product_count == 0} class="admin-empty-state">
      <.icon name="hero-cube" class="admin-empty-state-icon" />
      <p class="admin-empty-state-title">No products yet</p>
      <p :if={@connections == []} class="admin-empty-state-text">
        <.link navigate={~p"/admin/providers"} class="admin-link">
          Connect a provider
        </.link>
        to sync your products.
      </p>
      <p :if={@connections != []} class="admin-empty-state-text">
        Head to the
        <.link navigate={~p"/admin/providers"} class="admin-link">
          providers page
        </.link>
        to sync your products.
      </p>
    </div>
    """
  end

  # ---------------------------------------------------------------------------
  # Helper components
  # ---------------------------------------------------------------------------

  defp filter_select(assigns) do
    ~H"""
    <label class="admin-filter-select">
      <span class="admin-filter-label">{@label}</span>
      <select name={@name} class="admin-select admin-select-sm" aria-label={@label}>
        <option :for={{label, value} <- @options} value={value} selected={value == @value}>
          {label}
        </option>
      </select>
    </label>
    """
  end

  defp product_thumbnail(assigns) do
    image = Product.primary_image(assigns.product)

    url =
      if image do
        ProductImage.thumbnail_url(image)
      end

    alt = (image && image.alt) || assigns.product.title

    assigns = assign(assigns, url: url, alt: alt)

    ~H"""
    <div class="admin-thumbnail">
      <img :if={@url} src={@url} alt={@alt} loading="lazy" />
      <div
        :if={!@url}
        class="admin-thumbnail-placeholder admin-icon-muted"
      >
        <.icon name="hero-photo" class="size-5" />
      </div>
    </div>
    """
  end

  defp provider_badge(assigns) do
    label =
      case assigns.connection.provider_type do
        "printify" -> "Printify"
        "printful" -> "Printful"
        other -> String.capitalize(other)
      end

    assigns = assign(assigns, :label, label)

    ~H"""
    <span class="admin-provider-badge">{@label}</span>
    """
  end

  defp stock_badge(assigns) do
    {color, label} =
      if assigns.in_stock do
        {"green", "In stock"}
      else
        {"red", "Out of stock"}
      end

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

    ~H"""
    <span class={["admin-status-pill", "admin-status-pill-#{@color}"]}>{@label}</span>
    """
  end

  # ---------------------------------------------------------------------------
  # Filter helpers
  # ---------------------------------------------------------------------------

  defp build_filter_opts(provider, category, visibility, stock, sort) do
    []
    |> maybe_add_filter(:provider_connection_id, provider)
    |> maybe_add_filter(:category, category)
    |> maybe_add_visibility(visibility)
    |> maybe_add_stock(stock)
    |> Keyword.put(:sort, sort)
  end

  defp maybe_add_filter(opts, _key, "all"), do: opts
  defp maybe_add_filter(opts, key, value), do: Keyword.put(opts, key, value)

  defp maybe_add_visibility(opts, "visible"), do: Keyword.put(opts, :visible, true)
  defp maybe_add_visibility(opts, "hidden"), do: Keyword.put(opts, :visible, false)
  defp maybe_add_visibility(opts, _), do: opts

  defp maybe_add_stock(opts, "in_stock"), do: Keyword.put(opts, :in_stock, true)
  defp maybe_add_stock(opts, "out_of_stock"), do: Keyword.put(opts, :in_stock, false)
  defp maybe_add_stock(opts, _), do: opts
end
