"""V2 core interceptor logic — base64, CAIP-2, payment selection for x402 V2 protocol."""

from __future__ import annotations

import base64
import json
import time
from typing import Any, Optional

from ..types import NetworkType, StacksAccount, TokenContract
from ..types_v2 import (
    PaymentRequiredV2,
    PaymentPayloadV2,
    PaymentRequirementsV2,
    SettlementResponseV2,
    StacksPayloadV2,
    ResourceInfo,
    X402_HEADERS,
)
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 network_from_caip2, asset_from_v2


def private_key_to_account(
    private_key: str, network: NetworkType = "testnet"
) -> StacksAccount:
    """Create a Stacks account from a private key."""
    address = private_key_to_address(private_key, network)
    return StacksAccount(address=address, private_key=private_key, network=network)


def decode_payment_response(header: Optional[str]) -> Optional[SettlementResponseV2]:
    """Decode the payment-response header from base64 JSON."""
    if not header:
        return None
    try:
        decoded = base64.b64decode(header).decode("utf-8")
        return SettlementResponseV2.model_validate(json.loads(decoded))
    except Exception:
        return None


def decode_payment_required(header: Optional[str]) -> Optional[PaymentRequiredV2]:
    """Decode the payment-required header from base64 JSON."""
    if not header:
        return None
    try:
        decoded = base64.b64decode(header).decode("utf-8")
        return PaymentRequiredV2.model_validate(json.loads(decoded))
    except Exception:
        return None


def encode_payment_payload(payload: PaymentPayloadV2) -> str:
    """Encode a payment payload to base64 JSON."""
    return base64.b64encode(
        json.dumps(payload.model_dump(by_alias=True)).encode()
    ).decode()


def _get_token_contract_for_asset(asset: str, network: NetworkType) -> Optional[TokenContract]:
    """Get token contract from known assets or network defaults."""
    info = asset_from_v2(asset)
    if "token_contract" in info:
        return info["token_contract"]

    token_type = info["token_type"]
    if token_type == "sBTC":
        if network == "mainnet":
            return TokenContract(
                address="SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4",
                name="sbtc-token",
            )
        return TokenContract(
            address="ST1F7QA2MDF17S807EPA36TSS8AMEFY4KA9TVGWXT",
            name="sbtc-token",
        )
    if token_type == "USDCx":
        if network == "mainnet":
            return TokenContract(
                address="SP120SBRBQJ00MCWS7TM5R8WJNTTKD5K0HFRC2CNE",
                name="usdcx",
            )
        return TokenContract(
            address="ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM",
            name="usdcx",
        )
    return None


def sign_payment_v2(
    payment_requirements: PaymentRequirementsV2,
    account: StacksAccount,
) -> str:
    """Sign a payment transaction based on x402 v2 payment requirements.

    Returns the signed transaction hex.
    """
    amount = int(payment_requirements.amount)
    info = asset_from_v2(payment_requirements.asset)
    token_type = info["token_type"]
    v1_network = network_from_caip2(payment_requirements.network)
    memo = f"x402:{int(time.time()):x}"[:34]

    # Fetch current nonce from the network
    nonce = fetch_account_nonce(account.address, v1_network)

    if token_type in ("sBTC", "USDCx"):
        token_contract = _get_token_contract_for_asset(
            payment_requirements.asset, v1_network
        )
        if not token_contract:
            raise ValueError(f"Token contract required for {token_type} payments")

        fn_args = [
            serialize_uint(amount),
            serialize_standard_principal(account.address),
            serialize_standard_principal(payment_requirements.pay_to),
            (
                serialize_optional_some(serialize_buffer_from_string(memo))
                if memo
                else serialize_optional_none()
            ),
        ]
        return build_contract_call(
            account.private_key,
            token_contract.address,
            token_contract.name,
            "transfer",
            fn_args,
            nonce=nonce,
            network=v1_network,
        )
    else:
        return build_stx_transfer(
            account.private_key,
            payment_requirements.pay_to,
            amount,
            memo=memo,
            nonce=nonce,
            network=v1_network,
        )


def select_payment_option(
    accepts: list[PaymentRequirementsV2],
    account: StacksAccount,
) -> Optional[PaymentRequirementsV2]:
    """Select the first Stacks-compatible payment option."""
    for opt in accepts:
        if not opt.network.startswith("stacks:"):
            continue
        try:
            v1_net = network_from_caip2(opt.network)
            if v1_net == account.network:
                return opt
        except ValueError:
            continue
    return None


def get_payment_response_from_headers(
    headers: dict[str, str],
) -> Optional[SettlementResponseV2]:
    """Extract payment response from response headers."""
    header = headers.get(X402_HEADERS["PAYMENT_RESPONSE"])
    return decode_payment_response(header)
