"""
Unit tests for billing models.

These tests use mocks to avoid database overhead. Only use @pytest.mark.django_db
for tests that require actual database operations (constraints, migrations).
"""

from datetime import timedelta
from unittest.mock import Mock

import pytest
from django.utils import timezone


# =============================================================================
# Feature Model Tests
# =============================================================================


class TestFeatureModel:
    """Tests for the Feature model."""

    def test_feature_has_required_fields(self):
        """Feature model should have code, name, feature_type fields."""
        from smoothschedule.billing.models import Feature

        # Check model has expected fields
        field_names = [f.name for f in Feature._meta.get_fields()]
        assert "code" in field_names
        assert "name" in field_names
        assert "feature_type" in field_names
        assert "description" in field_names
        assert "created_at" in field_names

    def test_feature_type_choices(self):
        """Feature should support boolean and integer types."""
        from smoothschedule.billing.models import Feature

        feature = Feature(
            code="test_feature",
            name="Test Feature",
            feature_type="boolean",
        )
        assert feature.feature_type == "boolean"

        feature2 = Feature(
            code="test_limit",
            name="Test Limit",
            feature_type="integer",
        )
        assert feature2.feature_type == "integer"

    def test_feature_str_representation(self):
        """Feature __str__ should return the feature name."""
        from smoothschedule.billing.models import Feature

        feature = Feature(code="sms", name="SMS Notifications")
        assert str(feature) == "SMS Notifications"


# =============================================================================
# Plan Model Tests
# =============================================================================


class TestPlanModel:
    """Tests for the Plan model (logical plan grouping)."""

    def test_plan_has_required_fields(self):
        """Plan model should have code, name, display_order, is_active fields."""
        from smoothschedule.billing.models import Plan

        field_names = [f.name for f in Plan._meta.get_fields()]
        assert "code" in field_names
        assert "name" in field_names
        assert "display_order" in field_names
        assert "is_active" in field_names

    def test_plan_str_representation(self):
        """Plan __str__ should return the plan name."""
        from smoothschedule.billing.models import Plan

        plan = Plan(code="pro", name="Pro Plan")
        assert str(plan) == "Pro Plan"

    def test_plan_default_values(self):
        """Plan should have sensible defaults."""
        from smoothschedule.billing.models import Plan

        plan = Plan(code="starter", name="Starter")
        assert plan.is_active is True
        assert plan.display_order == 0


# =============================================================================
# PlanVersion Model Tests
# =============================================================================


class TestPlanVersionModel:
    """Tests for the PlanVersion model (concrete offer)."""

    def test_plan_version_has_required_fields(self):
        """PlanVersion should have pricing, visibility, and Stripe fields."""
        from smoothschedule.billing.models import PlanVersion

        field_names = [f.name for f in PlanVersion._meta.get_fields()]
        assert "plan" in field_names
        assert "version" in field_names
        assert "name" in field_names
        assert "is_public" in field_names
        assert "is_legacy" in field_names
        assert "starts_at" in field_names
        assert "ends_at" in field_names
        assert "price_monthly_cents" in field_names
        assert "price_yearly_cents" in field_names
        assert "stripe_product_id" in field_names
        assert "stripe_price_id_monthly" in field_names
        assert "stripe_price_id_yearly" in field_names

    def test_plan_version_str_representation(self):
        """PlanVersion __str__ should return the version name."""
        from smoothschedule.billing.models import PlanVersion

        pv = PlanVersion(name="Pro Plan - 2024 Holiday Promo")
        # Don't need to set plan for __str__ - it just uses name
        assert str(pv) == "Pro Plan - 2024 Holiday Promo"

    def test_plan_version_is_available_when_public_and_no_date_constraints(self):
        """PlanVersion.is_available should return True for public versions with no dates."""
        from smoothschedule.billing.models import PlanVersion

        pv = PlanVersion(is_public=True, is_legacy=False, starts_at=None, ends_at=None)
        assert pv.is_available is True

    def test_plan_version_is_not_available_when_legacy(self):
        """Legacy versions should not be available for new signups."""
        from smoothschedule.billing.models import PlanVersion

        pv = PlanVersion(is_public=True, is_legacy=True, starts_at=None, ends_at=None)
        assert pv.is_available is False

    def test_plan_version_is_not_available_when_not_public(self):
        """Non-public versions should not be available."""
        from smoothschedule.billing.models import PlanVersion

        pv = PlanVersion(is_public=False, is_legacy=False, starts_at=None, ends_at=None)
        assert pv.is_available is False

    def test_plan_version_is_available_within_date_window(self):
        """PlanVersion should be available within its date window."""
        from smoothschedule.billing.models import PlanVersion

        now = timezone.now()
        pv = PlanVersion(
            is_public=True,
            is_legacy=False,
            starts_at=now - timedelta(days=1),
            ends_at=now + timedelta(days=1),
        )
        assert pv.is_available is True

    def test_plan_version_is_not_available_before_start_date(self):
        """PlanVersion should not be available before its start date."""
        from smoothschedule.billing.models import PlanVersion

        now = timezone.now()
        pv = PlanVersion(
            is_public=True,
            is_legacy=False,
            starts_at=now + timedelta(days=1),
            ends_at=None,
        )
        assert pv.is_available is False

    def test_plan_version_is_not_available_after_end_date(self):
        """PlanVersion should not be available after its end date."""
        from smoothschedule.billing.models import PlanVersion

        now = timezone.now()
        pv = PlanVersion(
            is_public=True,
            is_legacy=False,
            starts_at=None,
            ends_at=now - timedelta(days=1),
        )
        assert pv.is_available is False


