"""Billing catalog resolver - DB as source of truth for plans and packages."""
from __future__ import annotations
from typing import Optional
from uuid import UUID

from src.core.exceptions import NotFoundError
from src.domain.admin.models import SubscriptionPlan
from src.domain.admin.repository import PlanPriceVersionRepository, SubscriptionPlanRepository
from src.domain.billing.models import UsageCreditPackage
from src.domain.billing.repository import UsageCreditPackageRepository


class BillingCatalogService:
    """
    Resolves subscription plans and usage packages from DB.
    Replaces scattered ad hoc lookups with a single normalized interface.
    """

    def __init__(
        self,
        plan_repo: SubscriptionPlanRepository,
        package_repo: UsageCreditPackageRepository,
        price_version_repo: Optional[PlanPriceVersionRepository] = None,
    ):
        self._plan_repo = plan_repo
        self._package_repo = package_repo
        self._price_version_repo = price_version_repo

    async def list_plans(self, active_only: bool = True) -> list[SubscriptionPlan]:
        """List subscription plans, optionally filtered to active only."""
        return await self._plan_repo.list_all(active_only=active_only)

    async def get_plan_by_slug(self, slug: str) -> SubscriptionPlan:
        plan = await self._plan_repo.get_by_slug(slug)
        if not plan:
            raise NotFoundError(f"Plan '{slug}' not found")
        if not plan.is_active:
            raise NotFoundError(f"Plan '{slug}' is not active")
        return plan

    async def get_plan_by_stripe_price_id(self, stripe_price_id: str) -> SubscriptionPlan:
        plan = await self._plan_repo.get_by_stripe_price_id(stripe_price_id)
        if not plan and self._price_version_repo:
            pv = await self._price_version_repo.get_by_stripe_price_id(stripe_price_id)
            if pv:
                plan = await self._plan_repo.get_by_id(pv.subscription_plan_id)
        if not plan:
            raise NotFoundError(f"No plan found for Stripe price {stripe_price_id}")
        if not plan.is_active:
            raise NotFoundError(f"Plan for price {stripe_price_id} is not active")
        return plan

    def resolve_stripe_price_id_for_plan(self, plan: SubscriptionPlan, interval: str) -> str:
        """Return Stripe price id for the given billing interval."""
        if interval == "month":
            price_id = plan.stripe_monthly_price_id
        elif interval == "year":
            price_id = plan.stripe_yearly_price_id
        else:
            raise ValueError(f"Unsupported billing interval: {interval}")
        if not price_id:
            raise NotFoundError(f"Plan '{plan.slug}' has no {interval}ly price configured")
        return price_id

    def get_plan_limits(self, plan: SubscriptionPlan) -> dict:
        """Normalized access to plan limits."""
        return plan.limits or {}

    def get_plan_features(self, plan: SubscriptionPlan) -> dict:
        """Normalized access to plan features."""
        return plan.features or {}

    async def get_package_by_slug(self, slug: str) -> UsageCreditPackage:
        pkg = await self._package_repo.get_by_slug(slug)
        if not pkg:
            raise NotFoundError(f"Usage package '{slug}' not found")
        if not pkg.is_active:
            raise NotFoundError(f"Usage package '{slug}' is not active")
        return pkg

    async def get_package_by_stripe_price_id(self, stripe_price_id: str) -> UsageCreditPackage:
        pkg = await self._package_repo.get_by_stripe_price_id(stripe_price_id)
        if not pkg:
            raise NotFoundError(f"No usage package found for Stripe price {stripe_price_id}")
        if not pkg.is_active:
            raise NotFoundError(f"Usage package for price {stripe_price_id} is not active")
        return pkg
