defmodule SimpleshopThemeWeb.Shop.ProductShow do
  use SimpleshopThemeWeb, :live_view

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

  @impl true
  def mount(%{"id" => slug}, _session, socket) do
    case Products.get_visible_product(slug) do
      nil ->
        {:ok, push_navigate(socket, to: ~p"/collections/all")}

      product ->
        related_products =
          Products.list_visible_products(
            category: product.category,
            limit: 4,
            exclude: product.id
          )

        gallery_images =
          (product.images || [])
          |> Enum.sort_by(& &1.position)
          |> Enum.map(fn img -> ProductImage.direct_url(img, 1200) end)
          |> Enum.reject(&is_nil/1)

        option_types = Product.option_types(product)
        variants = product.variants || []
        {selected_options, selected_variant} = initialize_variant_selection(variants)
        available_options = compute_available_options(option_types, variants, selected_options)
        display_price = variant_price(selected_variant, product)

        socket =
          socket
          |> assign(:page_title, product.title)
          |> assign(:product, product)
          |> assign(:gallery_images, gallery_images)
          |> assign(:related_products, related_products)
          |> assign(:quantity, 1)
          |> assign(:option_types, option_types)
          |> assign(:variants, variants)
          |> assign(:selected_options, selected_options)
          |> assign(:selected_variant, selected_variant)
          |> assign(:available_options, available_options)
          |> assign(:display_price, display_price)

        {:ok, socket}
    end
  end

  defp initialize_variant_selection([first | _] = _variants) do
    {first.options, first}
  end

  defp initialize_variant_selection([]) do
    {%{}, nil}
  end

  defp compute_available_options(option_types, variants, selected_options) do
    Enum.reduce(option_types, %{}, fn opt_type, acc ->
      other_options = Map.delete(selected_options, opt_type.name)

      available_values =
        variants
        |> Enum.filter(fn v ->
          v.is_available &&
            Enum.all?(other_options, fn {k, selected_val} ->
              v.options[k] == selected_val
            end)
        end)
        |> Enum.map(fn v -> v.options[opt_type.name] end)
        |> Enum.uniq()

      Map.put(acc, opt_type.name, available_values)
    end)
  end

  defp variant_price(%{price: price}, _product) when is_integer(price), do: price
  defp variant_price(_, %{cheapest_price: price}), do: price
  defp variant_price(_, _), do: 0

  defp find_variant(variants, selected_options) do
    Enum.find(variants, fn v -> v.options == selected_options end)
  end

  @impl true
  def handle_event("select_option", %{"option" => option_name, "value" => value}, socket) do
    selected_options = Map.put(socket.assigns.selected_options, option_name, value)

    selected_variant = find_variant(socket.assigns.variants, selected_options)

    available_options =
      compute_available_options(
        socket.assigns.option_types,
        socket.assigns.variants,
        selected_options
      )

    socket =
      socket
      |> assign(:selected_options, selected_options)
      |> assign(:selected_variant, selected_variant)
      |> assign(:available_options, available_options)
      |> assign(:display_price, variant_price(selected_variant, socket.assigns.product))

    {:noreply, socket}
  end

  @impl true
  def handle_event("increment_quantity", _params, socket) do
    quantity = min(socket.assigns.quantity + 1, 99)
    {:noreply, assign(socket, :quantity, quantity)}
  end

  @impl true
  def handle_event("decrement_quantity", _params, socket) do
    quantity = max(socket.assigns.quantity - 1, 1)
    {:noreply, assign(socket, :quantity, quantity)}
  end

  @impl true
  def handle_event("add_to_cart", _params, socket) do
    variant = socket.assigns.selected_variant

    if variant do
      cart = Cart.add_item(socket.assigns.raw_cart, variant.id, socket.assigns.quantity)

      socket =
        socket
        |> SimpleshopThemeWeb.CartHook.broadcast_and_update(cart)
        |> assign(:quantity, 1)
        |> assign(:cart_drawer_open, true)
        |> assign(:cart_status, "#{socket.assigns.product.title} added to cart")

      {:noreply, socket}
    else
      {:noreply, socket}
    end
  end

  @impl true
  def render(assigns) do
    ~H"""
    <SimpleshopThemeWeb.PageTemplates.pdp {assigns} />
    """
  end
end
