"""Runtime AI checkout, Stripe fulfillment, and package sync."""
from __future__ import annotations

from datetime import datetime, timezone
from typing import Optional
from uuid import UUID

import structlog
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.exceptions import NotFoundError
from src.domain.billing.interfaces import StripeClient, StripeSyncCapable
from src.domain.runtime_ai.catalog import DISPLAY_SCALE
from src.domain.runtime_ai.repository import (
    RuntimeAiLedgerRepository,
    RuntimeAiPackageRepository,
    RuntimeAiPurchaseRepository,
)
from src.domain.runtime_ai.wallet import RuntimeAiWallet

logger = structlog.get_logger()

PURCHASE_TYPE = "runtime_ai_topup"


class RuntimeAiService:
    def __init__(
        self,
        *,
        db: AsyncSession,
        stripe_client: Optional[StripeClient] = None,
        ensure_customer=None,
    ) -> None:
        self._db = db
        self._packages = RuntimeAiPackageRepository(db)
        self._purchases = RuntimeAiPurchaseRepository(db)
        self._wallet = RuntimeAiWallet(RuntimeAiLedgerRepository(db))
        self._stripe = stripe_client
        self._ensure_customer = ensure_customer

    async def list_packages(self):
        return await self._packages.list_active()

    async def balance_millicredits(self, workspace_id: UUID) -> int:
        return await self._wallet.balance_millicredits(workspace_id)

    async def start_checkout(
        self,
        *,
        workspace_id: UUID,
        user_id: UUID,
        email: str,
        workspace_name: str,
        package_slug: str,
        success_url: str,
        cancel_url: str,
        idempotency_key: Optional[str] = None,
    ) -> dict:
        pkg = await self._packages.get_by_slug(package_slug)
        if not pkg or not pkg.is_active:
            raise NotFoundError(f"Runtime AI package '{package_slug}' not found")
        if not pkg.stripe_price_id:
            raise NotFoundError(f"Runtime AI package '{package_slug}' is not synced to Stripe")
        if not self._stripe or not self._ensure_customer:
            raise NotFoundError("Stripe client not configured")
        customer_id = await self._ensure_customer(workspace_id, user_id, email, workspace_name)
        metadata = {
            "workspace_id": str(workspace_id),
            "package_slug": package_slug,
            "purchase_type": PURCHASE_TYPE,
        }
        url, session_id = await self._stripe.create_payment_checkout(
            customer_id, pkg.stripe_price_id, success_url, cancel_url, metadata, idempotency_key
        )
        return {"checkout_url": url, "session_id": session_id, "workspace_id": workspace_id}

    async def fulfill_paid_session(
        self,
        *,
        workspace_id: UUID,
        stripe_customer_id: str,
        session_id: str,
        payment_intent_id: Optional[str],
        package_slug: str,
    ) -> None:
        existing = await self._purchases.get_by_session_id(session_id)
        if existing and existing.status == "paid":
            logger.info("runtime_ai_topup_already_fulfilled", session_id=session_id)
            return
        pkg = await self._packages.get_by_slug(package_slug)
        if not pkg:
            logger.warning("runtime_ai_topup_package_missing", slug=package_slug, session_id=session_id)
            return
        millicredits = pkg.credits * DISPLAY_SCALE
        purchase = None
        try:
            with self._db.begin_nested():
                purchase = await self._purchases.create(
                    workspace_id=workspace_id,
                    stripe_customer_id=stripe_customer_id,
                    stripe_checkout_session_id=session_id,
                    stripe_payment_intent_id=payment_intent_id,
                    package_id=pkg.id,
                    package_slug=pkg.slug,
                    credits_purchased=pkg.credits,
                    millicredits_purchased=millicredits,
                    amount_minor=pkg.price_minor,
                    currency=pkg.currency,
                    status="paid",
                    paid_at=datetime.now(timezone.utc),
                )
        except IntegrityError:
            existing = await self._purchases.get_by_session_id(session_id)
            if existing and existing.status == "paid":
                return
            raise
        await self._wallet.credit_purchase(
            workspace_id=workspace_id,
            millicredits=millicredits,
            purchase_id=purchase.id,
            source_id=session_id,
            description=f"Purchased {pkg.credits} Runtime AI credits ({pkg.name})",
        )
        logger.info(
            "runtime_ai_topup_fulfilled",
            workspace_id=str(workspace_id),
            session_id=session_id,
            credits=pkg.credits,
        )

    async def sync_packages_to_stripe(self, stripe: StripeSyncCapable) -> None:
        for pkg in await self._packages.list_active():
            updates: dict = {}
            if not pkg.stripe_product_id:
                product = await stripe.create_product(
                    name=pkg.name,
                    description=pkg.description or "",
                    metadata={"p2hs_product": "runtime_ai", "p2hs_slug": pkg.slug, "p2hs_credits": str(pkg.credits)},
                )
                updates["stripe_product_id"] = product["id"]
            product_id = pkg.stripe_product_id or updates.get("stripe_product_id")
            if not product_id:
                continue
            if not pkg.stripe_price_id:
                price = await stripe.create_one_time_price(
                    product_id=product_id,
                    unit_amount=pkg.price_minor,
                    currency=pkg.currency,
                    nickname=f"{pkg.name}",
                )
                updates["stripe_price_id"] = price["id"]
            if updates:
                await self._packages.update(pkg.id, **updates)
