defmodule Berrypod.Products.ProductVariantTest do
  use Berrypod.DataCase, async: false

  alias Berrypod.Products.ProductVariant

  import Berrypod.ProductsFixtures

  describe "changeset/2" do
    setup do
      product = product_fixture()
      {:ok, product: product}
    end

    test "valid attributes create a valid changeset", %{product: product} do
      attrs = valid_product_variant_attrs(%{product_id: product.id})
      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)

      assert changeset.valid?
    end

    test "requires product_id", %{product: _product} do
      attrs = valid_product_variant_attrs() |> Map.delete(:product_id)
      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)

      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).product_id
    end

    test "requires provider_variant_id", %{product: product} do
      attrs =
        valid_product_variant_attrs(%{product_id: product.id})
        |> Map.delete(:provider_variant_id)

      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)

      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).provider_variant_id
    end

    test "requires title", %{product: product} do
      attrs =
        valid_product_variant_attrs(%{product_id: product.id})
        |> Map.delete(:title)

      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)

      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).title
    end

    test "requires price", %{product: product} do
      attrs =
        valid_product_variant_attrs(%{product_id: product.id})
        |> Map.delete(:price)

      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)

      refute changeset.valid?
      assert "can't be blank" in errors_on(changeset).price
    end

    test "validates price is non-negative", %{product: product} do
      attrs = valid_product_variant_attrs(%{product_id: product.id, price: -100})
      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)

      refute changeset.valid?
      assert "must be greater than or equal to 0" in errors_on(changeset).price
    end

    test "validates compare_at_price is non-negative when provided", %{product: product} do
      attrs = valid_product_variant_attrs(%{product_id: product.id, compare_at_price: -100})
      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)

      refute changeset.valid?
      assert "must be greater than or equal to 0" in errors_on(changeset).compare_at_price
    end

    test "validates cost is non-negative when provided", %{product: product} do
      attrs = valid_product_variant_attrs(%{product_id: product.id, cost: -100})
      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)

      refute changeset.valid?
      assert "must be greater than or equal to 0" in errors_on(changeset).cost
    end

    test "stores options as map", %{product: product} do
      options = %{"Size" => "Large", "Color" => "Red"}
      attrs = valid_product_variant_attrs(%{product_id: product.id, options: options})
      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)

      assert changeset.valid?
      assert changeset.changes.options == options
    end

    test "defaults is_enabled to true", %{product: product} do
      attrs =
        valid_product_variant_attrs(%{product_id: product.id})
        |> Map.delete(:is_enabled)

      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)
      assert changeset.valid?
    end

    test "defaults is_available to true", %{product: product} do
      attrs =
        valid_product_variant_attrs(%{product_id: product.id})
        |> Map.delete(:is_available)

      changeset = ProductVariant.changeset(%ProductVariant{}, attrs)
      assert changeset.valid?
    end
  end

  describe "profit/1" do
    test "calculates profit correctly" do
      variant = %ProductVariant{price: 2500, cost: 1200}
      assert ProductVariant.profit(variant) == 1300
    end

    test "returns nil when cost is nil" do
      variant = %ProductVariant{price: 2500, cost: nil}
      assert ProductVariant.profit(variant) == nil
    end

    test "returns nil when price is nil" do
      variant = %ProductVariant{price: nil, cost: 1200}
      assert ProductVariant.profit(variant) == nil
    end

    test "handles zero values" do
      variant = %ProductVariant{price: 0, cost: 0}
      assert ProductVariant.profit(variant) == 0
    end
  end

  describe "on_sale?/1" do
    test "returns true when compare_at_price is higher than price" do
      variant = %ProductVariant{price: 2000, compare_at_price: 2500}
      assert ProductVariant.on_sale?(variant) == true
    end

    test "returns false when compare_at_price equals price" do
      variant = %ProductVariant{price: 2500, compare_at_price: 2500}
      assert ProductVariant.on_sale?(variant) == false
    end

    test "returns false when compare_at_price is lower than price" do
      variant = %ProductVariant{price: 2500, compare_at_price: 2000}
      assert ProductVariant.on_sale?(variant) == false
    end

    test "returns false when compare_at_price is nil" do
      variant = %ProductVariant{price: 2500, compare_at_price: nil}
      assert ProductVariant.on_sale?(variant) == false
    end
  end

  describe "options_title/1" do
    test "formats options as slash-separated string" do
      variant = %ProductVariant{options: %{"Size" => "Large", "Color" => "Blue"}}
      title = ProductVariant.options_title(variant)

      # Map iteration order isn't guaranteed, so check both options are present
      assert String.contains?(title, "Large")
      assert String.contains?(title, "Blue")
      assert String.contains?(title, " / ")
    end

    test "returns nil for empty options" do
      variant = %ProductVariant{options: %{}}
      assert ProductVariant.options_title(variant) == nil
    end

    test "returns nil for nil options" do
      variant = %ProductVariant{options: nil}
      assert ProductVariant.options_title(variant) == nil
    end

    test "handles single option" do
      variant = %ProductVariant{options: %{"Size" => "Medium"}}
      assert ProductVariant.options_title(variant) == "Medium"
    end
  end

  describe "unique constraint" do
    test "enforces unique product_id + provider_variant_id" do
      product = product_fixture()
      _variant1 = product_variant_fixture(%{product: product, provider_variant_id: "var_123"})

      assert {:error, changeset} =
               Berrypod.Products.create_product_variant(
                 valid_product_variant_attrs(%{
                   product_id: product.id,
                   provider_variant_id: "var_123"
                 })
               )

      assert "has already been taken" in errors_on(changeset).product_id
    end
  end
end
