defmodule SimpleshopThemeWeb.CartHook do
  @moduledoc """
  LiveView on_mount hook for cart state and shared event handling.

  Mounted in the public_shop live_session to give all shop LiveViews
  cart state, PubSub sync, and shared event handlers via attach_hook.

  Handles these events so individual LiveViews don't have to:
  - `open_cart_drawer` / `close_cart_drawer` - toggle drawer visibility
  - `remove_item` - remove item from cart
  - `{:cart_updated, cart}` info - cross-tab cart sync via PubSub

  LiveViews with custom cart logic (add_to_cart, increment, decrement)
  can call `update_cart_assigns/2` and `broadcast_and_update/2` directly.
  """

  import Phoenix.Component, only: [assign: 3]
  import Phoenix.LiveView, only: [attach_hook: 4, connected?: 1, push_event: 3]

  alias SimpleshopTheme.Cart

  def on_mount(:mount_cart, _params, session, socket) do
    cart_items = Cart.get_from_session(session)

    socket =
      socket
      |> update_cart_assigns(cart_items)
      |> assign(:cart_drawer_open, false)
      |> assign(:cart_status, nil)
      |> attach_hook(:cart_events, :handle_event, &handle_cart_event/3)
      |> attach_hook(:cart_info, :handle_info, &handle_cart_info/2)

    socket =
      if connected?(socket) do
        csrf_token = Map.get(session, "_csrf_token", "default")
        topic = "cart:#{csrf_token}"
        Phoenix.PubSub.subscribe(SimpleshopTheme.PubSub, topic)
        assign(socket, :cart_topic, topic)
      else
        assign(socket, :cart_topic, nil)
      end

    {:cont, socket}
  end

  # Shared event handlers

  defp handle_cart_event("open_cart_drawer", _params, socket) do
    {:halt, assign(socket, :cart_drawer_open, true)}
  end

  defp handle_cart_event("close_cart_drawer", _params, socket) do
    {:halt, assign(socket, :cart_drawer_open, false)}
  end

  defp handle_cart_event("remove_item", %{"id" => variant_id}, socket) do
    cart = Cart.remove_item(socket.assigns.raw_cart, variant_id)

    socket =
      socket
      |> broadcast_and_update(cart)
      |> assign(:cart_status, "Item removed from cart")

    {:halt, socket}
  end

  defp handle_cart_event(_event, _params, socket), do: {:cont, socket}

  # Shared info handlers

  defp handle_cart_info({:cart_updated, cart}, socket) do
    {:halt, update_cart_assigns(socket, cart)}
  end

  defp handle_cart_info(_msg, socket), do: {:cont, socket}

  # Public helpers for LiveViews with custom cart logic

  @doc """
  Updates all cart-related assigns from raw cart data.
  """
  def update_cart_assigns(socket, cart) do
    %{items: items, count: count, subtotal: subtotal} = Cart.build_state(cart)

    socket
    |> assign(:raw_cart, cart)
    |> assign(:cart_items, items)
    |> assign(:cart_count, count)
    |> assign(:cart_subtotal, subtotal)
  end

  @doc """
  Broadcasts cart update to other tabs and updates local assigns.

  Uses broadcast_from to avoid notifying self (prevents double-update).
  """
  def broadcast_and_update(socket, cart) do
    if socket.assigns.cart_topic do
      Phoenix.PubSub.broadcast_from(
        SimpleshopTheme.PubSub,
        self(),
        socket.assigns.cart_topic,
        {:cart_updated, cart}
      )
    end

    socket
    |> update_cart_assigns(cart)
    |> push_event("persist_cart", %{items: Cart.serialize(cart)})
  end
end
