"""Tests for bob.x402 — x402 receipt parsing and submission."""

import base64
import json
from unittest.mock import MagicMock, patch

import pytest

from bob.x402 import (
    X402Receipt,
    BobX402Options,
    parse_payment_response,
    submit_receipt,
)


# ---------------------------------------------------------------------------
# parse_payment_response
# ---------------------------------------------------------------------------


class TestParsePaymentResponse:
    def test_base64_encoded_json(self):
        data = {"network": "eip155:8453", "payer": "0xABC", "txHash": "0xdef"}
        header = base64.b64encode(json.dumps(data).encode()).decode()
        receipt = parse_payment_response(header)
        assert receipt is not None
        assert receipt.network == "eip155:8453"
        assert receipt.payer == "0xABC"
        assert receipt.tx_hash == "0xdef"

    def test_plain_json(self):
        data = {"network": "eip155:1", "payer": "0x123"}
        header = json.dumps(data)
        receipt = parse_payment_response(header)
        assert receipt is not None
        assert receipt.network == "eip155:1"

    def test_nested_result_key(self):
        data = {"result": {"network": "eip155:8453", "payer": "0xABC", "txHash": "0x1"}}
        header = json.dumps(data)
        receipt = parse_payment_response(header)
        assert receipt is not None
        assert receipt.tx_hash == "0x1"

    def test_garbage_input(self):
        assert parse_payment_response("not-json-or-base64!!!") is None

    def test_empty_object(self):
        assert parse_payment_response("{}") is None

    def test_tx_hash_aliases(self):
        for key in ("txHash", "tx_hash", "transactionHash"):
            data = {"network": "eip155:8453", "payer": "0x1", key: "0xabc"}
            receipt = parse_payment_response(json.dumps(data))
            assert receipt is not None
            assert receipt.tx_hash == "0xabc", f"failed for key={key}"

    def test_amount_as_int(self):
        data = {"network": "eip155:8453", "payer": "0x1", "amount": 10000}
        receipt = parse_payment_response(json.dumps(data))
        assert receipt is not None
        assert receipt.amount == "10000"


# ---------------------------------------------------------------------------
# submit_receipt
# ---------------------------------------------------------------------------


class TestSubmitReceipt:
    def test_submits_valid_receipt(self):
        bob = MagicMock()
        receipt = X402Receipt(
            tx_hash="0xabc",
            network="eip155:8453",
            payer="0xPayer",
            payee="0xPayee",
            amount="10000",
        )
        submit_receipt(receipt, bob)
        bob.import_x402_receipt.assert_called_once_with(
            tx="0xabc",
            network="eip155:8453",
            payer="0xPayer",
            payee="0xPayee",
            amount="10000",
            asset=None,
            resource_url=None,
        )

    def test_skips_without_tx_hash(self):
        bob = MagicMock()
        errors = []
        opts = BobX402Options(on_receipt_error=lambda e, r: errors.append(str(e)))
        receipt = X402Receipt(network="eip155:8453", payer="0x1")
        submit_receipt(receipt, bob, options=opts)
        bob.import_x402_receipt.assert_not_called()
        assert len(errors) == 1
        assert "tx_hash" in errors[0].lower()

    def test_calls_on_receipt_submitted(self):
        bob = MagicMock()
        submitted = []
        opts = BobX402Options(on_receipt_submitted=lambda r: submitted.append(r))
        receipt = X402Receipt(tx_hash="0x1", network="eip155:8453", payer="0x1")
        submit_receipt(receipt, bob, options=opts)
        assert len(submitted) == 1

    def test_error_does_not_raise(self):
        bob = MagicMock()
        bob.import_x402_receipt.side_effect = Exception("network error")
        errors = []
        opts = BobX402Options(on_receipt_error=lambda e, r: errors.append(str(e)))
        receipt = X402Receipt(tx_hash="0x1", network="eip155:8453", payer="0x1")
        # Should not raise
        submit_receipt(receipt, bob, options=opts)
        assert len(errors) == 1
        assert "network error" in errors[0]
