defmodule Berrypod.CartTest do
  use ExUnit.Case, async: true

  alias Berrypod.Cart

  describe "add_item/3" do
    test "adds a new item to an empty cart" do
      cart = Cart.add_item([], "variant-1")
      assert cart == [{"variant-1", 1}]
    end

    test "adds a new item with custom quantity" do
      cart = Cart.add_item([], "variant-1", 3)
      assert cart == [{"variant-1", 3}]
    end

    test "increments quantity for existing item" do
      cart = [{"variant-1", 2}]
      cart = Cart.add_item(cart, "variant-1", 1)
      assert cart == [{"variant-1", 3}]
    end

    test "adds different items separately" do
      cart =
        []
        |> Cart.add_item("variant-1", 1)
        |> Cart.add_item("variant-2", 2)

      assert cart == [{"variant-1", 1}, {"variant-2", 2}]
    end
  end

  describe "update_quantity/3" do
    test "updates the quantity of an existing item" do
      cart = [{"variant-1", 2}]
      cart = Cart.update_quantity(cart, "variant-1", 5)
      assert cart == [{"variant-1", 5}]
    end

    test "removes item when quantity is zero" do
      cart = [{"variant-1", 2}]
      cart = Cart.update_quantity(cart, "variant-1", 0)
      assert cart == []
    end

    test "removes item when quantity is negative" do
      cart = [{"variant-1", 2}]
      cart = Cart.update_quantity(cart, "variant-1", -1)
      assert cart == []
    end

    test "does nothing for non-existent item" do
      cart = [{"variant-1", 2}]
      cart = Cart.update_quantity(cart, "variant-999", 5)
      assert cart == [{"variant-1", 2}]
    end
  end

  describe "remove_item/2" do
    test "removes an item from the cart" do
      cart = [{"variant-1", 2}, {"variant-2", 1}]
      cart = Cart.remove_item(cart, "variant-1")
      assert cart == [{"variant-2", 1}]
    end

    test "does nothing for non-existent item" do
      cart = [{"variant-1", 2}]
      cart = Cart.remove_item(cart, "variant-999")
      assert cart == [{"variant-1", 2}]
    end

    test "returns empty list when removing last item" do
      cart = Cart.remove_item([{"variant-1", 1}], "variant-1")
      assert cart == []
    end
  end

  describe "get_quantity/2" do
    test "returns quantity for existing item" do
      cart = [{"variant-1", 3}]
      assert Cart.get_quantity(cart, "variant-1") == 3
    end

    test "returns 0 for non-existent item" do
      assert Cart.get_quantity([], "variant-1") == 0
    end
  end

  describe "item_count/1" do
    test "sums all quantities" do
      cart = [{"variant-1", 2}, {"variant-2", 3}]
      assert Cart.item_count(cart) == 5
    end

    test "returns 0 for empty cart" do
      assert Cart.item_count([]) == 0
    end
  end

  describe "format_price/1" do
    test "formats pence as GBP string" do
      assert Cart.format_price(2400) == "£24.00"
    end

    test "formats with pence correctly" do
      assert Cart.format_price(1499) == "£14.99"
    end

    test "formats zero" do
      assert Cart.format_price(0) == "£0.00"
    end

    test "formats single digit pence with padding" do
      assert Cart.format_price(105) == "£1.05"
    end

    test "returns fallback for non-integer" do
      assert Cart.format_price(nil) == "£0.00"
      assert Cart.format_price("foo") == "£0.00"
    end
  end

  describe "calculate_subtotal/1" do
    test "sums price * quantity for all items" do
      items = [
        %{price: 2400, quantity: 1},
        %{price: 1499, quantity: 2}
      ]

      assert Cart.calculate_subtotal(items) == 5398
    end

    test "returns 0 for empty list" do
      assert Cart.calculate_subtotal([]) == 0
    end
  end

  describe "serialize/1 and deserialize/1" do
    test "round-trips cart data" do
      cart = [{"variant-1", 2}, {"variant-2", 1}]
      assert cart == cart |> Cart.serialize() |> Cart.deserialize()
    end

    test "serialize converts tuples to lists" do
      assert Cart.serialize([{"v1", 3}]) == [["v1", 3]]
    end

    test "deserialize drops malformed entries" do
      assert Cart.deserialize([[1, 2]]) == []
      assert Cart.deserialize([["valid", 1], [nil, 2]]) == [{"valid", 1}]
    end

    test "deserialize handles non-list input" do
      assert Cart.deserialize(nil) == []
      assert Cart.deserialize("garbage") == []
    end
  end

  describe "get_from_session/1" do
    test "returns cart items from session" do
      session = %{"cart" => [{"v1", 2}]}
      assert Cart.get_from_session(session) == [{"v1", 2}]
    end

    test "returns empty list when no cart in session" do
      assert Cart.get_from_session(%{}) == []
    end

    test "returns empty list for invalid cart data" do
      assert Cart.get_from_session(%{"cart" => "garbage"}) == []
    end
  end
end