# =============================================================================
# PlanFeature Model Tests
# =============================================================================


class TestPlanFeatureModel:
    """Tests for the PlanFeature model (plan -> feature mapping)."""

    def test_plan_feature_has_required_fields(self):
        """PlanFeature should have plan_version, feature, and value fields."""
        from smoothschedule.billing.models import PlanFeature

        field_names = [f.name for f in PlanFeature._meta.get_fields()]
        assert "plan_version" in field_names
        assert "feature" in field_names
        assert "bool_value" in field_names
        assert "int_value" in field_names

    @pytest.mark.django_db
    def test_plan_feature_str_representation(self):
        """PlanFeature __str__ should return plan version and feature names."""
        from smoothschedule.billing.models import Feature, Plan, PlanFeature, PlanVersion

        plan = Plan.objects.create(code="test_pf_str", name="Test Plan PF Str")
        pv = PlanVersion.objects.create(plan=plan, version=1, name="Test Plan v1 Str")
        feature = Feature.objects.create(
            code="test_pf_feature_str", name="Test PF Feature", feature_type="boolean"
        )

        pf = PlanFeature(plan_version=pv, feature=feature, bool_value=True)
        assert str(pf) == "Test Plan v1 Str - Test PF Feature"

    @pytest.mark.django_db
    def test_plan_feature_get_value_returns_bool(self):
        """get_value should return bool_value for boolean features."""
        from smoothschedule.billing.models import Feature
        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanFeature
        from smoothschedule.billing.models import PlanVersion

        # Create real instances since Django ForeignKey doesn't accept Mock
        feature = Feature.objects.create(
            code="test_bool_feature",
            name="Test Bool Feature",
            feature_type="boolean",
        )
        plan = Plan.objects.create(code="test_plan", name="Test Plan")
        pv = PlanVersion.objects.create(plan=plan, version=1, name="Test Plan v1")

        pf = PlanFeature(plan_version=pv, feature=feature, bool_value=True, int_value=None)
        assert pf.get_value() is True

    @pytest.mark.django_db
    def test_plan_feature_get_value_returns_int(self):
        """get_value should return int_value for integer features."""
        from smoothschedule.billing.models import Feature
        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanFeature
        from smoothschedule.billing.models import PlanVersion

        feature = Feature.objects.create(
            code="test_int_feature",
            name="Test Int Feature",
            feature_type="integer",
        )
        plan = Plan.objects.create(code="test_plan2", name="Test Plan 2")
        pv = PlanVersion.objects.create(plan=plan, version=1, name="Test Plan 2 v1")

        pf = PlanFeature(plan_version=pv, feature=feature, bool_value=None, int_value=100)
        assert pf.get_value() == 100


# =============================================================================
# Subscription Model Tests
# =============================================================================


