"""Tests for catalog sync (DB → Stripe) — price rotation vs product-only updates."""
from __future__ import annotations

from unittest.mock import AsyncMock, MagicMock
from uuid import uuid4

import pytest

from src.domain.admin.models import SubscriptionPlan
from src.domain.billing.stripe_sync_service import PlanStripeSyncService, UsagePackageStripeSyncService


@pytest.fixture
def plan() -> SubscriptionPlan:
    p = SubscriptionPlan(
        id=uuid4(),
        name="Pro",
        slug="pro",
        description="Pro tier",
        price_monthly=1000,
        price_yearly=None,
        currency="usd",
        stripe_product_id="prod_1",
        stripe_monthly_price_id="price_old",
        stripe_yearly_price_id=None,
        is_active=True,
        includes_metered_billing=False,
    )
    return p


@pytest.mark.asyncio
async def test_sync_plan_rotates_monthly_when_stripe_amount_differs(plan: SubscriptionPlan):
    stripe = AsyncMock()
    stripe.retrieve_price = AsyncMock(
        return_value={
            "id": "price_old",
            "unit_amount": 500,
            "currency": "usd",
            "active": True,
            "recurring": {"interval": "month", "usage_type": "licensed"},
        }
    )
    stripe.create_price = AsyncMock(return_value={"id": "price_new", "unit_amount": 1000})
    stripe.deactivate_price = AsyncMock()
    stripe.update_product = AsyncMock(return_value={"id": "prod_1"})

    plan_repo = AsyncMock()
    plan_repo.update = AsyncMock(side_effect=lambda pid, **kw: plan)

    pv_repo = AsyncMock()
    pv_repo.create = AsyncMock(return_value=MagicMock(id=uuid4()))
    pv_repo.set_current_exclusive = AsyncMock()

    svc = PlanStripeSyncService(plan_repo, stripe, price_version_repo=pv_repo)
    await svc.sync_plan(plan)

    stripe.create_price.assert_awaited_once()
    stripe.deactivate_price.assert_awaited_once_with("price_old")
    plan_repo.update.assert_any_call(plan.id, stripe_monthly_price_id="price_new")


@pytest.mark.asyncio
async def test_sync_plan_no_new_price_when_amount_matches(plan: SubscriptionPlan):
    stripe = AsyncMock()
    stripe.retrieve_price = AsyncMock(
        return_value={
            "id": "price_old",
            "unit_amount": 1000,
            "currency": "usd",
            "active": True,
            "recurring": {"interval": "month", "usage_type": "licensed"},
        }
    )
    stripe.create_price = AsyncMock()
    stripe.update_product = AsyncMock(return_value={"id": "prod_1"})

    plan_repo = AsyncMock()
    svc = PlanStripeSyncService(plan_repo, stripe, price_version_repo=AsyncMock())
    await svc.sync_plan(plan)

    stripe.create_price.assert_not_awaited()
    stripe.update_product.assert_awaited()


@pytest.mark.asyncio
async def test_sync_package_rotates_when_amount_differs():
    from src.domain.billing.models import UsageCreditPackage

    pkg = UsageCreditPackage(
        id=uuid4(),
        slug="pack-60",
        name="60 min",
        units=60,
        price_minor=999,
        currency="usd",
        stripe_product_id="prod_p",
        stripe_price_id="price_old",
    )
    stripe = AsyncMock()
    stripe.retrieve_price = AsyncMock(
        return_value={
            "id": "price_old",
            "unit_amount": 500,
            "currency": "usd",
            "active": True,
            "recurring": None,
        }
    )
    stripe.create_one_time_price = AsyncMock(return_value={"id": "price_new"})
    stripe.deactivate_price = AsyncMock()
    stripe.update_product = AsyncMock(return_value={"id": "prod_p"})

    pkg_repo = AsyncMock()
    pkg_repo.update = AsyncMock(side_effect=lambda pid, **kw: pkg)

    svc = UsagePackageStripeSyncService(pkg_repo, stripe)
    await svc.sync_package(pkg)

    stripe.create_one_time_price.assert_awaited_once()
    stripe.deactivate_price.assert_awaited_once_with("price_old")
