"""x402Facilitator - Payment verification and settlement component.

Provides both async (x402Facilitator) and sync (x402FacilitatorSync)
implementations. Runs as a service, manages scheme mechanisms, handles V1/V2 routing.
"""

from __future__ import annotations

import asyncio
from typing import Any

from typing_extensions import Self

from .facilitator_base import (
    AfterSettleHook,
    AfterVerifyHook,
    BeforeSettleHook,
    BeforeVerifyHook,
    OnSettleFailureHook,
    OnVerifyFailureHook,
    SyncAfterSettleHook,
    SyncAfterVerifyHook,
    SyncBeforeSettleHook,
    SyncBeforeVerifyHook,
    SyncOnSettleFailureHook,
    SyncOnVerifyFailureHook,
    x402FacilitatorBase,
)
from .schemas import (
    PaymentPayload,
    PaymentPayloadV1,
    PaymentRequirements,
    PaymentRequirementsV1,
    SettleResponse,
    VerifyResponse,
)

# ============================================================================
# Async Facilitator (Default)
# ============================================================================


class x402Facilitator(x402FacilitatorBase):
    """Async payment verification and settlement component.

    Supports both sync and async hooks (auto-detected).
    Use x402FacilitatorSync for sync-only environments.

    Example:
        ```python
        from x402 import x402Facilitator
        from x402.mechanisms.evm.exact import ExactEvmFacilitatorScheme

        facilitator = x402Facilitator()
        facilitator.register(
            ["eip155:8453", "eip155:84532"],
            ExactEvmFacilitatorScheme(wallet=facilitator_wallet),
        )
        facilitator.register_extension("bazaar")

        # Verify payment
        result = await facilitator.verify(payload, requirements)

        # Get supported kinds for /supported endpoint
        supported = facilitator.get_supported()
        ```
    """

    def __init__(self) -> None:
        """Initialize async x402Facilitator."""
        super().__init__()
        # Type the hook lists properly
        self._before_verify_hooks: list[BeforeVerifyHook] = []
        self._after_verify_hooks: list[AfterVerifyHook] = []
        self._on_verify_failure_hooks: list[OnVerifyFailureHook] = []

        self._before_settle_hooks: list[BeforeSettleHook] = []
        self._after_settle_hooks: list[AfterSettleHook] = []
        self._on_settle_failure_hooks: list[OnSettleFailureHook] = []

    # ========================================================================
    # Hook Registration
    # ========================================================================

    def on_before_verify(self, hook: BeforeVerifyHook) -> Self:
        """Register hook before verification. Return AbortResult to abort."""
        self._before_verify_hooks.append(hook)
        return self

    def on_after_verify(self, hook: AfterVerifyHook) -> Self:
        """Register hook after successful verification."""
        self._after_verify_hooks.append(hook)
        return self

    def on_verify_failure(self, hook: OnVerifyFailureHook) -> Self:
        """Register hook on verification failure. Return RecoveredVerifyResult to recover."""
        self._on_verify_failure_hooks.append(hook)
        return self

    def on_before_settle(self, hook: BeforeSettleHook) -> Self:
        """Register hook before settlement. Return AbortResult to abort."""
        self._before_settle_hooks.append(hook)
        return self

    def on_after_settle(self, hook: AfterSettleHook) -> Self:
        """Register hook after successful settlement."""
        self._after_settle_hooks.append(hook)
        return self

    def on_settle_failure(self, hook: OnSettleFailureHook) -> Self:
        """Register hook on settlement failure. Return RecoveredSettleResult to recover."""
        self._on_settle_failure_hooks.append(hook)
        return self

    # ========================================================================
    # Verify (Async)
    # ========================================================================

    async def verify(
        self,
        payload: PaymentPayload | PaymentPayloadV1,
        requirements: PaymentRequirements | PaymentRequirementsV1,
        payload_bytes: bytes | None = None,
        requirements_bytes: bytes | None = None,
    ) -> VerifyResponse:
        """Verify a payment.

        Routes to V1 or V2 verification based on payload version.

        Args:
            payload: Payment payload to verify.
            requirements: Requirements to verify against.
            payload_bytes: Raw payload bytes (escape hatch for extensions).
            requirements_bytes: Raw requirements bytes (escape hatch).

        Returns:
            VerifyResponse with is_valid=True or is_valid=False.

        Raises:
            SchemeNotFoundError: If no facilitator registered for scheme/network.
            PaymentAbortedError: If a before hook aborts.
        """
        gen = self._verify_core(payload, requirements, payload_bytes, requirements_bytes)
        result = None
        try:
            while True:
                _, hook, ctx = gen.send(result)
                result = await self._execute_hook(hook, ctx)
        except StopIteration as e:
            return e.value

    # ========================================================================
    # Settle (Async)
    # ========================================================================

    async def settle(
        self,
        payload: PaymentPayload | PaymentPayloadV1,
        requirements: PaymentRequirements | PaymentRequirementsV1,
        payload_bytes: bytes | None = None,
        requirements_bytes: bytes | None = None,
    ) -> SettleResponse:
        """Settle a payment.

        Routes to V1 or V2 settlement based on payload version.

        Args:
            payload: Payment payload to settle.
            requirements: Requirements for settlement.
            payload_bytes: Raw payload bytes (escape hatch for extensions).
            requirements_bytes: Raw requirements bytes (escape hatch).

        Returns:
            SettleResponse with success=True or success=False.

        Raises:
            SchemeNotFoundError: If no facilitator registered for scheme/network.
            PaymentAbortedError: If a before hook aborts.
        """
        gen = self._settle_core(payload, requirements, payload_bytes, requirements_bytes)
        result = None
        try:
            while True:
                _, hook, ctx = gen.send(result)
                result = await self._execute_hook(hook, ctx)
        except StopIteration as e:
            return e.value

    async def _execute_hook(self, hook: Any, context: Any) -> Any:
        """Execute hook, auto-detecting sync/async."""
        result = hook(context)
        if asyncio.iscoroutine(result) or asyncio.isfuture(result):
            return await result
        return result