class TestSubscriptionModel:
    """Tests for the Subscription model."""

    def test_subscription_has_required_fields(self):
        """Subscription should have business, plan_version, status, dates, etc."""
        from smoothschedule.billing.models import Subscription

        field_names = [f.name for f in Subscription._meta.get_fields()]
        assert "business" in field_names
        assert "plan_version" in field_names
        assert "status" in field_names
        assert "started_at" in field_names
        assert "current_period_start" in field_names
        assert "current_period_end" in field_names
        assert "trial_ends_at" in field_names
        assert "canceled_at" in field_names
        assert "stripe_subscription_id" in field_names

    def test_subscription_status_choices(self):
        """Subscription should support trial, active, past_due, canceled statuses."""
        from smoothschedule.billing.models import Subscription

        valid_statuses = ["trial", "active", "past_due", "canceled"]
        for status in valid_statuses:
            sub = Subscription(status=status)
            assert sub.status == status

    def test_subscription_is_active_when_status_is_active(self):
        """is_active property should return True for active subscriptions."""
        from smoothschedule.billing.models import Subscription

        sub = Subscription(status="active")
        assert sub.is_active is True

    def test_subscription_is_active_when_status_is_trial(self):
        """Trial subscriptions should be considered active."""
        from smoothschedule.billing.models import Subscription

        sub = Subscription(status="trial")
        assert sub.is_active is True

    def test_subscription_is_not_active_when_canceled(self):
        """Canceled subscriptions should not be considered active."""
        from smoothschedule.billing.models import Subscription

        sub = Subscription(status="canceled")
        assert sub.is_active is False

    def test_subscription_is_not_active_when_past_due(self):
        """Past due subscriptions should not be considered active."""
        from smoothschedule.billing.models import Subscription

        sub = Subscription(status="past_due")
        assert sub.is_active is False

    @pytest.mark.django_db
    def test_subscription_str_representation(self):
        """Subscription __str__ should return business and plan version names."""
        from smoothschedule.billing.models import Plan, PlanVersion, Subscription
        from smoothschedule.identity.core.models import Tenant

        tenant = Tenant.objects.create(
            schema_name="test_sub_str",
            name="Test Sub Str Business"
        )
        plan = Plan.objects.create(code="test_sub_str_plan", name="Test Sub Plan")
        pv = PlanVersion.objects.create(plan=plan, version=1, name="Test Sub Plan v1")

        sub = Subscription(business=tenant, plan_version=pv, status="active")
        assert str(sub) == "Test Sub Str Business - Test Sub Plan v1"


# =============================================================================
# AddOnProduct Model Tests
# =============================================================================


class TestAddOnProductModel:
    """Tests for the AddOnProduct model."""

    def test_addon_has_required_fields(self):
        """AddOnProduct should have code, name, pricing, Stripe fields."""
        from smoothschedule.billing.models import AddOnProduct

        field_names = [f.name for f in AddOnProduct._meta.get_fields()]
        assert "code" in field_names
        assert "name" in field_names
        assert "description" in field_names
        assert "price_monthly_cents" in field_names
        assert "price_one_time_cents" in field_names
        assert "stripe_product_id" in field_names
        assert "stripe_price_id" in field_names
        assert "is_active" in field_names

    def test_addon_str_representation(self):
        """AddOnProduct __str__ should return the addon name."""
        from smoothschedule.billing.models import AddOnProduct

        addon = AddOnProduct(code="sms_pack", name="SMS Pack (1000)")
        assert str(addon) == "SMS Pack (1000)"


# =============================================================================
# AddOnFeature Model Tests
# =============================================================================


class TestAddOnFeatureModel:
    """Tests for the AddOnFeature model."""

    def test_addon_feature_has_required_fields(self):
        """AddOnFeature should have addon, feature, and value fields."""
        from smoothschedule.billing.models import AddOnFeature

        field_names = [f.name for f in AddOnFeature._meta.get_fields()]
        assert "addon" in field_names
        assert "feature" in field_names
        assert "bool_value" in field_names
        assert "int_value" in field_names

    @pytest.mark.django_db
    def test_addon_feature_str_representation(self):
        """AddOnFeature __str__ should return addon and feature names."""
        from smoothschedule.billing.models import AddOnFeature, AddOnProduct, Feature

        addon = AddOnProduct.objects.create(code="test_addon_str", name="Test Addon")
        feature = Feature.objects.create(
            code="test_addon_feature_str", name="Test Feature", feature_type="boolean"
        )

        af = AddOnFeature(addon=addon, feature=feature, bool_value=True)
        assert str(af) == "Test Addon - Test Feature"

    @pytest.mark.django_db
    def test_addon_feature_get_value_returns_bool(self):
        """get_value should return bool_value for boolean features."""
        from smoothschedule.billing.models import AddOnFeature, AddOnProduct, Feature

        addon = AddOnProduct.objects.create(code="test_addon_bool", name="Test Addon Bool")
        feature = Feature.objects.create(
            code="test_af_bool", name="Test AF Bool", feature_type="boolean"
        )

        af = AddOnFeature(addon=addon, feature=feature, bool_value=True, int_value=None)
        assert af.get_value() is True

    @pytest.mark.django_db
    def test_addon_feature_get_value_returns_int(self):
        """get_value should return int_value for integer features."""
        from smoothschedule.billing.models import AddOnFeature, AddOnProduct, Feature

        addon = AddOnProduct.objects.create(code="test_addon_int", name="Test Addon Int")
        feature = Feature.objects.create(
            code="test_af_int", name="Test AF Int", feature_type="integer"
        )

        af = AddOnFeature(addon=addon, feature=feature, bool_value=None, int_value=50)
        assert af.get_value() == 50


