"""Admin domain - business logic.

Manages subscription plans: CRUD locally + sync to Stripe.
When a plan is created or price is updated, the service calls Stripe
to create/update the corresponding Product and Price objects.
Prices are append-only (plan_price_versions); plans reference current version.
"""
import structlog
from uuid import UUID

from src.core.exceptions import ConflictError, NotFoundError
from src.domain.admin.repository import PlanPriceVersionRepository, SubscriptionPlanRepository

logger = structlog.get_logger()


class AdminPlanService:
    def __init__(
        self,
        plan_repo: SubscriptionPlanRepository,
        price_version_repo: PlanPriceVersionRepository,
        stripe_client=None,
    ):
        self._plan_repo = plan_repo
        self._price_version_repo = price_version_repo
        self._stripe = stripe_client

    async def list_plans(self, active_only: bool = False):
        return await self._plan_repo.list_all(active_only=active_only)

    async def get_plan(self, plan_id: UUID):
        plan = await self._plan_repo.get_by_id(plan_id)
        if not plan:
            raise NotFoundError("Plan not found")
        return plan

    async def create_plan(self, data: dict):
        existing = await self._plan_repo.get_by_slug(data["slug"])
        if existing:
            raise ConflictError(f"Plan with slug '{data['slug']}' already exists")

        stripe_product_id = None
        stripe_monthly_price_id = None
        stripe_yearly_price_id = None
        metered_stripe_price_id = None

        if self._stripe:
            product = await self._stripe.create_product(
                name=data["name"],
                description=data.get("description"),
                metadata={"p2hs_slug": data["slug"]},
            )
            stripe_product_id = product["id"]

            monthly_price = await self._stripe.create_price(
                product_id=stripe_product_id,
                unit_amount=data["price_monthly"],
                currency=data.get("currency", "usd"),
                interval="month",
                nickname=f"{data['name']} - Monthly",
            )
            stripe_monthly_price_id = monthly_price["id"]

            if data.get("price_yearly"):
                yearly_price = await self._stripe.create_price(
                    product_id=stripe_product_id,
                    unit_amount=data["price_yearly"],
                    currency=data.get("currency", "usd"),
                    interval="year",
                    nickname=f"{data['name']} - Yearly",
                )
                stripe_yearly_price_id = yearly_price["id"]

            if data.get("includes_metered_billing"):
                metered_price = await self._stripe.create_metered_price(
                    product_id=stripe_product_id,
                    currency=data.get("currency", "usd"),
                    unit_label=data.get("metered_unit_label", "unit"),
                    nickname=f"{data['name']} - Usage",
                )
                metered_stripe_price_id = metered_price["id"]

            logger.info(
                "admin_plan_synced_to_stripe",
                slug=data["slug"],
                product=stripe_product_id,
            )

        features = data.get("features")
        if features and hasattr(features, "model_dump"):
            features = features.model_dump(exclude_none=True)

        limits = data.get("limits")
        if limits and hasattr(limits, "model_dump"):
            limits = limits.model_dump(exclude_none=True)

        plan = await self._plan_repo.create(
            name=data["name"],
            slug=data["slug"],
            description=data.get("description"),
            price_monthly=data["price_monthly"],
            price_yearly=data.get("price_yearly"),
            currency=data.get("currency", "usd"),
            stripe_product_id=stripe_product_id,
            stripe_monthly_price_id=stripe_monthly_price_id,
            stripe_yearly_price_id=stripe_yearly_price_id,
            features=features,
            limits=limits,
            sort_order=data.get("sort_order", 0),
            trial_days=data.get("trial_days", 0),
            includes_metered_billing=data.get("includes_metered_billing", False),
            metered_stripe_price_id=metered_stripe_price_id,
            metered_unit_label=data.get("metered_unit_label"),
        )

        # Append-only price versions (plans reference current version)
        version_updates = {}
        if stripe_monthly_price_id:
            v = await self._price_version_repo.create(
                subscription_plan_id=plan.id,
                interval="month",
                stripe_price_id=stripe_monthly_price_id,
                price_minor=data["price_monthly"],
                currency=data.get("currency", "usd"),
                stripe_product_id=stripe_product_id,
            )
            version_updates["current_monthly_price_version_id"] = v.id
        if stripe_yearly_price_id:
            v = await self._price_version_repo.create(
                subscription_plan_id=plan.id,
                interval="year",
                stripe_price_id=stripe_yearly_price_id,
                price_minor=data.get("price_yearly", 0),
                currency=data.get("currency", "usd"),
                stripe_product_id=stripe_product_id,
            )
            version_updates["current_yearly_price_version_id"] = v.id
        if version_updates:
            plan = await self._plan_repo.update(plan.id, **version_updates) or plan

        logger.info("admin_plan_created", plan_id=str(plan.id), slug=plan.slug)
        return plan

    async def update_plan(self, plan_id: UUID, data: dict):
        plan = await self._plan_repo.get_by_id(plan_id)
        if not plan:
            raise NotFoundError("Plan not found")

        price_changed = (
            data.get("price_monthly") is not None and data["price_monthly"] != plan.price_monthly
        ) or (
            data.get("price_yearly") is not None and data["price_yearly"] != plan.price_yearly
        )

        if price_changed and self._stripe and plan.stripe_product_id:
            if data.get("price_monthly") is not None and data["price_monthly"] != plan.price_monthly:
                new_price = await self._stripe.create_price(
                    product_id=plan.stripe_product_id,
                    unit_amount=data["price_monthly"],
                    currency=plan.currency,
                    interval="month",
                    nickname=f"{data.get('name', plan.name)} - Monthly",
                )
                data["stripe_monthly_price_id"] = new_price["id"]

                if plan.stripe_monthly_price_id:
                    await self._stripe.deactivate_price(plan.stripe_monthly_price_id)

                # Append-only: create new version, set as current
                v = await self._price_version_repo.create(
                    subscription_plan_id=plan.id,
                    interval="month",
                    stripe_price_id=new_price["id"],
                    price_minor=data["price_monthly"],
                    currency=plan.currency,
                    stripe_product_id=plan.stripe_product_id,
                )
                await self._price_version_repo.set_current_exclusive(plan.id, "month", v.id)
                data["current_monthly_price_version_id"] = v.id

            if data.get("price_yearly") is not None and data["price_yearly"] != plan.price_yearly:
                new_price = await self._stripe.create_price(
                    product_id=plan.stripe_product_id,
                    unit_amount=data["price_yearly"],
                    currency=plan.currency,
                    interval="year",
                    nickname=f"{data.get('name', plan.name)} - Yearly",
                )
                data["stripe_yearly_price_id"] = new_price["id"]

                if plan.stripe_yearly_price_id:
                    await self._stripe.deactivate_price(plan.stripe_yearly_price_id)

                # Append-only: create new version, set as current
                v = await self._price_version_repo.create(
                    subscription_plan_id=plan.id,
                    interval="year",
                    stripe_price_id=new_price["id"],
                    price_minor=data["price_yearly"],
                    currency=plan.currency,
                    stripe_product_id=plan.stripe_product_id,
                )
                await self._price_version_repo.set_current_exclusive(plan.id, "year", v.id)
                data["current_yearly_price_version_id"] = v.id

            logger.info("admin_plan_prices_updated_in_stripe", plan_id=str(plan_id))

        if self._stripe and plan.stripe_product_id:
            update_fields = {}
            if data.get("name"):
                update_fields["name"] = data["name"]
            if data.get("description") is not None:
                update_fields["description"] = data["description"]
            if data.get("is_active") is not None:
                update_fields["active"] = data["is_active"]
            if update_fields:
                await self._stripe.update_product(plan.stripe_product_id, **update_fields)

        features = data.get("features")
        if features and hasattr(features, "model_dump"):
            data["features"] = features.model_dump(exclude_none=True)

        limits = data.get("limits")
        if limits and hasattr(limits, "model_dump"):
            data["limits"] = limits.model_dump(exclude_none=True)

        updated = await self._plan_repo.update(plan_id, **data)
        logger.info("admin_plan_updated", plan_id=str(plan_id))
        return updated
