"""
Unit tests for Invoice generation service.

Tests verify that:
- Invoices are created with correct snapshot values
- Line items are created for plan and add-ons
- Changing PlanVersion pricing does NOT affect existing invoices
- Totals are calculated correctly
"""

from datetime import timedelta
from decimal import Decimal
from unittest.mock import Mock
from unittest.mock import patch

import pytest
from django.utils import timezone


@pytest.fixture
def clean_tenant_subscription(shared_tenant):
    """Delete any existing subscription for shared_tenant before test."""
    from smoothschedule.billing.models import Subscription
    Subscription.objects.filter(business=shared_tenant).delete()
    yield shared_tenant


# =============================================================================
# generate_invoice_for_subscription() Tests
# =============================================================================


@pytest.mark.django_db
class TestGenerateInvoiceForSubscription:
    """Tests for the invoice generation service."""

    def test_creates_invoice_with_plan_snapshots(self, clean_tenant_subscription):
        """Invoice should capture plan name and code at billing time."""
        import uuid
        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanVersion
        from smoothschedule.billing.models import Subscription
        from smoothschedule.billing.services.invoicing import (
            generate_invoice_for_subscription,
        )

        shared_tenant = clean_tenant_subscription
        unique_id = str(uuid.uuid4())[:8]

        # Create plan and subscription
        plan = Plan.objects.create(code=f"pro_{unique_id}", name="Pro")
        pv = PlanVersion.objects.create(
            plan=plan, version=1, name="Pro Plan v1", price_monthly_cents=2999
        )
        now = timezone.now()
        subscription = Subscription.objects.create(
            business=shared_tenant,
            plan_version=pv,
            status="active",
            current_period_start=now,
            current_period_end=now + timedelta(days=30),
        )

        period_start = now
        period_end = now + timedelta(days=30)

        invoice = generate_invoice_for_subscription(
            subscription, period_start, period_end
        )

        # Verify snapshot values
        assert invoice.plan_code_at_billing == f"pro_{unique_id}"
        assert invoice.plan_name_at_billing == "Pro Plan v1"
        assert invoice.plan_version_id_at_billing == pv.id

    def test_creates_line_item_for_base_plan(self, clean_tenant_subscription):
        """Invoice should have a line item for the base plan subscription."""
        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanVersion
        from smoothschedule.billing.models import Subscription
        from smoothschedule.billing.services.invoicing import (
            generate_invoice_for_subscription,
        )

        shared_tenant = clean_tenant_subscription

        plan = Plan.objects.create(code="starter_line", name="Starter")
        pv = PlanVersion.objects.create(
            plan=plan, version=1, name="Starter Plan", price_monthly_cents=999
        )
        now = timezone.now()
        subscription = Subscription.objects.create(
            business=shared_tenant,
            plan_version=pv,
            status="active",
            current_period_start=now,
            current_period_end=now + timedelta(days=30),
        )

        period_start = now
        period_end = now + timedelta(days=30)

        invoice = generate_invoice_for_subscription(
            subscription, period_start, period_end
        )

        # Verify line item for plan
        lines = list(invoice.lines.all())
        assert len(lines) == 1
        assert lines[0].line_type == "plan"
        assert lines[0].unit_amount == 999
        assert "Starter Plan" in lines[0].description

    def test_creates_line_items_for_active_addons(self, clean_tenant_subscription):
        """Invoice should have line items for each active add-on."""
        from smoothschedule.billing.models import AddOnProduct
        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanVersion
        from smoothschedule.billing.models import Subscription
        from smoothschedule.billing.models import SubscriptionAddOn
        from smoothschedule.billing.services.invoicing import (
            generate_invoice_for_subscription,
        )

        shared_tenant = clean_tenant_subscription

        plan = Plan.objects.create(code="pro_addon", name="Pro")
        pv = PlanVersion.objects.create(
            plan=plan, version=1, name="Pro Plan", price_monthly_cents=2999
        )
        addon = AddOnProduct.objects.create(
            code="sms_pack", name="SMS Pack", price_monthly_cents=500
        )
        now = timezone.now()
        subscription = Subscription.objects.create(
            business=shared_tenant,
            plan_version=pv,
            status="active",
            current_period_start=now,
            current_period_end=now + timedelta(days=30),
        )
        SubscriptionAddOn.objects.create(
            subscription=subscription, addon=addon, status="active"
        )

        period_start = now
        period_end = now + timedelta(days=30)

        invoice = generate_invoice_for_subscription(
            subscription, period_start, period_end
        )

        # Should have 2 lines - plan and add-on
        lines = list(invoice.lines.all())
        assert len(lines) == 2

        addon_line = [l for l in lines if l.line_type == "addon"][0]
        assert addon_line.unit_amount == 500
        assert "SMS Pack" in addon_line.description

    def test_calculates_totals_correctly(self, clean_tenant_subscription):
        """Invoice totals should be calculated from line items."""
        from smoothschedule.billing.models import AddOnProduct
        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanVersion
        from smoothschedule.billing.models import Subscription
        from smoothschedule.billing.models import SubscriptionAddOn
        from smoothschedule.billing.services.invoicing import (
            generate_invoice_for_subscription,
        )

        shared_tenant = clean_tenant_subscription

        plan = Plan.objects.create(code="pro_totals", name="Pro")
        pv = PlanVersion.objects.create(
            plan=plan, version=1, name="Pro Plan", price_monthly_cents=2999
        )
        addon = AddOnProduct.objects.create(
            code="sms_pack_totals", name="SMS Pack", price_monthly_cents=500
        )
        now = timezone.now()
        subscription = Subscription.objects.create(
            business=shared_tenant,
            plan_version=pv,
            status="active",
            current_period_start=now,
            current_period_end=now + timedelta(days=30),
        )
        SubscriptionAddOn.objects.create(
            subscription=subscription, addon=addon, status="active"
        )

        period_start = now
        period_end = now + timedelta(days=30)

        invoice = generate_invoice_for_subscription(
            subscription, period_start, period_end
        )

        # Total should be plan + addon = 2999 + 500 = 3499 cents
        assert invoice.subtotal_amount == 3499
        assert invoice.total_amount == 3499

    def test_skips_inactive_addons(self, clean_tenant_subscription):
        """Inactive add-ons should not be included in the invoice."""
        from smoothschedule.billing.models import AddOnProduct
        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanVersion
        from smoothschedule.billing.models import Subscription
        from smoothschedule.billing.models import SubscriptionAddOn
        from smoothschedule.billing.services.invoicing import (
            generate_invoice_for_subscription,
        )

        shared_tenant = clean_tenant_subscription

        plan = Plan.objects.create(code="pro_inactive", name="Pro")
        pv = PlanVersion.objects.create(
            plan=plan, version=1, name="Pro Plan", price_monthly_cents=2999
        )
        addon = AddOnProduct.objects.create(
            code="sms_pack_inactive", name="SMS Pack", price_monthly_cents=500
        )
        now = timezone.now()
        subscription = Subscription.objects.create(
            business=shared_tenant,
            plan_version=pv,
            status="active",
            current_period_start=now,
            current_period_end=now + timedelta(days=30),
        )
        # Create canceled add-on
        SubscriptionAddOn.objects.create(
            subscription=subscription, addon=addon, status="canceled"
        )

        period_start = now
        period_end = now + timedelta(days=30)

        invoice = generate_invoice_for_subscription(
            subscription, period_start, period_end
        )

        # Should only have plan line, not the canceled add-on
        lines = list(invoice.lines.all())
        assert len(lines) == 1
        assert lines[0].line_type == "plan"


