defmodule BerrypodWeb.CartController do
  @moduledoc """
  Cart controller handling both JSON API persistence and HTML form fallbacks.

  The JSON `update/2` action is called by a JS hook after each LiveView cart
  change. The HTML actions (`add/2`, `remove/2`, `update_item/2`) provide
  no-JS fallbacks via plain form POST + redirect.
  """

  use BerrypodWeb, :controller

  alias Berrypod.Cart

  plug BerrypodWeb.Plugs.RateLimit, [type: :api] when action == :update

  @doc """
  Updates the cart in session (JSON API for JS hook).
  """
  def update(conn, %{"items" => items}) when is_list(items) do
    cart_items = Cart.deserialize(items)

    conn
    |> Cart.put_in_session(cart_items)
    |> json(%{ok: true})
  end

  def update(conn, _params) do
    conn
    |> put_status(:bad_request)
    |> json(%{error: "Invalid cart data"})
  end

  @doc """
  Adds an item to cart via form POST (no-JS fallback).
  """
  def add(conn, %{"variant_id" => variant_id, "quantity" => qty_str}) do
    quantity = parse_quantity(qty_str)
    cart = Cart.get_from_session(get_session(conn))
    cart = Cart.add_item(cart, variant_id, quantity)

    conn
    |> Cart.put_in_session(cart)
    |> put_flash(:info, "Added to basket")
    |> redirect(to: R.cart())
  end

  def add(conn, _params) do
    conn
    |> put_flash(:error, "Could not add item to basket")
    |> redirect(to: R.cart())
  end

  @doc """
  Removes an item from cart via form POST (no-JS fallback).
  """
  def remove(conn, %{"variant_id" => variant_id}) do
    cart = Cart.get_from_session(get_session(conn))
    cart = Cart.remove_item(cart, variant_id)

    conn
    |> Cart.put_in_session(cart)
    |> put_flash(:info, "Removed from basket")
    |> redirect(to: R.cart())
  end

  @doc """
  Updates item quantity via form POST (no-JS fallback).
  """
  def update_item(conn, %{"variant_id" => variant_id, "quantity" => qty_str}) do
    quantity = parse_update_quantity(qty_str)
    cart = Cart.get_from_session(get_session(conn))
    cart = Cart.update_quantity(cart, variant_id, quantity)

    conn
    |> Cart.put_in_session(cart)
    |> redirect(to: R.cart())
  end

  @doc """
  Updates delivery country via form POST (no-JS fallback).
  """
  def update_country(conn, %{"country" => code}) when is_binary(code) and code != "" do
    conn
    |> put_session("country_code", code)
    |> redirect(to: R.cart())
  end

  def update_country(conn, _params) do
    redirect(conn, to: R.cart())
  end

  defp parse_quantity(str) when is_binary(str) do
    case Integer.parse(str) do
      {qty, _} when qty > 0 -> qty
      _ -> 1
    end
  end

  defp parse_quantity(_), do: 1

  # Allows 0 and negative values so Cart.update_quantity can remove items
  defp parse_update_quantity(str) when is_binary(str) do
    case Integer.parse(str) do
      {qty, _} -> qty
      :error -> 1
    end
  end

  defp parse_update_quantity(_), do: 1
end