# =============================================================================
# SubscriptionAddOn Model Tests
# =============================================================================


class TestSubscriptionAddOnModel:
    """Tests for the SubscriptionAddOn model."""

    def test_subscription_addon_has_required_fields(self):
        """SubscriptionAddOn should have subscription, addon, status, dates."""
        from smoothschedule.billing.models import SubscriptionAddOn

        field_names = [f.name for f in SubscriptionAddOn._meta.get_fields()]
        assert "subscription" in field_names
        assert "addon" in field_names
        assert "status" in field_names
        assert "activated_at" in field_names
        assert "expires_at" in field_names
        assert "canceled_at" in field_names
        assert "stripe_subscription_item_id" in field_names

    def test_subscription_addon_is_active_when_status_active_no_expiry(self):
        """is_active should return True for active add-ons without expiry."""
        from smoothschedule.billing.models import SubscriptionAddOn

        sa = SubscriptionAddOn(status="active", expires_at=None)
        assert sa.is_active is True

    def test_subscription_addon_is_active_when_status_active_future_expiry(self):
        """is_active should return True for active add-ons with future expiry."""
        from smoothschedule.billing.models import SubscriptionAddOn

        future = timezone.now() + timedelta(days=30)
        sa = SubscriptionAddOn(status="active", expires_at=future)
        assert sa.is_active is True

    def test_subscription_addon_is_not_active_when_expired(self):
        """is_active should return False for expired add-ons."""
        from smoothschedule.billing.models import SubscriptionAddOn

        past = timezone.now() - timedelta(days=1)
        sa = SubscriptionAddOn(status="active", expires_at=past)
        assert sa.is_active is False

    def test_subscription_addon_is_not_active_when_canceled(self):
        """is_active should return False for canceled add-ons."""
        from smoothschedule.billing.models import SubscriptionAddOn

        sa = SubscriptionAddOn(status="canceled", expires_at=None)
        assert sa.is_active is False

    @pytest.mark.django_db
    def test_subscription_addon_str_representation(self):
        """SubscriptionAddOn __str__ should return business and addon names."""
        from datetime import date
        from smoothschedule.billing.models import (
            AddOnProduct, Plan, PlanVersion, Subscription, SubscriptionAddOn
        )
        from smoothschedule.identity.core.models import Tenant

        tenant = Tenant.objects.create(
            schema_name="test_sa_str",
            name="Test SA Business"
        )
        plan = Plan.objects.create(code="test_sa_plan", name="Test SA Plan")
        pv = PlanVersion.objects.create(plan=plan, version=1, name="Test SA Plan v1")
        sub = Subscription.objects.create(
            business=tenant,
            plan_version=pv,
            status="active",
            current_period_start=date.today(),
            current_period_end=date.today()
        )
        addon = AddOnProduct.objects.create(code="test_sa_addon", name="Test SA Addon")

        sa = SubscriptionAddOn(subscription=sub, addon=addon, status="active")
        assert str(sa) == "Test SA Business - Test SA Addon"


# =============================================================================
# EntitlementOverride Model Tests
# =============================================================================


