"""V2 Payment Verifier (Coinbase compatible) — facilitator client for /verify, /settle, /supported."""

from __future__ import annotations

import json
import urllib.request
import urllib.error
from typing import Optional

from .types_v2 import (
    PaymentRequirementsV2,
    PaymentPayloadV2,
    VerifyResponseV2,
    SettlementResponseV2,
    FacilitatorVerifyRequestV2,
    FacilitatorSettleRequestV2,
    SupportedResponse,
    SupportedKind,
    StacksPayloadV2,
    X402_ERROR_CODES,
)


class VerifyOptions:
    """Options for verifying a payment."""

    def __init__(self, *, payment_requirements: PaymentRequirementsV2) -> None:
        self.payment_requirements = payment_requirements


class SettleOptions:
    """Options for settling a payment."""

    def __init__(self, *, payment_requirements: PaymentRequirementsV2) -> None:
        self.payment_requirements = payment_requirements


class X402PaymentVerifier:
    """Payment verifier using the V2 facilitator API (Coinbase compatible)."""

    def __init__(self, facilitator_url: str = "http://localhost:8085") -> None:
        self._facilitator_url = facilitator_url.rstrip("/")

    def _post(self, path: str, body: dict) -> dict:
        url = f"{self._facilitator_url}{path}"
        data = json.dumps(body).encode()
        req = urllib.request.Request(
            url,
            data=data,
            headers={"Content-Type": "application/json", "Accept": "application/json"},
            method="POST",
        )
        with urllib.request.urlopen(req, timeout=30) as resp:
            return json.loads(resp.read())

    def _get(self, path: str) -> dict:
        url = f"{self._facilitator_url}{path}"
        req = urllib.request.Request(url, headers={"Accept": "application/json"})
        with urllib.request.urlopen(req, timeout=30) as resp:
            return json.loads(resp.read())

    def verify(
        self,
        payment_payload: PaymentPayloadV2,
        options: VerifyOptions,
    ) -> VerifyResponseV2:
        """Verify a payment using the V2 facilitator API."""
        try:
            request_body = FacilitatorVerifyRequestV2(
                x402_version=2,
                payment_payload=payment_payload,
                payment_requirements=options.payment_requirements,
            )
            data = self._post("/verify", request_body.model_dump(by_alias=True))
            return VerifyResponseV2.model_validate(data)
        except urllib.error.HTTPError as exc:
            try:
                err_data = json.loads(exc.read())
            except Exception:
                err_data = {}
            return VerifyResponseV2(
                is_valid=False,
                invalid_reason=err_data.get("invalidReason", X402_ERROR_CODES["UNEXPECTED_VERIFY_ERROR"]),
                payer=err_data.get("payer"),
            )
        except Exception:
            return VerifyResponseV2(
                is_valid=False,
                invalid_reason=X402_ERROR_CODES["UNEXPECTED_VERIFY_ERROR"],
            )

    def settle(
        self,
        payment_payload: PaymentPayloadV2,
        options: SettleOptions,
    ) -> SettlementResponseV2:
        """Settle a payment using the V2 facilitator API."""
        try:
            request_body = FacilitatorSettleRequestV2(
                x402_version=2,
                payment_payload=payment_payload,
                payment_requirements=options.payment_requirements,
            )
            data = self._post("/settle", request_body.model_dump(by_alias=True))
            return SettlementResponseV2.model_validate(data)
        except urllib.error.HTTPError as exc:
            try:
                err_data = json.loads(exc.read())
            except Exception:
                err_data = {}
            return SettlementResponseV2(
                success=False,
                error_reason=err_data.get("errorReason", X402_ERROR_CODES["UNEXPECTED_SETTLE_ERROR"]),
                payer=err_data.get("payer"),
                transaction=err_data.get("transaction", ""),
                network=err_data.get("network", options.payment_requirements.network),
            )
        except Exception:
            return SettlementResponseV2(
                success=False,
                error_reason=X402_ERROR_CODES["UNEXPECTED_SETTLE_ERROR"],
                transaction="",
                network=options.payment_requirements.network,
            )

    def get_supported(self) -> SupportedResponse:
        """Get supported payment kinds from the facilitator."""
        try:
            data = self._get("/supported")
            return SupportedResponse.model_validate(data)
        except Exception:
            return SupportedResponse(kinds=[], extensions=[], signers={})

    def is_kind_supported(
        self,
        network: str,
        scheme: str = "exact",
        x402_version: int = 2,
    ) -> bool:
        """Check if a specific payment kind is supported."""
        supported = self.get_supported()
        return any(
            k.x402_version == x402_version
            and k.scheme == scheme
            and k.network == network
            for k in supported.kinds
        )

    def verify_and_settle(
        self,
        payment_payload: PaymentPayloadV2,
        options: VerifyOptions,
    ) -> SettlementResponseV2:
        """Verify then settle in one operation."""
        verify_result = self.verify(payment_payload, options)
        if not verify_result.is_valid:
            return SettlementResponseV2(
                success=False,
                error_reason=verify_result.invalid_reason or X402_ERROR_CODES["UNEXPECTED_VERIFY_ERROR"],
                payer=verify_result.payer,
                transaction="",
                network=options.payment_requirements.network,
            )
        settle_opts = SettleOptions(payment_requirements=options.payment_requirements)
        return self.settle(payment_payload, settle_opts)

    @staticmethod
    def create_payment_payload(
        signed_transaction: str,
        payment_requirements: PaymentRequirementsV2,
    ) -> PaymentPayloadV2:
        """Create a payment payload from a signed transaction."""
        return PaymentPayloadV2(
            x402_version=2,
            accepted=payment_requirements,
            payload=StacksPayloadV2(transaction=signed_transaction),
        )

    def is_payment_valid(
        self,
        payment_payload: PaymentPayloadV2,
        options: VerifyOptions,
    ) -> bool:
        """Quick boolean check."""
        result = self.verify(payment_payload, options)
        return result.is_valid


def create_verifier(facilitator_url: Optional[str] = None) -> X402PaymentVerifier:
    """Create a verifier instance."""
    return X402PaymentVerifier(facilitator_url or "http://localhost:8085")
