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

import pytest
from sqlalchemy.exc import IntegrityError

from models.database.product import Product


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

    def test_product_creation(self, db, test_vendor, test_marketplace_product):
        """Test Product model linking vendor catalog to marketplace product."""
        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
            vendor_sku="VENDOR_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.vendor_id == test_vendor.id
        assert product.marketplace_product_id == test_marketplace_product.id
        assert product.price == 89.99
        assert product.is_featured is True
        assert product.vendor.vendor_code == test_vendor.vendor_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_vendor(self, db, test_vendor, test_marketplace_product):
        """Test that same marketplace product can't be added twice to vendor catalog."""
        product1 = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
            is_active=True,
        )
        db.add(product1)
        db.commit()

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

    def test_product_default_values(self, db, test_vendor, test_marketplace_product):
        """Test Product model default values."""
        product = Product(
            vendor_id=test_vendor.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.min_quantity == 1  # Default
        assert product.display_order == 0  # Default

    def test_product_vendor_override_fields(
        self, db, test_vendor, test_marketplace_product
    ):
        """Test Product model vendor-specific override fields."""
        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
            vendor_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.vendor_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_vendor, test_marketplace_product
    ):
        """Test Product model inventory settings."""
        product = Product(
            vendor_id=test_vendor.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_vendor, test_marketplace_product):
        """Test Product relationships."""
        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
        )
        db.add(product)
        db.commit()
        db.refresh(product)

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

    def test_product_effective_properties(
        self, db, test_vendor, test_marketplace_product
    ):
        """Test Product effective properties with override pattern."""
        # First, set some values on the marketplace product
        test_marketplace_product.price_numeric = 100.00
        test_marketplace_product.brand = "SourceBrand"
        db.commit()
        db.refresh(test_marketplace_product)

        # Create product without overrides
        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        # Should inherit from marketplace product
        assert product.effective_price == 100.00
        assert product.effective_brand == "SourceBrand"

        # Now override the price
        product.price = 89.99
        db.commit()
        db.refresh(product)

        # Should use the override
        assert product.effective_price == 89.99
        # Brand still inherited
        assert product.effective_brand == "SourceBrand"

    def test_product_reset_to_source(self, db, test_vendor, test_marketplace_product):
        """Test reset_to_source methods."""
        # Set up marketplace product values (use cents internally)
        test_marketplace_product.price_cents = 10000  # €100.00
        test_marketplace_product.brand = "SourceBrand"
        db.commit()

        # Create product with overrides (price property converts euros to cents)
        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
            price_cents=8999,  # €89.99
            brand="OverrideBrand",
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        assert product.effective_price == 89.99
        assert product.effective_brand == "OverrideBrand"

        # Reset price_cents to source (OVERRIDABLE_FIELDS now uses _cents names)
        product.reset_field_to_source("price_cents")
        db.commit()
        db.refresh(product)

        assert product.price_cents is None
        assert product.price is None  # Property returns None when cents is None
        assert product.effective_price == 100.00  # Now inherits from marketplace

        # Reset all fields
        product.reset_all_to_source()
        db.commit()
        db.refresh(product)

        assert product.brand is None
        assert product.effective_brand == "SourceBrand"  # Now inherits

    def test_product_get_override_info(self, db, test_vendor, test_marketplace_product):
        """Test get_override_info method."""
        test_marketplace_product.price_numeric = 100.00
        test_marketplace_product.brand = "SourceBrand"
        db.commit()

        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
            price=89.99,  # Override
            # brand not set - will inherit
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        info = product.get_override_info()

        # Price is overridden
        assert info["price"] == 89.99
        assert info["price_overridden"] is True
        assert info["price_source"] == 100.00

        # Brand is inherited
        assert info["brand"] == "SourceBrand"
        assert info["brand_overridden"] is False
        assert info["brand_source"] == "SourceBrand"


@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_vendor, test_marketplace_product
    ):
        """Test physical product with no inventory entries returns 0."""
        # Ensure product is physical
        test_marketplace_product.is_digital = False
        db.commit()

        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
        )
        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_vendor, test_marketplace_product
    ):
        """Test physical product calculates inventory from entries."""
        from models.database.inventory import Inventory

        test_marketplace_product.is_digital = False
        db.commit()

        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        # Add inventory entries
        inv1 = Inventory(
            product_id=product.id,
            vendor_id=test_vendor.id,
            location="WAREHOUSE_A",
            quantity=100,
            reserved_quantity=10,
        )
        inv2 = Inventory(
            product_id=product.id,
            vendor_id=test_vendor.id,
            location="WAREHOUSE_B",
            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_vendor, test_marketplace_product
    ):
        """Test digital product returns unlimited inventory."""
        test_marketplace_product.is_digital = True
        db.commit()

        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
        )
        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_vendor, test_marketplace_product
    ):
        """Test digital product returns unlimited even with inventory entries."""
        from models.database.inventory import Inventory

        test_marketplace_product.is_digital = True
        db.commit()

        product = Product(
            vendor_id=test_vendor.id,
            marketplace_product_id=test_marketplace_product.id,
        )
        db.add(product)
        db.commit()
        db.refresh(product)

        # Add inventory entries (e.g., for license keys)
        inv = Inventory(
            product_id=product.id,
            vendor_id=test_vendor.id,
            location="DIGITAL_LICENSES",
            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