class TestEntitlementOverrideModel:
    """Tests for the EntitlementOverride model."""

    def test_override_has_required_fields(self):
        """EntitlementOverride should have business, feature, source, value fields."""
        from smoothschedule.billing.models import EntitlementOverride

        field_names = [f.name for f in EntitlementOverride._meta.get_fields()]
        assert "business" in field_names
        assert "feature" in field_names
        assert "source" in field_names
        assert "bool_value" in field_names
        assert "int_value" in field_names
        assert "reason" in field_names
        assert "granted_by" in field_names
        assert "expires_at" in field_names
        assert "created_at" in field_names

    def test_override_source_choices(self):
        """EntitlementOverride should support manual, promo, support sources."""
        from smoothschedule.billing.models import EntitlementOverride

        valid_sources = ["manual", "promo", "support"]
        for source in valid_sources:
            override = EntitlementOverride(source=source)
            assert override.source == source

    def test_override_is_active_when_no_expiry(self):
        """is_active should return True for overrides without expiry."""
        from smoothschedule.billing.models import EntitlementOverride

        override = EntitlementOverride(expires_at=None)
        assert override.is_active is True

    def test_override_is_active_when_future_expiry(self):
        """is_active should return True for overrides with future expiry."""
        from smoothschedule.billing.models import EntitlementOverride

        future = timezone.now() + timedelta(days=30)
        override = EntitlementOverride(expires_at=future)
        assert override.is_active is True

    def test_override_is_not_active_when_expired(self):
        """is_active should return False for expired overrides."""
        from smoothschedule.billing.models import EntitlementOverride

        past = timezone.now() - timedelta(days=1)
        override = EntitlementOverride(expires_at=past)
        assert override.is_active is False

    @pytest.mark.django_db
    def test_override_get_value_returns_bool(self):
        """get_value should return bool_value for boolean features."""
        from smoothschedule.billing.models import EntitlementOverride
        from smoothschedule.billing.models import Feature

        feature = Feature.objects.create(
            code="override_test_bool",
            name="Override Test Bool",
            feature_type="boolean",
        )

        override = EntitlementOverride(feature=feature, bool_value=True, int_value=None)
        assert override.get_value() is True

    @pytest.mark.django_db
    def test_override_get_value_returns_int(self):
        """get_value should return int_value for integer features."""
        from smoothschedule.billing.models import EntitlementOverride
        from smoothschedule.billing.models import Feature

        feature = Feature.objects.create(
            code="override_test_int",
            name="Override Test Int",
            feature_type="integer",
        )

        override = EntitlementOverride(feature=feature, bool_value=None, int_value=500)
        assert override.get_value() == 500

    @pytest.mark.django_db
    def test_override_str_representation(self):
        """EntitlementOverride __str__ should return business and feature names."""
        from smoothschedule.billing.models import EntitlementOverride, Feature
        from smoothschedule.identity.core.models import Tenant

        tenant = Tenant.objects.create(
            schema_name="test_override_str",
            name="Test Override Business"
        )
        feature = Feature.objects.create(
            code="override_str_feature",
            name="Override Feature",
            feature_type="boolean"
        )

        override = EntitlementOverride(business=tenant, feature=feature, bool_value=True)
        assert str(override) == "Test Override Business - Override Feature override"


# =============================================================================
# Invoice Model Tests
# =============================================================================


class TestInvoiceModel:
    """Tests for the Invoice model."""

    def test_invoice_has_required_fields(self):
        """Invoice should have business, amounts, status fields."""
        from smoothschedule.billing.models import Invoice

        field_names = [f.name for f in Invoice._meta.get_fields()]
        assert "business" in field_names
        assert "period_start" in field_names
        assert "period_end" in field_names
        assert "subtotal_amount" in field_names
        assert "total_amount" in field_names
        assert "status" in field_names
        assert "stripe_invoice_id" in field_names

    @pytest.mark.django_db
    def test_invoice_str_representation(self):
        """Invoice __str__ should return invoice id, business name, and status."""
        from datetime import date
        from smoothschedule.billing.models import Invoice
        from smoothschedule.identity.core.models import Tenant

        tenant = Tenant.objects.create(
            schema_name="test_inv_str",
            name="Test Invoice Business"
        )

        invoice = Invoice.objects.create(
            business=tenant,
            status="paid",
            total_amount=9900,
            period_start=date.today(),
            period_end=date.today()
        )
        expected = f"Invoice {invoice.id} - Test Invoice Business (paid)"
        assert str(invoice) == expected


# =============================================================================
# InvoiceLine Model Tests
# =============================================================================


