"""
Unit tests for FreeExile Web3 Crypto Payment Engine.
Validates crypto order generation, Pyth/Chainlink oracle price conversions,
on-chain RPC transaction verification (EVM & Solana), anti-double-spending, and expiration timeouts.
"""

import unittest
import time
import sys
import os

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../server")))

from shop.receipt_verifier import (
    Web3CryptoVerifier,
    CryptoChain,
    CryptoToken,
    Web3TransactionVerificationError,
)
from shop.currency_exchange import CurrencyExchangeEngine


class TestWeb3CryptoPayment(unittest.TestCase):
    def setUp(self):
        self.fx = CurrencyExchangeEngine()
        self.verifier = Web3CryptoVerifier()

    def test_oracle_crypto_price_conversion(self):
        """Converting a $20 pack to USDT, USDC, ETH, and SOL using Oracle prices."""
        usd_price = 20.0

        # Stablecoins (1:1 USD)
        usdt_amount = self.verifier.convert_usd_to_crypto_amount(usd_price, CryptoToken.TOKEN_USDT)
        usdc_amount = self.verifier.convert_usd_to_crypto_amount(usd_price, CryptoToken.TOKEN_USDC)
        self.assertEqual(usdt_amount, 20.0)
        self.assertEqual(usdc_amount, 20.0)

        # ETH ($3000/ETH -> $20 = 20/3000 = ~0.006666 ETH)
        eth_amount = self.verifier.convert_usd_to_crypto_amount(usd_price, CryptoToken.TOKEN_ETH)
        self.assertAlmostEqual(eth_amount, 0.006666, places=4)

        # SOL ($150/SOL -> $20 = 20/150 = ~0.1333 SOL)
        sol_amount = self.verifier.convert_usd_to_crypto_amount(usd_price, CryptoToken.TOKEN_SOL)
        self.assertAlmostEqual(sol_amount, 0.1333, places=3)

    def test_crypto_order_creation_and_expiration(self):
        """Order must contain merchant vault address, chain ID, and a valid expiration window."""
        order = self.verifier.create_crypto_order(
            account_id="acc_crypto_whale_88",
            product_id="coin_pack_1000",
            fiat_usd_amount=10.0,
            chain=CryptoChain.CHAIN_ARBITRUM,
            token=CryptoToken.TOKEN_USDT,
            buyer_wallet="0x71C...aB3"
        )
        self.assertIsNotNone(order.order_id)
        self.assertTrue(order.merchant_vault_address.startswith("0x"))
        self.assertEqual(order.required_token_amount, 10.0)
        self.assertGreater(order.expires_at_ts, time.time())
        self.assertFalse(order.is_expired())

    def test_successful_on_chain_verification_and_settlement(self):
        """Valid transaction with matching recipient, token contract, and amount passes verification."""
        order = self.verifier.create_crypto_order(
            account_id="acc_crypto_whale_88",
            product_id="coin_pack_1000",
            fiat_usd_amount=10.0,
            chain=CryptoChain.CHAIN_POLYGON,
            token=CryptoToken.TOKEN_USDT,
            buyer_wallet="0x1111222233334444555566667777888899990000"
        )

        mock_tx_hash = "0x9876543210abcdef1234567890abcdef1234567890abcdef1234567890abcdef"

        # Register simulated on-chain transaction
        self.verifier.mock_register_on_chain_tx(
            tx_hash=mock_tx_hash,
            chain=CryptoChain.CHAIN_POLYGON,
            from_address="0x1111222233334444555566667777888899990000",
            to_address=order.merchant_vault_address,
            token=CryptoToken.TOKEN_USDT,
            amount_transferred=10.0,
            confirmations=15,
            is_confirmed=True
        )

        verified, details = self.verifier.verify_and_settle_order(
            order_id=order.order_id,
            tx_hash=mock_tx_hash,
            chain=CryptoChain.CHAIN_POLYGON
        )
        self.assertTrue(verified)
        self.assertEqual(details["order_id"], order.order_id)
        self.assertEqual(details["status"], "SETTLED")

    def test_anti_double_spending_protection(self):
        """Reusing the same tx_hash for a second order MUST be strictly rejected."""
        order1 = self.verifier.create_crypto_order(
            account_id="acc_user_1",
            product_id="coin_pack_100",
            fiat_usd_amount=1.0,
            chain=CryptoChain.CHAIN_SOLANA,
            token=CryptoToken.TOKEN_SOL,
            buyer_wallet="SolBuyer11111111111111111111111111111111111"
        )
        mock_tx_hash = "SolTxHash_UniqueSignature_ABC_123"

        self.verifier.mock_register_on_chain_tx(
            tx_hash=mock_tx_hash,
            chain=CryptoChain.CHAIN_SOLANA,
            from_address="SolBuyer11111111111111111111111111111111111",
            to_address=order1.merchant_vault_address,
            token=CryptoToken.TOKEN_SOL,
            amount_transferred=order1.required_token_amount,
            confirmations=32,
            is_confirmed=True
        )

        # First settlement succeeds
        v1, _ = self.verifier.verify_and_settle_order(order1.order_id, mock_tx_hash, CryptoChain.CHAIN_SOLANA)
        self.assertTrue(v1)

        # Attacker tries to use the same tx_hash for a second order
        order2 = self.verifier.create_crypto_order(
            account_id="acc_user_2",
            product_id="coin_pack_100",
            fiat_usd_amount=1.0,
            chain=CryptoChain.CHAIN_SOLANA,
            token=CryptoToken.TOKEN_SOL,
            buyer_wallet="SolAttacker222222222222222222222222222222222"
        )
        with self.assertRaises(Web3TransactionVerificationError) as ctx:
            self.verifier.verify_and_settle_order(order2.order_id, mock_tx_hash, CryptoChain.CHAIN_SOLANA)
        self.assertIn("DOUBLE_SPENDING_DETECTED", str(ctx.exception))


if __name__ == "__main__":
    unittest.main()
