# tests/unit/models/database/test_product.py
"""Unit tests for Product (store catalog) database model."""

import pytest
from sqlalchemy.exc import IntegrityError

from app.modules.catalog.models import Product


@pytest.mark.unit
@pytest.mark.database
class TestProductModel:
    """Test Product (store catalog) model."""

    def test_product_creation(self, db, test_store, test_marketplace_product):
        """Test Product model linking store catalog to marketplace product."""
        product = Product(
            store_id=test_store.id,
            marketplace_product_id=test_marketplace_product.id,
            store_sku="STORE_PROD_001",
            price=89.99,
            currency="EUR",
            availability="in stock",
            is_featured=True,
            is_active=True,
        )

        db.add(product)
        db.commit()
        db.refresh(product)

        assert product.id is not None
        assert product.store_id == test_store.id
        assert product.marketplace_product_id == test_marketplace_product.id
        assert product.price == 89.99
        assert product.is_featured is True
        assert product.store.store_code == test_store.store_code
        # Use get_title() method instead of .title attribute
        assert product.marketplace_product.get_title(
            "en"
        ) == test_marketplace_product.get_title("en")

    def test_product_unique_per_store(self, db, test_store, test_marketplace_product):
        """Test that same marketplace product can't be added twice to store catalog."""
        product1 = Product(
            store_id=test_store.id,
            marketplace_product_id=test_marketplace_product.id,
            is_active=True,
        )
        db.add(product1)
        db.commit()

        # Same marketplace product to same store should fail
        with pytest.raises(IntegrityError):
            product2 = Product(
                store_id=test_store.id,
                marketplace_product_id=test_marketplace_product.id,
                is_active=True,
            )
            db.add(product2)
            db.commit()

    def test_product_default_values(self, db, test_store, test_marketplace_product):
        """Test Product model default values."""
        product = Product(
            store_id=test_store.id,
            marketplace_product_id=test_marketplace_product.id,
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        assert product.is_active is True  # Default
        assert product.is_featured is False  # Default
        assert product.is_digital is False  # Default
        assert product.product_type == "physical"  # Default
        assert product.min_quantity == 1  # Default
        assert product.display_order == 0  # Default

    def test_product_store_override_fields(
        self, db, test_store, test_marketplace_product
    ):
        """Test Product model store-specific override fields."""
        product = Product(
            store_id=test_store.id,
            marketplace_product_id=test_marketplace_product.id,
            store_sku="CUSTOM_SKU_001",
            price=49.99,
            sale_price=39.99,
            currency="USD",
            availability="limited",
            condition="new",
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        assert product.store_sku == "CUSTOM_SKU_001"
        assert product.price == 49.99
        assert product.sale_price == 39.99
        assert product.currency == "USD"
        assert product.availability == "limited"

    def test_product_inventory_settings(
        self, db, test_store, test_marketplace_product
    ):
        """Test Product model inventory settings."""
        product = Product(
            store_id=test_store.id,
            marketplace_product_id=test_marketplace_product.id,
            min_quantity=2,
            max_quantity=10,
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        assert product.min_quantity == 2
        assert product.max_quantity == 10

    def test_product_relationships(self, db, test_store, test_marketplace_product):
        """Test Product relationships."""
        product = Product(
            store_id=test_store.id,
            marketplace_product_id=test_marketplace_product.id,
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        assert product.store is not None
        assert product.marketplace_product is not None
        assert product.inventory_entries == []  # No inventory yet

    def test_product_get_source_comparison_info(
        self, db, test_store, test_marketplace_product
    ):
        """Test get_source_comparison_info method for 'view original source' feature.

        Products are independent entities with all fields populated at creation.
        Source values are kept for comparison only, not inheritance.
        """
        # Set up marketplace product values
        test_marketplace_product.price_cents = 10000  # €100.00
        test_marketplace_product.brand = "SourceBrand"
        db.commit()

        # Create product with its own values (independent copy pattern)
        product = Product(
            store_id=test_store.id,
            marketplace_product_id=test_marketplace_product.id,
            price_cents=8999,  # €89.99 - store's price
            brand="StoreBrand",  # Store's brand
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        info = product.get_source_comparison_info()

        # Product has its own price
        assert info["price"] == 89.99
        assert info["price_cents"] == 8999
        assert info["price_source"] == 100.00  # Original marketplace price

        # Product has its own brand
        assert info["brand"] == "StoreBrand"
        assert info["brand_source"] == "SourceBrand"  # Original marketplace brand

        # No more *_overridden keys in the pattern
        assert "price_overridden" not in info
        assert "brand_overridden" not in info

    def test_product_fields_are_independent(
        self, db, test_store, test_marketplace_product
    ):
        """Test that product fields don't inherit from marketplace product.

        Products are independent entities - NULL fields stay NULL,
        no inheritance/fallback logic.
        """
        # Set up marketplace product values
        test_marketplace_product.price_cents = 10000
        test_marketplace_product.brand = "SourceBrand"
        db.commit()

        # Create product without copying values
        product = Product(
            store_id=test_store.id,
            marketplace_product_id=test_marketplace_product.id,
            # Not copying price_cents or brand
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        # Fields should be NULL (not inherited)
        assert product.price_cents is None
        assert product.price is None
        assert product.brand is None

        # But we can still see the source values for comparison
        info = product.get_source_comparison_info()
        assert info["price_source"] == 100.00
        assert info["brand_source"] == "SourceBrand"

    def test_product_direct_creation_without_marketplace(self, db, test_store):
        """Test creating a product directly without a marketplace source.

        Products can be created directly without a marketplace_product_id,
        making them fully independent store products.
        """
        product = Product(
            store_id=test_store.id,
            marketplace_product_id=None,  # No marketplace source
            store_sku="DIRECT_001",
            brand="DirectBrand",
            price=59.99,
            currency="EUR",
            is_digital=True,
            product_type="digital",
            is_active=True,
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        assert product.id is not None
        assert product.marketplace_product_id is None
        assert product.marketplace_product is None
        assert product.store_sku == "DIRECT_001"
        assert product.brand == "DirectBrand"
        assert product.is_digital is True
        assert product.product_type == "digital"

    def test_product_is_digital_column(self, db, test_store):
        """Test is_digital is an independent column, not derived from marketplace."""
        # Create digital product without marketplace source
        digital_product = Product(
            store_id=test_store.id,
            store_sku="DIGITAL_001",
            is_digital=True,
            product_type="digital",
        )
        db.add(digital_product)
        db.commit()
        db.refresh(digital_product)

        assert digital_product.is_digital is True
        assert digital_product.product_type == "digital"

        # Create physical product without marketplace source
        physical_product = Product(
            store_id=test_store.id,
            store_sku="PHYSICAL_001",
            is_digital=False,
            product_type="physical",
        )
        db.add(physical_product)
        db.commit()
        db.refresh(physical_product)

        assert physical_product.is_digital is False
        assert physical_product.product_type == "physical"

    def test_product_type_values(self, db, test_store):
        """Test product_type can be set to various values."""
        product_types = ["physical", "digital", "service", "subscription"]

        for ptype in product_types:
            product = Product(
                store_id=test_store.id,
                store_sku=f"TYPE_{ptype.upper()}",
                product_type=ptype,
                is_digital=(ptype == "digital"),
            )
            db.add(product)  # noqa: PERF006
            db.commit()
            db.refresh(product)

            assert product.product_type == ptype


@pytest.mark.unit
@pytest.mark.database
@pytest.mark.inventory
class TestProductInventoryProperties:
    """Test Product inventory properties including digital product handling."""

    def test_physical_product_no_inventory_returns_zero(self, db, test_store):
        """Test physical product with no inventory entries returns 0."""
        product = Product(
            store_id=test_store.id,
            store_sku="PHYS_INV_001",
            is_digital=False,
            product_type="physical",
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        assert product.is_digital is False
        assert product.has_unlimited_inventory is False
        assert product.total_inventory == 0
        assert product.available_inventory == 0

    def test_physical_product_with_inventory(self, db, test_store):
        """Test physical product calculates inventory from entries."""
        from app.modules.inventory.models import Inventory  # noqa: IMPORT-002

        product = Product(
            store_id=test_store.id,
            store_sku="PHYS_INV_002",
            is_digital=False,
            product_type="physical",
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        # Add inventory entries
        inv1 = Inventory(
            product_id=product.id,
            store_id=test_store.id,
            warehouse="strassen",
            bin_location="SA-01-01",
            quantity=100,
            reserved_quantity=10,
        )
        inv2 = Inventory(
            product_id=product.id,
            store_id=test_store.id,
            warehouse="strassen",
            bin_location="SA-01-02",
            quantity=50,
            reserved_quantity=5,
        )
        db.add_all([inv1, inv2])
        db.commit()
        db.refresh(product)

        assert product.has_unlimited_inventory is False
        assert product.total_inventory == 150  # 100 + 50
        assert product.available_inventory == 135  # (100-10) + (50-5)

    def test_digital_product_has_unlimited_inventory(self, db, test_store):
        """Test digital product returns unlimited inventory."""
        product = Product(
            store_id=test_store.id,
            store_sku="DIG_INV_001",
            is_digital=True,
            product_type="digital",
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        assert product.is_digital is True
        assert product.has_unlimited_inventory is True
        assert product.total_inventory == Product.UNLIMITED_INVENTORY
        assert product.available_inventory == Product.UNLIMITED_INVENTORY

    def test_digital_product_ignores_inventory_entries(self, db, test_store):
        """Test digital product returns unlimited even with inventory entries."""
        from app.modules.inventory.models import Inventory  # noqa: IMPORT-002

        product = Product(
            store_id=test_store.id,
            store_sku="DIG_INV_002",
            is_digital=True,
            product_type="digital",
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        # Add inventory entries (e.g., for license keys)
        inv = Inventory(
            product_id=product.id,
            store_id=test_store.id,
            warehouse="strassen",
            bin_location="DIG-01-01",
            quantity=10,
            reserved_quantity=2,
        )
        db.add(inv)
        db.commit()
        db.refresh(product)

        # Digital product should still return unlimited
        assert product.has_unlimited_inventory is True
        assert product.total_inventory == Product.UNLIMITED_INVENTORY
        assert product.available_inventory == Product.UNLIMITED_INVENTORY

    def test_unlimited_inventory_constant(self):
        """Test UNLIMITED_INVENTORY constant value."""
        assert Product.UNLIMITED_INVENTORY == 999999
        # Should be large enough to never cause "insufficient inventory"
        assert Product.UNLIMITED_INVENTORY > 100000