# ============================================================================
# Sync Facilitator
# ============================================================================


class x402FacilitatorSync(x402FacilitatorBase):
    """Sync payment verification and settlement component.

    Only supports sync hooks. For async hook support, use x402Facilitator.

    Example:
        ```python
        from x402 import x402FacilitatorSync
        from x402.mechanisms.evm.exact import ExactEvmFacilitatorScheme

        facilitator = x402FacilitatorSync()
        facilitator.register(
            ["eip155:8453", "eip155:84532"],
            ExactEvmFacilitatorScheme(wallet=facilitator_wallet),
        )

        # Verify payment
        result = facilitator.verify(payload, requirements)
        ```
    """

    def __init__(self) -> None:
        """Initialize sync x402Facilitator."""
        super().__init__()
        # Type the hook lists for sync-only
        self._before_verify_hooks: list[SyncBeforeVerifyHook] = []
        self._after_verify_hooks: list[SyncAfterVerifyHook] = []
        self._on_verify_failure_hooks: list[SyncOnVerifyFailureHook] = []

        self._before_settle_hooks: list[SyncBeforeSettleHook] = []
        self._after_settle_hooks: list[SyncAfterSettleHook] = []
        self._on_settle_failure_hooks: list[SyncOnSettleFailureHook] = []

    # ========================================================================
    # Hook Registration
    # ========================================================================

    def on_before_verify(self, hook: SyncBeforeVerifyHook) -> Self:
        """Register hook before verification. Return AbortResult to abort."""
        self._before_verify_hooks.append(hook)
        return self

    def on_after_verify(self, hook: SyncAfterVerifyHook) -> Self:
        """Register hook after successful verification."""
        self._after_verify_hooks.append(hook)
        return self

    def on_verify_failure(self, hook: SyncOnVerifyFailureHook) -> Self:
        """Register hook on verification failure. Return RecoveredVerifyResult to recover."""
        self._on_verify_failure_hooks.append(hook)
        return self

    def on_before_settle(self, hook: SyncBeforeSettleHook) -> Self:
        """Register hook before settlement. Return AbortResult to abort."""
        self._before_settle_hooks.append(hook)
        return self

    def on_after_settle(self, hook: SyncAfterSettleHook) -> Self:
        """Register hook after successful settlement."""
        self._after_settle_hooks.append(hook)
        return self

    def on_settle_failure(self, hook: SyncOnSettleFailureHook) -> Self:
        """Register hook on settlement failure. Return RecoveredSettleResult to recover."""
        self._on_settle_failure_hooks.append(hook)
        return self

    # ========================================================================
    # Verify (Sync)
    # ========================================================================

    def verify(
        self,
        payload: PaymentPayload | PaymentPayloadV1,
        requirements: PaymentRequirements | PaymentRequirementsV1,
        payload_bytes: bytes | None = None,
        requirements_bytes: bytes | None = None,
    ) -> VerifyResponse:
        """Verify a payment.

        Routes to V1 or V2 verification based on payload version.

        Args:
            payload: Payment payload to verify.
            requirements: Requirements to verify against.
            payload_bytes: Raw payload bytes (escape hatch for extensions).
            requirements_bytes: Raw requirements bytes (escape hatch).

        Returns:
            VerifyResponse with is_valid=True or is_valid=False.

        Raises:
            SchemeNotFoundError: If no facilitator registered for scheme/network.
            PaymentAbortedError: If a before hook aborts.
        """
        gen = self._verify_core(payload, requirements, payload_bytes, requirements_bytes)
        result = None
        try:
            while True:
                _, hook, ctx = gen.send(result)
                result = self._execute_hook_sync(hook, ctx)
        except StopIteration as e:
            return e.value

    # ========================================================================
    # Settle (Sync)
    # ========================================================================

    def settle(
        self,
        payload: PaymentPayload | PaymentPayloadV1,
        requirements: PaymentRequirements | PaymentRequirementsV1,
        payload_bytes: bytes | None = None,
        requirements_bytes: bytes | None = None,
    ) -> SettleResponse:
        """Settle a payment.

        Routes to V1 or V2 settlement based on payload version.

        Args:
            payload: Payment payload to settle.
            requirements: Requirements for settlement.
            payload_bytes: Raw payload bytes (escape hatch for extensions).
            requirements_bytes: Raw requirements bytes (escape hatch).

        Returns:
            SettleResponse with success=True or success=False.

        Raises:
            SchemeNotFoundError: If no facilitator registered for scheme/network.
            PaymentAbortedError: If a before hook aborts.
        """
        gen = self._settle_core(payload, requirements, payload_bytes, requirements_bytes)
        result = None
        try:
            while True:
                _, hook, ctx = gen.send(result)
                result = self._execute_hook_sync(hook, ctx)
        except StopIteration as e:
            return e.value

    def _execute_hook_sync(self, hook: Any, context: Any) -> Any:
        """Execute hook synchronously. Raises if async hook detected."""
        result = hook(context)
        if asyncio.iscoroutine(result):
            result.close()  # Prevent warning
            raise TypeError(
                "Async hooks are not supported in x402FacilitatorSync. "
                "Use x402Facilitator for async hook support."
            )
        return result
