"""V1 Payment client for making x402 payments on Stacks."""

from __future__ import annotations

from typing import Optional

from .types import (
    NetworkType,
    TokenType,
    PaymentDetails,
    PaymentResult,
    SignedPaymentResult,
    X402PaymentRequired,
    X402ClientConfig,
    TokenContract,
)
from .stacks_tx.keys import private_key_to_address
from .stacks_tx.transaction import build_stx_transfer, build_contract_call
from .stacks_tx.clarity import (
    serialize_uint,
    serialize_standard_principal,
    serialize_optional_none,
    serialize_optional_some,
    serialize_buffer_from_string,
)
from .stacks_tx.network import fetch_account_nonce
from .utils import get_api_endpoint, is_payment_request_expired


class X402PaymentClient:
    """Payment client for making x402 payments on Stacks."""

    def __init__(self, config: X402ClientConfig) -> None:
        self._network: NetworkType = config.network
        self._private_key: str = config.private_key
        self._timeout: int = config.timeout or 30000

    def sign_payment(self, payment_request: X402PaymentRequired) -> SignedPaymentResult:
        """Sign a payment based on x402 payment request (without broadcasting)."""
        try:
            amount = int(payment_request.max_amount_required)
            token_type: TokenType = payment_request.token_type or "STX"
            memo = payment_request.nonce[:34]

            if token_type == "sBTC":
                return self.sign_sbtc_transfer(PaymentDetails(
                    recipient=payment_request.pay_to,
                    amount=amount,
                    sender_key=self._private_key,
                    network=payment_request.network,
                    memo=memo,
                    token_type=token_type,
                    token_contract=payment_request.token_contract,
                ))
            elif token_type == "USDCx":
                return self.sign_usdcx_transfer(PaymentDetails(
                    recipient=payment_request.pay_to,
                    amount=amount,
                    sender_key=self._private_key,
                    network=payment_request.network,
                    memo=memo,
                    token_type=token_type,
                    token_contract=payment_request.token_contract,
                ))
            else:
                return self.sign_stx_transfer(PaymentDetails(
                    recipient=payment_request.pay_to,
                    amount=amount,
                    sender_key=self._private_key,
                    network=payment_request.network,
                    memo=memo,
                    token_type=token_type,
                ))
        except Exception as e:
            return SignedPaymentResult(
                signed_transaction="",
                success=False,
                error=str(e),
            )

    def sign_stx_transfer(self, details: PaymentDetails) -> SignedPaymentResult:
        """Sign STX token transfer (without broadcasting)."""
        try:
            network = details.network
            sender_address = private_key_to_address(self._private_key, network)
            nonce = details.nonce if details.nonce is not None else 0
            fee = details.fee if details.fee is not None else 200

            tx_hex = build_stx_transfer(
                self._private_key,
                details.recipient,
                details.amount,
                memo=details.memo or "",
                nonce=nonce,
                fee=fee,
                network=network,
            )
            return SignedPaymentResult(
                signed_transaction=tx_hex,
                success=True,
                sender_address=sender_address,
            )
        except Exception as e:
            return SignedPaymentResult(
                signed_transaction="",
                success=False,
                error=str(e),
            )

    def sign_sbtc_transfer(self, details: PaymentDetails) -> SignedPaymentResult:
        """Sign sBTC token transfer (SIP-010 contract call)."""
        return self._sign_sip010_transfer(details, "sBTC")

    def sign_usdcx_transfer(self, details: PaymentDetails) -> SignedPaymentResult:
        """Sign USDCx token transfer (SIP-010 contract call)."""
        return self._sign_sip010_transfer(details, "USDCx")

    def _sign_sip010_transfer(
        self, details: PaymentDetails, token_label: str
    ) -> SignedPaymentResult:
        """Sign a SIP-010 token transfer."""
        try:
            network = details.network
            sender_address = private_key_to_address(self._private_key, network)

            if not details.token_contract:
                raise ValueError(f"Token contract required for {token_label} transfers")

            contract_address = details.token_contract.address
            contract_name = details.token_contract.name

            fn_args = [
                serialize_uint(details.amount),
                serialize_standard_principal(sender_address),
                serialize_standard_principal(details.recipient),
                (
                    serialize_optional_some(serialize_buffer_from_string(details.memo))
                    if details.memo
                    else serialize_optional_none()
                ),
            ]

            nonce = details.nonce if details.nonce is not None else 0
            fee = details.fee if details.fee is not None else 200

            tx_hex = build_contract_call(
                self._private_key,
                contract_address,
                contract_name,
                "transfer",
                fn_args,
                nonce=nonce,
                fee=fee,
                network=network,
            )
            return SignedPaymentResult(
                signed_transaction=tx_hex,
                success=True,
                sender_address=sender_address,
            )
        except Exception as e:
            return SignedPaymentResult(
                signed_transaction="",
                success=False,
                error=str(e),
            )

    def send_stx_transfer(self, details: PaymentDetails) -> PaymentResult:
        """Send STX token transfer (broadcasts via Stacks API)."""
        try:
            import json
            import urllib.request

            signed = self.sign_stx_transfer(details)
            if not signed.success:
                return PaymentResult(tx_id="", tx_raw="", success=False, error=signed.error)

            base_url = get_api_endpoint(details.network)
            url = f"{base_url}/v2/transactions"
            data = bytes.fromhex(signed.signed_transaction)
            req = urllib.request.Request(
                url,
                data=data,
                headers={"Content-Type": "application/octet-stream"},
                method="POST",
            )
            with urllib.request.urlopen(req, timeout=30) as resp:
                result = json.loads(resp.read())

            tx_id = result if isinstance(result, str) else result.get("txid", "")
            return PaymentResult(
                tx_id=tx_id,
                tx_raw=signed.signed_transaction,
                success=True,
            )
        except Exception as e:
            return PaymentResult(tx_id="", tx_raw="", success=False, error=str(e))

    def send_sbtc_transfer(self, details: PaymentDetails) -> PaymentResult:
        """Send sBTC token transfer."""
        return self._send_sip010_transfer(details, "sBTC")

    def send_usdcx_transfer(self, details: PaymentDetails) -> PaymentResult:
        """Send USDCx token transfer."""
        return self._send_sip010_transfer(details, "USDCx")

    def _send_sip010_transfer(
        self, details: PaymentDetails, token_label: str
    ) -> PaymentResult:
        """Send a SIP-010 token transfer."""
        try:
            import json
            import urllib.request

            signed = self._sign_sip010_transfer(details, token_label)
            if not signed.success:
                return PaymentResult(tx_id="", tx_raw="", success=False, error=signed.error)

            base_url = get_api_endpoint(details.network)
            url = f"{base_url}/v2/transactions"
            data = bytes.fromhex(signed.signed_transaction)
            req = urllib.request.Request(
                url,
                data=data,
                headers={"Content-Type": "application/octet-stream"},
                method="POST",
            )
            with urllib.request.urlopen(req, timeout=30) as resp:
                result = json.loads(resp.read())

            tx_id = result if isinstance(result, str) else result.get("txid", "")
            return PaymentResult(
                tx_id=tx_id,
                tx_raw=signed.signed_transaction,
                success=True,
            )
        except Exception as e:
            return PaymentResult(tx_id="", tx_raw="", success=False, error=str(e))

    @staticmethod
    def is_valid_payment_request(request: dict) -> bool:
        """Validate payment request structure."""
        return (
            isinstance(request, dict)
            and isinstance(request.get("maxAmountRequired"), str)
            and isinstance(request.get("resource"), str)
            and isinstance(request.get("payTo"), str)
            and isinstance(request.get("network"), str)
            and isinstance(request.get("nonce"), str)
            and isinstance(request.get("expiresAt"), str)
            and request.get("network") in ("mainnet", "testnet")
        )
