"""Recover Stripe outcomes before attempting additional financial actions."""

from datetime import timedelta
from urllib.parse import urlsplit

from src.core.exceptions import ForbiddenError, P2HSError
from src.domain.agent_access.service import utcnow
from src.domain.billing.agent_purchases import AgentPurchaseService


class AgentPurchaseProcessor:
    def __init__(self, access, stripe):
        self.access = access
        self.stripe = stripe
        self.purchases = AgentPurchaseService(access)

    async def process(self, operation, grant):
        snapshot = operation.arguments
        checkout = operation.action == "create_credit_checkout"
        key = str(operation.id)
        provider_id = snapshot.get("provider_id")
        payment = None
        # Reconcile after revocation too: paid credits belong to the customer.
        if provider_id:
            payment = await (
                self.stripe.retrieve_checkout(provider_id)
                if checkout
                else self.stripe.retrieve_intent(provider_id)
            )
            paid = (
                payment.get("payment_status") == "paid"
                if checkout
                else payment.get("status") == "succeeded"
            )
            if paid:
                await self.purchases.fulfill(operation, payment, checkout=checkout)
                return
            if payment.get("status") in ("expired", "canceled"):
                operation.status = "canceled"
                operation.result = {"error": "Payment canceled"}
                return
        try:
            if not getattr(self.access.settings, "agent_access_enabled", True):
                raise ForbiddenError("Agent access disabled")
            await self.access.require_scope(
                grant, "credits.checkout" if checkout else "credits.purchase"
            )
        except P2HSError:
            if provider_id:
                if checkout:
                    await self.stripe.expire_checkout(provider_id, key)
                else:
                    await self.stripe.cancel_intent(provider_id, key)
            operation.status = "canceled"
            operation.result = {"error": "Purchase authorization was revoked or expired"}
            return
        if not provider_id:
            # Stripe may prune idempotency records after 24 hours. Never blindly recreate afterward.
            if utcnow() - operation.created_at > timedelta(hours=23):
                operation.status = "manual_review"
                operation.result = {"error": "Purchase needs reconciliation in P2HS support"}
                return
            if checkout:
                payment = await self.stripe.create_checkout(
                    snapshot, key, self.access.settings.app_url.rstrip("/")
                )
                url = payment.get("url", "")
                if (
                    urlsplit(url).scheme != "https"
                    or urlsplit(url).hostname != "checkout.stripe.com"
                ):
                    # Persist the provider ID even if Stripe returns an unexpected link.
                    operation.arguments = {**snapshot, "provider_id": payment["id"]}
                    operation.status = "manual_review"
                    return
                operation.arguments = {**snapshot, "provider_id": payment["id"]}
                operation.status = "awaiting_payment"
                operation.result = {"checkout_url": url}
                return
            if not snapshot.get("payment_method"):
                customer = await self.stripe.customer(snapshot["customer_id"])
                method = (customer.get("invoice_settings") or {}).get("default_payment_method")
                if not isinstance(method, str) or not method.startswith("pm_"):
                    operation.status = "failed"
                    operation.result = {
                        "error": "Set a default saved card in dashboard billing or use checkout"
                    }
                    return
                # Persist the method first so intent retries use identical parameters.
                operation.arguments = {**snapshot, "payment_method": method}
                return
            payment = await self.stripe.create_intent(snapshot, key)
            operation.arguments = {**snapshot, "provider_id": payment["id"]}
            operation.status = "awaiting_payment"
            return
        if checkout:
            return
        status = payment.get("status")
        if status == "requires_confirmation":
            if utcnow() - operation.created_at > timedelta(hours=23):
                operation.status = "manual_review"
                return
            # Persist intent identity before charging. Retrieve uncertain outcomes next tick.
            payment = await self.stripe.confirm_intent(provider_id, key)
            await self.purchases.fulfill(operation, payment, checkout=False)
        elif status in ("requires_action", "requires_payment_method"):
            # Cancel before releasing the cap; no client_secret is exposed to agents.
            await self.stripe.cancel_intent(provider_id, key)
            operation.status = "canceled"
            operation.result = {"error": "Saved card needs attention; use a checkout link"}
