"""Fail-closed provider billing sessions without entitlement authority."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
import hashlib
import hmac
from typing import Mapping, Optional, Protocol
from urllib.parse import urlparse

from sqlalchemy import or_, select
from sqlalchemy.orm import Session
import stripe

from licensing_shared.canonical_json import canonicalize_json
from licensing_shared.catalog import LicensingCatalog, SkuKind
from licensing_shared.constants import validate_identifier

from .constants import DEFAULT_IDEMPOTENCY_DAYS, MAX_IDEMPOTENCY_KEY_CHARACTERS
from .errors import ServerErrorCode, ServerLicensingError
from .models import (
    AuditEvent,
    IdempotencyRecord,
    License,
    Membership,
    Subscription,
    SubscriptionItem,
    User,
)
from .security import new_identifier


STRIPE_PROVIDER_ID = "stripe"
CHECKOUT_IDEMPOTENCY_SCOPE = "account.billing.checkout"
PORTAL_IDEMPOTENCY_SCOPE = "account.billing.portal"
MAXIMUM_PROVIDER_URL_CHARACTERS = 4096
MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS = 256
BILLING_HOLD_STATUSES = frozenset(("dispute_open", "dispute_lost", "refunded"))


def _utc(value: datetime) -> datetime:
    if not isinstance(value, datetime):
        raise TypeError("value must be a datetime")
    if value.tzinfo is None or value.utcoffset() is None:
        return value.replace(tzinfo=timezone.utc)
    return value.astimezone(timezone.utc)


def _bounded_text(value: object, field_name: str, maximum: int) -> str:
    if not isinstance(field_name, str) or not field_name:
        raise ValueError("field_name must be a non-empty string")
    if isinstance(maximum, bool) or not isinstance(maximum, int) or maximum < 1:
        raise ValueError("maximum must be a positive integer")
    if not isinstance(value, str) or not value.strip():
        raise ValueError(f"{field_name} must be a non-empty string")
    normalized = value.strip()
    if len(normalized) > maximum:
        raise ValueError(f"{field_name} exceeds {maximum} characters")
    return normalized


def _https_url(value: object, field_name: str) -> str:
    normalized = _bounded_text(value, field_name, MAXIMUM_PROVIDER_URL_CHARACTERS)
    parsed = urlparse(normalized)
    if (
        parsed.scheme != "https"
        or not parsed.netloc
        or parsed.username is not None
        or parsed.password is not None
        or parsed.fragment
    ):
        raise ValueError(f"{field_name} must be an HTTPS URL without credentials or fragment")
    return normalized


def _provider_value(value: object, field_name: str) -> object:
    if isinstance(value, Mapping):
        return value.get(field_name)
    return getattr(value, field_name, None)


@dataclass(frozen=True)
class ProviderBillingSession:
    session_id: str
    url: str
    expires_at: Optional[datetime] = None

    def __post_init__(self) -> None:
        object.__setattr__(
            self,
            "session_id",
            _bounded_text(
                self.session_id,
                "provider session ID",
                MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
            ),
        )
        object.__setattr__(self, "url", _https_url(self.url, "provider session URL"))
        if self.expires_at is not None:
            object.__setattr__(self, "expires_at", _utc(self.expires_at))


@dataclass(frozen=True)
class ProviderBillingIncidentResolution:
    provider_incident_id: str
    subscription_id: Optional[str]
    apply_hold: bool
    hold_status: Optional[str]

    def __post_init__(self) -> None:
        object.__setattr__(
            self,
            "provider_incident_id",
            _bounded_text(
                self.provider_incident_id,
                "provider incident ID",
                MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
            ),
        )
        if self.subscription_id is not None:
            object.__setattr__(
                self,
                "subscription_id",
                _bounded_text(
                    self.subscription_id,
                    "provider subscription ID",
                    MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
                ),
            )
        if not isinstance(self.apply_hold, bool):
            raise TypeError("apply_hold must be a Boolean")
        if self.hold_status is not None and self.hold_status not in BILLING_HOLD_STATUSES:
            raise ValueError("hold_status is invalid")
        if not self.apply_hold and self.hold_status is not None:
            raise ValueError("hold_status requires apply_hold")


class BillingProvider(Protocol):
    def create_checkout_session(
        self,
        *,
        price_id: str,
        sku_id: str,
        license_id: str,
        customer_id: Optional[str],
        customer_email: Optional[str],
        success_url: str,
        cancel_url: str,
        idempotency_key: str,
    ) -> ProviderBillingSession:
        ...

    def create_portal_session(
        self,
        *,
        customer_id: str,
        return_url: str,
        idempotency_key: str,
    ) -> ProviderBillingSession:
        ...

    def retrieve_subscription(self, subscription_id: str) -> Mapping[str, object]:
        ...

    def resolve_billing_incident(
        self,
        incident: Mapping[str, object],
    ) -> ProviderBillingIncidentResolution:
        ...


class BillingProviderFailure(RuntimeError):
    """Sanitized provider failure suitable for conversion at the API boundary."""


class StripeBillingProvider:
    def __init__(self, secret_key: str) -> None:
        self._secret_key = _bounded_text(secret_key, "Stripe secret key", 512)

    def create_checkout_session(
        self,
        *,
        price_id: str,
        sku_id: str,
        license_id: str,
        customer_id: Optional[str],
        customer_email: Optional[str],
        success_url: str,
        cancel_url: str,
        idempotency_key: str,
    ) -> ProviderBillingSession:
        normalized_price = _bounded_text(price_id, "Stripe price ID", 256)
        normalized_sku = validate_identifier(sku_id, "sku_id")
        normalized_license = validate_identifier(license_id, "license_id")
        normalized_success = _https_url(success_url, "Checkout success URL")
        normalized_cancel = _https_url(cancel_url, "Checkout cancel URL")
        normalized_idempotency = _bounded_text(
            idempotency_key,
            "provider idempotency key",
            255,
        )
        parameters: dict[str, object] = {
            "mode": "subscription",
            "line_items": [{"price": normalized_price, "quantity": 1}],
            "success_url": normalized_success,
            "cancel_url": normalized_cancel,
            "client_reference_id": normalized_license,
            "consent_collection": {"terms_of_service": "required"},
            "metadata": {"license_id": normalized_license, "sku_id": normalized_sku},
            "subscription_data": {
                "metadata": {"license_id": normalized_license, "sku_id": normalized_sku}
            },
        }
        if customer_id is not None:
            parameters["customer"] = _bounded_text(customer_id, "Stripe customer ID", 256)
        elif customer_email is not None:
            parameters["customer_email"] = _bounded_text(
                customer_email,
                "customer email",
                256,
            )
        try:
            created = stripe.checkout.Session.create(
                **parameters,
                api_key=self._secret_key,
                idempotency_key=normalized_idempotency,
            )
        except stripe.error.StripeError as exc:
            raise BillingProviderFailure("Stripe Checkout is temporarily unavailable") from exc
        return self._parse_session(created, include_expiry=True)

    def create_portal_session(
        self,
        *,
        customer_id: str,
        return_url: str,
        idempotency_key: str,
    ) -> ProviderBillingSession:
        try:
            created = stripe.billing_portal.Session.create(
                customer=_bounded_text(customer_id, "Stripe customer ID", 256),
                return_url=_https_url(return_url, "billing portal return URL"),
                api_key=self._secret_key,
                idempotency_key=_bounded_text(
                    idempotency_key,
                    "provider idempotency key",
                    255,
                ),
            )
        except stripe.error.StripeError as exc:
            raise BillingProviderFailure("Stripe billing management is temporarily unavailable") from exc
        return self._parse_session(created, include_expiry=False)

    def retrieve_subscription(self, subscription_id: str) -> Mapping[str, object]:
        normalized_subscription = _bounded_text(
            subscription_id,
            "Stripe subscription ID",
            MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
        )
        try:
            retrieved = stripe.Subscription.retrieve(
                normalized_subscription,
                api_key=self._secret_key,
            )
        except stripe.error.StripeError as exc:
            raise BillingProviderFailure(
                "Stripe subscription reconciliation is temporarily unavailable"
            ) from exc
        recursive = getattr(retrieved, "to_dict_recursive", None)
        value = recursive() if callable(recursive) else dict(retrieved)
        if not isinstance(value, Mapping):
            raise BillingProviderFailure("Stripe returned an invalid subscription object")
        return dict(value)

    def resolve_billing_incident(
        self,
        incident: Mapping[str, object],
    ) -> ProviderBillingIncidentResolution:
        if not isinstance(incident, Mapping):
            raise TypeError("incident must be a mapping")
        event_type = _bounded_text(
            incident.get("providerEventType"),
            "provider event type",
            MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
        )
        incident_id = _bounded_text(
            incident.get("incidentId"),
            "provider incident ID",
            MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
        )
        charge_id = _bounded_text(
            incident.get("chargeId"),
            "Stripe charge ID",
            MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
        )
        dispute_status = str(incident.get("incidentStatus") or "").strip()
        try:
            if event_type.startswith("charge.dispute."):
                dispute = self._provider_mapping(
                    stripe.Dispute.retrieve(incident_id, api_key=self._secret_key),
                    "dispute",
                )
                resolved_charge = dispute.get("charge")
                if isinstance(resolved_charge, Mapping):
                    resolved_charge = resolved_charge.get("id")
                resolved_charge_id = _bounded_text(
                    resolved_charge,
                    "Stripe dispute charge ID",
                    MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
                )
                if resolved_charge_id != charge_id:
                    raise BillingProviderFailure(
                        "Stripe returned a dispute for a different charge"
                    )
                dispute_status = str(dispute.get("status") or "").strip()
                if not dispute_status or len(dispute_status) > 64:
                    raise BillingProviderFailure(
                        "Stripe returned an invalid dispute status"
                    )
            charge = self._provider_mapping(
                stripe.Charge.retrieve(charge_id, api_key=self._secret_key),
                "charge",
            )
            invoice_reference = charge.get("invoice")
            if isinstance(invoice_reference, Mapping):
                invoice = dict(invoice_reference)
            elif isinstance(invoice_reference, str) and invoice_reference.strip():
                invoice = self._provider_mapping(
                    stripe.Invoice.retrieve(
                        _bounded_text(
                            invoice_reference,
                            "Stripe invoice ID",
                            MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
                        ),
                        api_key=self._secret_key,
                    ),
                    "invoice",
                )
            else:
                return ProviderBillingIncidentResolution(
                    provider_incident_id=incident_id,
                    subscription_id=None,
                    apply_hold=False,
                    hold_status=None,
                )
        except stripe.error.StripeError as exc:
            raise BillingProviderFailure(
                "Stripe billing incident reconciliation is temporarily unavailable"
            ) from exc
        subscription_reference = invoice.get("subscription")
        if subscription_reference is None:
            parent = invoice.get("parent")
            subscription_details = (
                parent.get("subscription_details")
                if isinstance(parent, Mapping)
                else None
            )
            subscription_reference = (
                subscription_details.get("subscription")
                if isinstance(subscription_details, Mapping)
                else None
            )
        if isinstance(subscription_reference, Mapping):
            subscription_reference = subscription_reference.get("id")
        if not isinstance(subscription_reference, str) or not subscription_reference.strip():
            return ProviderBillingIncidentResolution(
                provider_incident_id=incident_id,
                subscription_id=None,
                apply_hold=False,
                hold_status=None,
            )
        subscription_id = _bounded_text(
            subscription_reference,
            "Stripe subscription ID",
            MAXIMUM_PROVIDER_IDENTIFIER_CHARACTERS,
        )
        amount = charge.get("amount")
        amount_refunded = charge.get("amount_refunded")
        if (
            isinstance(amount, bool)
            or not isinstance(amount, int)
            or amount < 1
            or isinstance(amount_refunded, bool)
            or not isinstance(amount_refunded, int)
            or amount_refunded < 0
            or amount_refunded > amount
        ):
            raise BillingProviderFailure("Stripe returned invalid charge refund amounts")
        fully_refunded = amount_refunded == amount
        if fully_refunded:
            return ProviderBillingIncidentResolution(
                provider_incident_id=incident_id,
                subscription_id=subscription_id,
                apply_hold=True,
                hold_status="refunded",
            )
        if event_type.startswith("refund.") or event_type == "charge.refunded":
            return ProviderBillingIncidentResolution(
                provider_incident_id=incident_id,
                subscription_id=subscription_id,
                apply_hold=False,
                hold_status=None,
            )
        if event_type == "charge.dispute.funds_reinstated" or dispute_status in (
            "won",
            "warning_closed",
        ):
            hold_status = None
        elif dispute_status == "lost":
            hold_status = "dispute_lost"
        else:
            hold_status = "dispute_open"
        return ProviderBillingIncidentResolution(
            provider_incident_id=incident_id,
            subscription_id=subscription_id,
            apply_hold=True,
            hold_status=hold_status,
        )

    @staticmethod
    def _provider_mapping(value: object, field_name: str) -> dict[str, object]:
        if not isinstance(field_name, str) or not field_name:
            raise ValueError("field_name must be a non-empty string")
        recursive = getattr(value, "to_dict_recursive", None)
        mapping = recursive() if callable(recursive) else value
        if not isinstance(mapping, Mapping):
            raise BillingProviderFailure(f"Stripe returned an invalid {field_name} object")
        return dict(mapping)

    @staticmethod
    def _parse_session(value: object, *, include_expiry: bool) -> ProviderBillingSession:
        if not isinstance(include_expiry, bool):
            raise TypeError("include_expiry must be a Boolean")
        raw_expiry = _provider_value(value, "expires_at") if include_expiry else None
        expiry = None
        if raw_expiry is not None:
            if isinstance(raw_expiry, bool) or not isinstance(raw_expiry, int):
                raise BillingProviderFailure("Stripe returned an invalid session expiry")
            try:
                expiry = datetime.fromtimestamp(raw_expiry, tz=timezone.utc)
            except (OSError, OverflowError, ValueError) as exc:
                raise BillingProviderFailure("Stripe returned an invalid session expiry") from exc
        try:
            return ProviderBillingSession(
                session_id=_provider_value(value, "id"),
                url=_provider_value(value, "url"),
                expires_at=expiry,
            )
        except (TypeError, ValueError) as exc:
            raise BillingProviderFailure("Stripe returned an invalid billing session") from exc


def commerce_capabilities(
    catalog: LicensingCatalog,
    commerce_enabled: bool,
    price_sku_map: Mapping[str, str],
) -> dict[str, object]:
    if not isinstance(catalog, LicensingCatalog):
        raise TypeError("catalog must be a LicensingCatalog")
    if not isinstance(commerce_enabled, bool):
        raise TypeError("commerce_enabled must be a Boolean")
    if not isinstance(price_sku_map, Mapping):
        raise TypeError("price_sku_map must be a mapping")
    purchasable = tuple(
        sorted(
            {
                sku_id
                for sku_id in price_sku_map.values()
                if isinstance(sku_id, str)
                and sku_id in catalog.skus
                and catalog.skus[sku_id].active
                and catalog.skus[sku_id].kind is SkuKind.SUBSCRIPTION
            }
        )
    )
    return {
        "checkoutAvailable": commerce_enabled and bool(purchasable),
        "billingPortalAvailable": commerce_enabled,
        "purchasableSkuIds": list(purchasable) if commerce_enabled else [],
    }


def subscription_statuses(session: Session, license_id: str) -> list[dict[str, object]]:
    if not isinstance(session, Session):
        raise TypeError("session must be a Session")
    normalized_license = validate_identifier(license_id, "license_id")
    values = []
    subscriptions = session.scalars(
        select(Subscription)
        .where(Subscription.license_id == normalized_license)
        .order_by(Subscription.created_at, Subscription.id)
    ).all()
    for subscription in subscriptions:
        sku_ids = tuple(
            session.scalars(
                select(SubscriptionItem.sku_id)
                .where(SubscriptionItem.subscription_id == subscription.id)
                .order_by(SubscriptionItem.sku_id)
            )
        )
        values.append(
            {
                "subscriptionId": subscription.id,
                "provider": subscription.provider,
                "status": subscription.status,
                "skuIds": list(sku_ids),
                "currentPeriodEnd": _utc(subscription.current_period_end).isoformat(),
                "cancelAtPeriodEnd": subscription.cancel_at_period_end,
                "graceEndsAt": (
                    None
                    if subscription.grace_ends_at is None
                    else _utc(subscription.grace_ends_at).isoformat()
                ),
                "billingHold": subscription.billing_hold_status,
            }
        )
    return values


class AccountBillingService:
    def __init__(
        self,
        session: Session,
        catalog: LicensingCatalog,
        provider: Optional[BillingProvider],
        price_sku_map: Mapping[str, str],
        commerce_enabled: bool,
        checkout_success_url: Optional[str],
        checkout_cancel_url: Optional[str],
        billing_portal_return_url: Optional[str],
    ) -> None:
        if not isinstance(session, Session):
            raise TypeError("session must be a Session")
        if not isinstance(catalog, LicensingCatalog):
            raise TypeError("catalog must be a LicensingCatalog")
        if provider is not None and not all(
            callable(getattr(provider, name, None))
            for name in ("create_checkout_session", "create_portal_session")
        ):
            raise TypeError("provider must expose Checkout and portal session creation")
        if not isinstance(price_sku_map, Mapping):
            raise TypeError("price_sku_map must be a mapping")
        if not isinstance(commerce_enabled, bool):
            raise TypeError("commerce_enabled must be a Boolean")
        self._session = session
        self._catalog = catalog
        self._provider = provider
        self._price_sku_map = dict(price_sku_map)
        self._commerce_enabled = commerce_enabled
        self._checkout_success_url = checkout_success_url
        self._checkout_cancel_url = checkout_cancel_url
        self._billing_portal_return_url = billing_portal_return_url

    def create_checkout_session(
        self,
        license_id: str,
        sku_id: str,
        actor_user_id: str,
        idempotency_key: str,
        correlation_id: str,
    ) -> dict[str, object]:
        normalized_license = validate_identifier(license_id, "license_id")
        normalized_sku = validate_identifier(sku_id, "sku_id")
        normalized_actor = validate_identifier(actor_user_id, "actor_user_id")
        normalized_idempotency = _bounded_text(
            idempotency_key,
            "idempotency_key",
            MAX_IDEMPOTENCY_KEY_CHARACTERS,
        )
        normalized_correlation = validate_identifier(correlation_id, "correlation_id")
        now = datetime.now(timezone.utc)
        digest = hashlib.sha256(
            canonicalize_json({"licenseId": normalized_license, "skuId": normalized_sku})
        ).digest()
        with self._session.begin():
            replay = self._load_replay(
                CHECKOUT_IDEMPOTENCY_SCOPE,
                normalized_actor,
                normalized_idempotency,
                digest,
                now,
            )
            if replay is not None:
                replay["idempotentReplay"] = True
                return replay
            self._require_provider()
            license_row, actor = self._require_billing_authority(
                normalized_license,
                normalized_actor,
                now,
            )
            sku = self._catalog.skus.get(normalized_sku)
            if sku is None or not sku.active or sku.kind is not SkuKind.SUBSCRIPTION:
                raise ServerLicensingError(
                    ServerErrorCode.SKU_NOT_AVAILABLE,
                    "the requested subscription is not available for purchase",
                    status_code=404,
                )
            price_ids = tuple(
                price_id
                for price_id, mapped_sku in self._price_sku_map.items()
                if mapped_sku == normalized_sku
            )
            if len(price_ids) != 1:
                raise ServerLicensingError(
                    ServerErrorCode.SKU_NOT_AVAILABLE,
                    "the requested subscription has no unambiguous provider price",
                    status_code=503,
                )
            customer_id = self._latest_customer_id(license_row.id)
            try:
                provider_session = self._provider.create_checkout_session(
                    price_id=price_ids[0],
                    sku_id=sku.sku_id,
                    license_id=license_row.id,
                    customer_id=customer_id,
                    customer_email=None if customer_id is not None else actor.verified_email,
                    success_url=self._checkout_success_url,
                    cancel_url=self._checkout_cancel_url,
                    idempotency_key=self._provider_idempotency_key(
                        CHECKOUT_IDEMPOTENCY_SCOPE,
                        normalized_actor,
                        normalized_idempotency,
                    ),
                )
            except BillingProviderFailure as exc:
                raise ServerLicensingError(
                    ServerErrorCode.BILLING_UNAVAILABLE,
                    "subscription checkout is temporarily unavailable",
                    status_code=503,
                    retryable=True,
                ) from exc
            response = self._session_response(provider_session)
            self._store_response(
                CHECKOUT_IDEMPOTENCY_SCOPE,
                normalized_actor,
                normalized_idempotency,
                digest,
                response,
                now,
            )
            self._audit(
                "billing.checkout_session_created",
                license_row.id,
                normalized_actor,
                normalized_correlation,
                {"skuId": sku.sku_id, "provider": STRIPE_PROVIDER_ID},
            )
            response["idempotentReplay"] = False
            return response

    def create_portal_session(
        self,
        license_id: str,
        actor_user_id: str,
        idempotency_key: str,
        correlation_id: str,
    ) -> dict[str, object]:
        normalized_license = validate_identifier(license_id, "license_id")
        normalized_actor = validate_identifier(actor_user_id, "actor_user_id")
        normalized_idempotency = _bounded_text(
            idempotency_key,
            "idempotency_key",
            MAX_IDEMPOTENCY_KEY_CHARACTERS,
        )
        normalized_correlation = validate_identifier(correlation_id, "correlation_id")
        now = datetime.now(timezone.utc)
        digest = hashlib.sha256(
            canonicalize_json({"licenseId": normalized_license})
        ).digest()
        with self._session.begin():
            replay = self._load_replay(
                PORTAL_IDEMPOTENCY_SCOPE,
                normalized_actor,
                normalized_idempotency,
                digest,
                now,
            )
            if replay is not None:
                replay["idempotentReplay"] = True
                return replay
            self._require_provider()
            license_row, _actor = self._require_billing_authority(
                normalized_license,
                normalized_actor,
                now,
            )
            customer_id = self._latest_customer_id(license_row.id)
            if customer_id is None:
                raise ServerLicensingError(
                    ServerErrorCode.SUBSCRIPTION_INACTIVE,
                    "no provider-managed subscription is available for this license",
                    status_code=404,
                )
            try:
                provider_session = self._provider.create_portal_session(
                    customer_id=customer_id,
                    return_url=self._billing_portal_return_url,
                    idempotency_key=self._provider_idempotency_key(
                        PORTAL_IDEMPOTENCY_SCOPE,
                        normalized_actor,
                        normalized_idempotency,
                    ),
                )
            except BillingProviderFailure as exc:
                raise ServerLicensingError(
                    ServerErrorCode.BILLING_UNAVAILABLE,
                    "billing management is temporarily unavailable",
                    status_code=503,
                    retryable=True,
                ) from exc
            response = self._session_response(provider_session)
            self._store_response(
                PORTAL_IDEMPOTENCY_SCOPE,
                normalized_actor,
                normalized_idempotency,
                digest,
                response,
                now,
            )
            self._audit(
                "billing.portal_session_created",
                license_row.id,
                normalized_actor,
                normalized_correlation,
                {"provider": STRIPE_PROVIDER_ID},
            )
            response["idempotentReplay"] = False
            return response

    def _require_provider(self) -> None:
        if not self._commerce_enabled or self._provider is None:
            raise ServerLicensingError(
                ServerErrorCode.BILLING_UNAVAILABLE,
                "provider billing is not enabled",
                status_code=503,
            )

    def _require_billing_authority(
        self,
        license_id: str,
        actor_user_id: str,
        now: datetime,
    ) -> tuple[License, User]:
        actor = self._session.get(User, actor_user_id)
        license_row = self._session.scalar(
            select(License).where(License.id == license_id).with_for_update()
        )
        if actor is None or actor.status != "active":
            raise ServerLicensingError(
                ServerErrorCode.AUTHORIZATION_DENIED,
                "account is not active",
                status_code=403,
            )
        if license_row is None or license_row.status != "active":
            raise ServerLicensingError(
                ServerErrorCode.LICENSE_NOT_FOUND,
                "license was not found",
                status_code=404,
            )
        authorized = license_row.owner_user_id == actor.id
        if not authorized and license_row.owner_organization_id is not None:
            authorized = self._session.scalar(
                select(Membership.id).where(
                    Membership.organization_id == license_row.owner_organization_id,
                    Membership.user_id == actor.id,
                    Membership.status == "active",
                    Membership.role.in_(("owner", "admin")),
                    or_(Membership.valid_until.is_(None), Membership.valid_until > now),
                )
            ) is not None
        if not authorized:
            raise ServerLicensingError(
                ServerErrorCode.AUTHORIZATION_DENIED,
                "only the license owner or a team administrator can manage billing",
                status_code=403,
            )
        return license_row, actor

    def _latest_customer_id(self, license_id: str) -> Optional[str]:
        subscription = self._session.scalar(
            select(Subscription)
            .where(
                Subscription.license_id == license_id,
                Subscription.provider == STRIPE_PROVIDER_ID,
                Subscription.provider_customer_id.is_not(None),
            )
            .order_by(Subscription.updated_at.desc(), Subscription.id.desc())
            .limit(1)
        )
        return None if subscription is None else subscription.provider_customer_id

    def _load_replay(
        self,
        scope: str,
        subject_key: str,
        idempotency_key: str,
        request_digest: bytes,
        now: datetime,
    ) -> Optional[dict[str, object]]:
        record = self._session.scalar(
            select(IdempotencyRecord)
            .where(
                IdempotencyRecord.scope == scope,
                IdempotencyRecord.subject_key == subject_key,
                IdempotencyRecord.idempotency_key == idempotency_key,
            )
            .with_for_update()
        )
        if record is None:
            return None
        if _utc(record.expires_at) <= now:
            self._session.delete(record)
            self._session.flush()
            return None
        if not hmac.compare_digest(record.request_digest, request_digest):
            raise ServerLicensingError(
                ServerErrorCode.IDEMPOTENCY_CONFLICT,
                "idempotency key was already used for a different billing request",
                status_code=409,
            )
        return dict(record.response_json)

    def _store_response(
        self,
        scope: str,
        subject_key: str,
        idempotency_key: str,
        request_digest: bytes,
        response: dict[str, object],
        now: datetime,
    ) -> None:
        self._session.add(
            IdempotencyRecord(
                id=new_identifier("idempotency"),
                scope=scope,
                subject_key=subject_key,
                idempotency_key=idempotency_key,
                request_digest=request_digest,
                response_status=200,
                response_json=response,
                expires_at=now + timedelta(days=DEFAULT_IDEMPOTENCY_DAYS),
            )
        )

    def _audit(
        self,
        action: str,
        license_id: str,
        actor_user_id: str,
        correlation_id: str,
        metadata: dict[str, object],
    ) -> None:
        self._session.add(
            AuditEvent(
                id=new_identifier("audit"),
                actor_type="user",
                actor_id=actor_user_id,
                action=validate_identifier(action, "audit action"),
                target_type="license",
                target_id=license_id,
                reason=None,
                correlation_id=correlation_id,
                source_address_digest=None,
                metadata_json=metadata,
            )
        )

    @staticmethod
    def _provider_idempotency_key(scope: str, subject: str, key: str) -> str:
        digest = hashlib.sha256(f"{scope}\n{subject}\n{key}".encode("utf-8")).hexdigest()
        return f"apolon_{digest}"

    @staticmethod
    def _session_response(provider_session: ProviderBillingSession) -> dict[str, object]:
        if not isinstance(provider_session, ProviderBillingSession):
            raise TypeError("provider must return ProviderBillingSession")
        return {
            "provider": STRIPE_PROVIDER_ID,
            "sessionId": provider_session.session_id,
            "url": provider_session.url,
            "expiresAt": (
                None
                if provider_session.expires_at is None
                else provider_session.expires_at.isoformat()
            ),
        }


__all__ = [
    "AccountBillingService",
    "BILLING_HOLD_STATUSES",
    "BillingProvider",
    "BillingProviderFailure",
    "ProviderBillingIncidentResolution",
    "ProviderBillingSession",
    "StripeBillingProvider",
    "commerce_capabilities",
    "subscription_statuses",
]