# =============================================================================
# Invoice Immutability Tests (Database required)
# =============================================================================


@pytest.mark.django_db
class TestInvoiceImmutability:
    """Tests that verify invoice immutability."""

    def test_changing_plan_price_does_not_affect_existing_invoice(
        self, clean_tenant_subscription
    ):
        """
        Changing a PlanVersion's price should NOT affect existing invoices.
        This verifies the snapshot design.
        """
        from smoothschedule.billing.models import Invoice
        from smoothschedule.billing.models import InvoiceLine
        from smoothschedule.billing.models import Plan
        from smoothschedule.billing.models import PlanVersion
        from smoothschedule.billing.models import Subscription

        shared_tenant = clean_tenant_subscription

        # Create plan and subscription
        plan = Plan.objects.create(code="test_immutable", name="Test Plan")
        pv = PlanVersion.objects.create(
            plan=plan,
            version=1,
            name="Test Plan v1",
            price_monthly_cents=1000,  # $10.00
        )

        now = timezone.now()
        subscription = Subscription.objects.create(
            business=shared_tenant,
            plan_version=pv,
            status="active",
            current_period_start=now,
            current_period_end=now + timedelta(days=30),
        )

        # Create invoice with snapshot
        invoice = Invoice.objects.create(
            business=shared_tenant,
            subscription=subscription,
            period_start=now,
            period_end=now + timedelta(days=30),
            plan_code_at_billing="test_immutable",
            plan_name_at_billing="Test Plan v1",
            plan_version_id_at_billing=pv.id,
            subtotal_amount=1000,
            total_amount=1000,
            status="paid",
        )

        # Create line item
        InvoiceLine.objects.create(
            invoice=invoice,
            line_type="plan",
            description="Test Plan v1",
            quantity=1,
            unit_amount=1000,
            subtotal_amount=1000,
            total_amount=1000,
            plan_version=pv,
        )

        # Now change the plan price
        pv.price_monthly_cents = 2000  # Changed to $20.00!
        pv.save()

        # Refresh invoice from DB
        invoice.refresh_from_db()

        # Invoice should still show the original amounts
        assert invoice.subtotal_amount == 1000
        assert invoice.total_amount == 1000
        assert invoice.plan_name_at_billing == "Test Plan v1"

        # Line item should also be unchanged
        line = invoice.lines.first()
        assert line.unit_amount == 1000
        assert line.total_amount == 1000