class TestInvoiceLineModel:
    """Tests for the InvoiceLine model."""

    def test_invoice_line_has_required_fields(self):
        """InvoiceLine should have invoice, type, description, amounts."""
        from smoothschedule.billing.models import InvoiceLine

        field_names = [f.name for f in InvoiceLine._meta.get_fields()]
        assert "invoice" in field_names
        assert "line_type" in field_names
        assert "description" in field_names
        assert "quantity" in field_names
        assert "unit_amount" in field_names
        assert "total_amount" in field_names

    @pytest.mark.django_db
    def test_invoice_line_str_representation(self):
        """InvoiceLine __str__ should return type, description, and total amount."""
        from datetime import date
        from smoothschedule.billing.models import Invoice, InvoiceLine
        from smoothschedule.identity.core.models import Tenant

        tenant = Tenant.objects.create(
            schema_name="test_il_str",
            name="Test IL Business"
        )
        invoice = Invoice.objects.create(
            business=tenant,
            status="paid",
            total_amount=9900,
            period_start=date.today(),
            period_end=date.today()
        )

        line = InvoiceLine(
            invoice=invoice,
            line_type="plan",
            description="Pro Plan Monthly",
            total_amount=9900
        )
        assert str(line) == "plan: Pro Plan Monthly (9900 cents)"


# =============================================================================
# Database Constraint Tests (require @pytest.mark.django_db)
# =============================================================================


@pytest.mark.django_db
class TestModelConstraints:
    """Tests that verify database constraints. These require actual DB access."""

    def test_feature_code_is_unique(self):
        """Feature.code should be unique."""
        import uuid

        from django.db import IntegrityError

        from smoothschedule.billing.models import Feature

        unique_code = f"test_feature_{uuid.uuid4().hex[:8]}"
        Feature.objects.create(code=unique_code, name="Test Feature", feature_type="boolean")

        with pytest.raises(IntegrityError):
            Feature.objects.create(code=unique_code, name="Test Feature 2", feature_type="boolean")

    def test_plan_code_is_unique(self):
        """Plan.code should be unique."""
        import uuid

        from django.db import IntegrityError

        from smoothschedule.billing.models import Plan

        unique_code = f"test_plan_{uuid.uuid4().hex[:8]}"
        Plan.objects.create(code=unique_code, name="Test Plan")

        with pytest.raises(IntegrityError):
            Plan.objects.create(code=unique_code, name="Test Plan 2")

    def test_plan_version_unique_together(self):
        """PlanVersion (plan, version) should be unique together."""
        import uuid

        from django.db import IntegrityError

        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanVersion

        unique_code = f"test_plan_{uuid.uuid4().hex[:8]}"
        plan = Plan.objects.create(code=unique_code, name="Test Plan")
        PlanVersion.objects.create(plan=plan, version=1, name="Test Plan v1")

        with pytest.raises(IntegrityError):
            PlanVersion.objects.create(plan=plan, version=1, name="Test Plan v1 duplicate")

    def test_plan_feature_unique_together(self):
        """PlanFeature (plan_version, feature) should be unique together."""
        import uuid

        from django.db import IntegrityError

        from smoothschedule.billing.models import Feature
        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanFeature
        from smoothschedule.billing.models import PlanVersion

        unique_plan_code = f"test_plan_{uuid.uuid4().hex[:8]}"
        unique_feature_code = f"test_feature_{uuid.uuid4().hex[:8]}"

        plan = Plan.objects.create(code=unique_plan_code, name="Test Plan")
        pv = PlanVersion.objects.create(plan=plan, version=1, name="Test Plan v1")
        feature = Feature.objects.create(code=unique_feature_code, name="Test Feature", feature_type="boolean")

        PlanFeature.objects.create(plan_version=pv, feature=feature, bool_value=True)

        with pytest.raises(IntegrityError):
            PlanFeature.objects.create(
                plan_version=pv, feature=feature, bool_value=False
            )

    def test_addon_code_is_unique(self):
        """AddOnProduct.code should be unique."""
        import uuid

        from django.db import IntegrityError

        from smoothschedule.billing.models import AddOnProduct

        unique_code = f"test_addon_{uuid.uuid4().hex[:8]}"
        AddOnProduct.objects.create(code=unique_code, name="Test Addon")

        with pytest.raises(IntegrityError):
            AddOnProduct.objects.create(code=unique_code, name="Test Addon 2")
