"""Tests for utility functions."""

from x402_stacks.utils import (
    micro_stx_to_stx,
    stx_to_micro_stx,
    sats_to_btc,
    btc_to_sats,
    usdcx_to_micro_usdcx,
    micro_usdcx_to_usdcx,
    is_valid_stacks_address,
    get_address_network,
    truncate_address,
    get_api_endpoint,
    get_explorer_url,
    format_payment_amount,
    parse_payment_memo,
    create_payment_memo,
    estimate_fee,
    is_payment_request_expired,
    create_expiration_timestamp,
    network_to_caip2,
    network_from_caip2,
    is_valid_stacks_caip2,
    asset_to_v2,
    asset_from_v2,
    generate_keypair,
    get_token_symbol,
    get_token_decimals,
    get_token_smallest_unit,
    get_default_sbtc_contract,
    get_default_usdcx_contract,
)
import pytest


class TestCurrencyConversion:
    def test_stx_roundtrip(self):
        assert stx_to_micro_stx(1) == 1_000_000
        assert micro_stx_to_stx(1_000_000) == "1.000000"

    def test_btc_roundtrip(self):
        assert btc_to_sats(1) == 100_000_000
        assert sats_to_btc(100_000_000) == "1.00000000"

    def test_usdcx_roundtrip(self):
        assert usdcx_to_micro_usdcx(1) == 1_000_000
        assert micro_usdcx_to_usdcx(1_000_000) == "1.000000"

    def test_string_input(self):
        assert stx_to_micro_stx("2.5") == 2_500_000
        assert micro_stx_to_stx("500000") == "0.500000"


class TestAddressValidation:
    def test_valid_mainnet(self):
        assert is_valid_stacks_address("SP000000000000000000002Q6VF78")

    def test_valid_testnet(self):
        assert is_valid_stacks_address("ST000000000000000000002AMW42H")

    def test_invalid(self):
        assert not is_valid_stacks_address("invalid")
        assert not is_valid_stacks_address("")
        assert not is_valid_stacks_address("0x123")

    def test_get_network(self):
        assert get_address_network("SP000000000000000000002Q6VF78") == "mainnet"
        assert get_address_network("ST000000000000000000002AMW42H") == "testnet"
        assert get_address_network("invalid") is None

    def test_truncate(self):
        assert truncate_address("SP000000000000000000002Q6VF78", 4) == "SP00...VF78"
        assert truncate_address("SHORT") == "SHORT"


class TestNetworkUtils:
    def test_api_endpoint(self):
        assert "mainnet" in get_api_endpoint("mainnet")
        assert "testnet" in get_api_endpoint("testnet")

    def test_explorer_url(self):
        url = get_explorer_url("abc123", "mainnet")
        assert "abc123" in url
        assert "testnet" not in url

        url = get_explorer_url("abc123", "testnet")
        assert "testnet" in url


class TestPaymentUtils:
    def test_format_stx(self):
        result = format_payment_amount(1_000_000, token_type="STX")
        assert "1.000000" in result
        assert "STX" in result

    def test_format_sbtc(self):
        result = format_payment_amount(100_000_000, token_type="sBTC")
        assert "sBTC" in result

    def test_format_no_symbol(self):
        result = format_payment_amount(1_000_000, include_symbol=False)
        assert "STX" not in result

    def test_parse_memo(self):
        result = parse_payment_memo("x402:/api/data,nonce=abc123")
        assert result["resource"] == "/api/data"
        assert result["nonce"] == "abc123"

    def test_parse_memo_with_custom(self):
        result = parse_payment_memo("x402:/api,nonce=abc,foo=bar")
        assert result["nonce"] == "abc"
        assert result["custom"]["foo"] == "bar"

    def test_parse_invalid_memo(self):
        result = parse_payment_memo("not-x402")
        assert result == {}

    def test_create_memo(self):
        memo = create_payment_memo("/api/data", "abc123")
        assert memo == "x402:/api/data,nonce=abc123"

    def test_estimate_fee(self):
        assert estimate_fee(180, 1) == 180


class TestTimingUtils:
    def test_expired(self):
        assert is_payment_request_expired("2020-01-01T00:00:00Z")

    def test_not_expired(self):
        assert not is_payment_request_expired("2099-01-01T00:00:00Z")

    def test_create_timestamp(self):
        ts = create_expiration_timestamp(300)
        assert not is_payment_request_expired(ts)


class TestCAIP2:
    def test_network_to_caip2(self):
        assert network_to_caip2("mainnet") == "stacks:1"
        assert network_to_caip2("testnet") == "stacks:2147483648"

    def test_network_from_caip2(self):
        assert network_from_caip2("stacks:1") == "mainnet"
        assert network_from_caip2("stacks:2147483648") == "testnet"

    def test_invalid_caip2(self):
        with pytest.raises(ValueError):
            network_from_caip2("stacks:999")
        with pytest.raises(ValueError):
            network_from_caip2("ethereum:1")

    def test_is_valid(self):
        assert is_valid_stacks_caip2("stacks:1")
        assert is_valid_stacks_caip2("stacks:2147483648")
        assert not is_valid_stacks_caip2("stacks:999")

    def test_asset_to_v2(self):
        assert asset_to_v2("STX") == "STX"
        assert asset_to_v2("sBTC") == "SBTC"
        assert asset_to_v2("USDCx") == "USDCX"

    def test_asset_from_v2(self):
        assert asset_from_v2("STX")["token_type"] == "STX"
        assert asset_from_v2("SBTC")["token_type"] == "sBTC"
        assert asset_from_v2("USDCX")["token_type"] == "USDCx"

    def test_asset_from_v2_contract(self):
        result = asset_from_v2("ST123.sbtc-token")
        assert result["token_type"] == "sBTC"
        assert result["token_contract"].address == "ST123"


class TestTokenUtils:
    def test_symbols(self):
        assert get_token_symbol("STX") == "STX"
        assert get_token_symbol("sBTC") == "sBTC"

    def test_decimals(self):
        assert get_token_decimals("STX") == 6
        assert get_token_decimals("sBTC") == 8

    def test_smallest_unit(self):
        assert get_token_smallest_unit("STX") == "microSTX"
        assert get_token_smallest_unit("sBTC") == "sats"

    def test_default_contracts(self):
        sbtc = get_default_sbtc_contract("mainnet")
        assert sbtc.name == "sbtc-token"

        usdcx = get_default_usdcx_contract("testnet")
        assert usdcx.name == "usdcx"


class TestKeypair:
    def test_generate(self):
        kp = generate_keypair("testnet")
        assert "private_key" in kp
        assert "public_key" in kp
        assert "address" in kp
        assert kp["address"].startswith("ST")

    def test_mainnet(self):
        kp = generate_keypair("mainnet")
        assert kp["address"].startswith("SP")
