"""Resolve payments.* to a bound Stripe or PayPal connection.

Class C: no platform payment default. Core stamps connection_id + action on
the grant; Cloud executes the existing payment.* job handlers.
"""
from __future__ import annotations

from dataclasses import dataclass
from typing import Any
from uuid import UUID

from src.domain.connectors.catalog import get_action, slugs_implementing
from src.domain.connectors.runtime_surfaces import INVOKE_JOB_ALIASES, PAYMENT_INVOKE_VERBS

PAYMENT_PROVIDERS: tuple[str, ...] = ("stripe", "paypal")

PAYMENT_JOB_TYPES: frozenset[str] = frozenset(INVOKE_JOB_ALIASES) - {"email.send"}


def canonical_payment_capability(request_type: str) -> str | None:
    if request_type in PAYMENT_INVOKE_VERBS:
        return request_type
    if request_type in PAYMENT_JOB_TYPES:
        return INVOKE_JOB_ALIASES[request_type]
    return None


def job_type_for_payment_capability(capability: str) -> str:
    for job_type, canonical in INVOKE_JOB_ALIASES.items():
        if canonical == capability and job_type != "email.send":
            return job_type
    return capability


@dataclass(frozen=True)
class PaymentResolution:
    ok: bool
    capability: str
    reason_code: str | None = None
    message: str | None = None
    connection_id: UUID | None = None
    action: str | None = None
    provider: str | None = None

    @property
    def allowed(self) -> bool:
        return self.ok


def _fail(capability: str, code: str, message: str) -> PaymentResolution:
    return PaymentResolution(ok=False, capability=capability, reason_code=code, message=message)


def implementing_action_for(connector_key: str, capability: str) -> str | None:
    for slug in slugs_implementing(capability):
        pair = get_action(slug)
        if pair and pair[0].key == connector_key:
            return slug
    return None


def connection_implements_capability(connector_key: str, capability: str) -> bool:
    if implementing_action_for(connector_key, capability):
        return True
    # PayPal has no catalog API; Cloud records are the catalog. Eligibility is
    # still the payment-family connection.
    return capability == "payments.catalog.upsert" and connector_key in PAYMENT_PROVIDERS


@dataclass
class PaymentConnectionSnapshot:
    connection_id: UUID
    connector_key: str
    status: str
    has_secrets: bool


def resolve_payment_capability(
    *,
    capability: str,
    snapshots: list[PaymentConnectionSnapshot],
    enabled_capabilities: set[str] | None = None,
    connection_id: UUID | None = None,
    provider_hint: str | None = None,
) -> PaymentResolution:
    if capability not in PAYMENT_INVOKE_VERBS:
        return _fail(capability, "capability_not_invocable", f"{capability} is not a payments invoke verb")
    if enabled_capabilities is not None and "integrations.execute" not in enabled_capabilities:
        return _fail(capability, "capability_not_enabled", "integrations.execute is not enabled")

    hint = (provider_hint or "").strip().lower()
    eligible = [
        row
        for row in snapshots
        if row.status == "active"
        and row.connector_key in PAYMENT_PROVIDERS
        and row.has_secrets
        and connection_implements_capability(row.connector_key, capability)
    ]
    if hint in PAYMENT_PROVIDERS:
        eligible = [row for row in eligible if row.connector_key == hint]

    if connection_id is not None:
        match = next((row for row in eligible if row.connection_id == connection_id), None)
        if match is None:
            return _fail(
                capability,
                "capability_not_bound",
                "connection_id is not an eligible Stripe or PayPal payment binding",
            )
        action = implementing_action_for(match.connector_key, capability)
        return PaymentResolution(
            ok=True,
            capability=capability,
            connection_id=match.connection_id,
            action=action,
            provider=match.connector_key,
        )

    if not eligible:
        return _fail(
            capability,
            "capability_not_bound",
            "Connect Stripe or PayPal before invoking payments capabilities",
        )
    providers = {row.connector_key for row in eligible}
    if len(eligible) > 1 and len(providers) > 1:
        return _fail(
            capability,
            "binding_ambiguous",
            "Multiple payment providers are connected; bind one or pass binding_connection_id",
        )
    if len(eligible) > 1:
        return _fail(
            capability,
            "binding_ambiguous",
            "Multiple payment connections are eligible; bind one or pass binding_connection_id",
        )
    chosen = eligible[0]
    action = implementing_action_for(chosen.connector_key, capability)
    return PaymentResolution(
        ok=True,
        capability=capability,
        connection_id=chosen.connection_id,
        action=action,
        provider=chosen.connector_key,
    )


def snapshots_from_rows(rows: list[Any], catalogs: dict[Any, Any]) -> list[PaymentConnectionSnapshot]:
    snapshots: list[PaymentConnectionSnapshot] = []
    for row in rows:
        catalog = catalogs.get(row.connector_id)
        if catalog is None:
            continue
        snapshots.append(
            PaymentConnectionSnapshot(
                connection_id=row.id,
                connector_key=catalog.key,
                status=row.status,
                has_secrets=bool(row.secrets_enc),
            )
        )
    return snapshots
