from datetime import timedelta
from types import SimpleNamespace

from django.apps import apps
from django.contrib.auth import get_user_model
from django.test import TestCase, override_settings, tag
from django.utils import timezone
from unittest.mock import patch

from billing.addons import AddonEntitlementService
from constants.plans import PlanNames
from util.subscription_helper import get_user_max_contacts_per_agent


@tag("batch_billing")
class AddonEntitlementSyncTests(TestCase):
    def setUp(self):
        User = get_user_model()
        self.user = User.objects.create_user(username="sync-user", email="sync@example.com")
        self.period_start = timezone.now()
        self.period_end = self.period_start + timedelta(days=30)

    @patch("billing.addons.get_stripe_settings")
    def test_sync_creates_entitlements_and_task_credits(self, mock_settings):
        mock_settings.return_value = SimpleNamespace(
            startup_task_pack_price_ids=("price_task",),
            startup_contact_cap_price_ids=("price_contact",),
        )

        items = [
            {
                "price": {
                    "id": "price_task",
                    "product": "prod_task",
                    "metadata": {"task_credits_delta": "250"},
                },
                "quantity": 2,
            },
            {
                "price": {
                    "id": "price_contact",
                    "product": "prod_contact",
                    "metadata": {"contact_cap_delta": "5"},
                },
                "quantity": 1,
            },
        ]

        AddonEntitlementService.sync_subscription_entitlements(
            owner=self.user,
            owner_type="user",
            plan_id=PlanNames.STARTUP,
            subscription_items=items,
            period_start=self.period_start,
            period_end=self.period_end,
            created_via="test_sync",
        )

        entitlements = AddonEntitlementService.get_active_entitlements(self.user)
        self.assertEqual(entitlements.count(), 2)

        task_entitlement = entitlements.get(price_id="price_task")
        self.assertEqual(task_entitlement.quantity, 2)
        self.assertEqual(task_entitlement.task_credits_delta, 250)
        self.assertEqual(task_entitlement.expires_at, self.period_end)

        contact_entitlement = entitlements.get(price_id="price_contact")
        self.assertEqual(contact_entitlement.contact_cap_delta, 5)
        self.assertEqual(contact_entitlement.quantity, 1)

        TaskCredit = apps.get_model("api", "TaskCredit")
        addon_blocks = TaskCredit.objects.filter(
            user=self.user, stripe_invoice_id__startswith="addon:price_task"
        )
        self.assertEqual(addon_blocks.count(), 1)
        self.assertEqual(int(addon_blocks.first().credits), 500)
        self.assertEqual(addon_blocks.first().grant_type, "task_pack")

        # Remove contact pack and ensure it expires
        AddonEntitlementService.sync_subscription_entitlements(
            owner=self.user,
            owner_type="user",
            plan_id=PlanNames.STARTUP,
            subscription_items=[items[0]],
            period_start=self.period_start,
            period_end=self.period_end,
            created_via="test_sync",
        )
        contact_entitlement.refresh_from_db()
        self.assertLessEqual(contact_entitlement.expires_at, timezone.now())

    @patch("billing.addons.get_stripe_settings")
    def test_contact_pack_delta_defaults_to_zero_without_metadata(self, mock_settings):
        mock_settings.return_value = SimpleNamespace(
            startup_contact_cap_price_ids=("price_contact",),
        )

        items = [
            {
                "price": {
                    "id": "price_contact",
                    "product": "prod_contact",
                    "metadata": {},
                },
                "quantity": 2,
            },
        ]

        AddonEntitlementService.sync_subscription_entitlements(
            owner=self.user,
            owner_type="user",
            plan_id=PlanNames.STARTUP,
            subscription_items=items,
            period_start=self.period_start,
            period_end=self.period_end,
            created_via="test_sync",
        )

        ent = AddonEntitlementService.get_active_entitlements(self.user, "price_contact").first()
        self.assertIsNotNone(ent)
        self.assertEqual(ent.contact_cap_delta, 0)
        self.assertEqual(ent.quantity, 2)

    @patch("billing.addons.get_stripe_settings")
    def test_sync_handles_multiple_contact_pack_prices(self, mock_settings):
        mock_settings.return_value = SimpleNamespace(
            startup_task_pack_price_ids=(),
            startup_contact_cap_price_ids=("price_contact_small", "price_contact_large"),
        )

        items = [
            {
                "price": {
                    "id": "price_contact_small",
                    "product": "prod_contact_small",
                    "metadata": {"contact_cap_delta": "20"},
                },
                "quantity": 1,
            },
            {
                "price": {
                    "id": "price_contact_large",
                    "product": "prod_contact_large",
                    "metadata": {"contact_cap_delta": "50"},
                },
                "quantity": 2,
            },
        ]

        AddonEntitlementService.sync_subscription_entitlements(
            owner=self.user,
            owner_type="user",
            plan_id=PlanNames.STARTUP,
            subscription_items=items,
            period_start=self.period_start,
            period_end=self.period_end,
            created_via="test_sync",
        )

        small = AddonEntitlementService.get_active_entitlements(self.user, "price_contact_small").first()
        large = AddonEntitlementService.get_active_entitlements(self.user, "price_contact_large").first()

        self.assertIsNotNone(small)
        self.assertEqual(small.contact_cap_delta, 20)
        self.assertEqual(small.quantity, 1)

        self.assertIsNotNone(large)
        self.assertEqual(large.contact_cap_delta, 50)
        self.assertEqual(large.quantity, 2)

    @patch("billing.addons.get_stripe_settings")
    def test_sync_sets_browser_task_daily_delta(self, mock_settings):
        mock_settings.return_value = SimpleNamespace(
            startup_browser_task_limit_price_ids=("price_browser",),
        )

        items = [
            {
                "price": {
                    "id": "price_browser",
                    "product": "prod_browser",
                    "metadata": {"browser_task_daily_delta": "4"},
                },
                "quantity": 3,
            },
        ]

        AddonEntitlementService.sync_subscription_entitlements(
            owner=self.user,
            owner_type="user",
            plan_id=PlanNames.STARTUP,
            subscription_items=items,
            period_start=self.period_start,
            period_end=self.period_end,
            created_via="test_sync",
        )

        ent = AddonEntitlementService.get_active_entitlements(self.user, "price_browser").first()
        self.assertIsNotNone(ent)
        self.assertEqual(ent.browser_task_daily_delta, 4)
        self.assertEqual(ent.quantity, 3)
        self.assertEqual(AddonEntitlementService.get_browser_task_daily_uplift(self.user), 12)

    @patch("billing.addons.get_stripe_settings")
    def test_sync_enables_advanced_captcha_without_metadata(self, mock_settings):
        mock_settings.return_value = SimpleNamespace(
            startup_advanced_captcha_resolution_price_id="price_captcha",
        )

        items = [
            {
                "price": {
                    "id": "price_captcha",
                    "product": "prod_captcha",
                    "metadata": {},
                },
                "quantity": 1,
            },
        ]

        AddonEntitlementService.sync_subscription_entitlements(
            owner=self.user,
            owner_type="user",
            plan_id=PlanNames.STARTUP,
            subscription_items=items,
            period_start=self.period_start,
            period_end=self.period_end,
            created_via="test_sync",
        )

        ent = AddonEntitlementService.get_active_entitlements(self.user, "price_captcha").first()
        self.assertIsNotNone(ent)
        self.assertEqual(ent.advanced_captcha_resolution_delta, 1)
        self.assertTrue(AddonEntitlementService.has_advanced_captcha_resolution(self.user))

    @patch("billing.addons.get_stripe_settings")
    def test_sync_expires_entitlements_when_prices_missing(self, mock_settings):
        mock_settings.return_value = SimpleNamespace(
            startup_task_pack_price_ids=(),
            startup_contact_cap_price_ids=(),
            startup_browser_task_limit_price_ids=(),
        )

        AddonEntitlement = apps.get_model("api", "AddonEntitlement")
        ent = AddonEntitlement.objects.create(
            user=self.user,
            price_id="legacy_price",
            quantity=1,
            task_credits_delta=100,
            starts_at=self.period_start - timedelta(days=1),
            is_recurring=True,
        )

        AddonEntitlementService.sync_subscription_entitlements(
            owner=self.user,
            owner_type="user",
            plan_id=PlanNames.STARTUP,
            subscription_items=[],
            period_start=self.period_start,
            period_end=self.period_end,
            created_via="test_sync",
        )

        ent.refresh_from_db()
        self.assertIsNotNone(ent.expires_at)
        self.assertLessEqual(ent.expires_at, timezone.now())


@tag("batch_billing")
@override_settings(GOBII_PROPRIETARY_MODE=True)
class AddonContactCapTests(TestCase):
    def setUp(self):
        User = get_user_model()
        self.user = User.objects.create_user(username="cap-user", email="cap@example.com")

    def test_contact_cap_addon_applies_on_billing_override(self):
        AddonEntitlement = apps.get_model("api", "AddonEntitlement")
        AddonEntitlement.objects.create(
            user=self.user,
            price_id="price_contact",
            quantity=1,
            contact_cap_delta=7,
            starts_at=timezone.now() - timedelta(days=1),
            expires_at=timezone.now() + timedelta(days=10),
            is_recurring=True,
        )

        UserBilling = apps.get_model("api", "UserBilling")
        billing, _ = UserBilling.objects.get_or_create(user=self.user, defaults={"max_contacts_per_agent": 10})
        billing.max_contacts_per_agent = 10
        billing.save(update_fields=["max_contacts_per_agent"])

        self.assertEqual(get_user_max_contacts_per_agent(self.user), 17)
