// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {Test} from "forge-std/Test.sol";
import {BridgeEscrowVault} from "../../../contracts/bridge/interop/BridgeEscrowVault.sol";
import {BridgeRegistry} from "../../../contracts/bridge/interop/BridgeRegistry.sol";
import {wXRP} from "../../../contracts/bridge/interop/wXRP.sol";
import {MintBurnController} from "../../../contracts/bridge/interop/MintBurnController.sol";
import {BridgeVerifier} from "../../../contracts/bridge/interop/BridgeVerifier.sol";

contract BridgeIntegrationTest is Test {
    BridgeEscrowVault public vault;
    BridgeRegistry public registry;
    wXRP public wxrp;
    MintBurnController public controller;
    BridgeVerifier public verifier;
    
    address public admin = address(0x1);
    address public operator = address(0x2);
    address public user = address(0x5);
    address public hsmSigner = address(0x4);
    uint256 public attestor1Pk = 0x10;
    uint256 public attestor2Pk = 0x11;
    uint256 public attestor3Pk = 0x12;
    address public attestor1;
    address public attestor2;
    address public attestor3;

    function setUp() public {
        attestor1 = vm.addr(attestor1Pk);
        attestor2 = vm.addr(attestor2Pk);
        attestor3 = vm.addr(attestor3Pk);
        vm.startPrank(admin);
        
        // Deploy registry
        registry = new BridgeRegistry(admin);
        registry.grantRole(registry.REGISTRAR_ROLE(), admin);
        
        // Register Polygon destination
        registry.registerDestination(137, "Polygon", 128, 3600, 10, address(0x200));
        
        // Deploy vault
        vault = new BridgeEscrowVault(admin);
        vault.grantRole(vault.OPERATOR_ROLE(), operator);
        
        // Deploy wXRP
        wxrp = new wXRP(admin);
        
        // Deploy controller
        controller = new MintBurnController(admin, address(wxrp), hsmSigner);
        wxrp.grantRole(wxrp.MINTER_ROLE(), address(controller));
        wxrp.grantRole(wxrp.BURNER_ROLE(), address(controller));
        
        // Deploy verifier
        verifier = new BridgeVerifier(admin, 6667); // 66.67% quorum
        verifier.addAttestor(attestor1, 1000);
        verifier.addAttestor(attestor2, 1000);
        verifier.addAttestor(attestor3, 1000);
        
        vm.stopPrank();
        
        vm.deal(user, 100 ether);
    }

    function test_FullEVMBridgeFlow() public {
        // 1. User deposits native ETH
        vm.startPrank(user);
        bytes32 transferId = vault.depositNative{value: 1 ether}(
            BridgeEscrowVault.DestinationType.EVM,
            abi.encodePacked(address(0x100)),
            3600,
            keccak256("test-transfer")
        );
        vm.stopPrank();
        
        // 2. Operator confirms deposit
        vm.startPrank(operator);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.DEPOSIT_CONFIRMED);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.ROUTE_SELECTED);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.EXECUTING);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.DESTINATION_SENT);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.FINALITY_CONFIRMED);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.COMPLETED);
        vm.stopPrank();
        
        // Verify final state
        BridgeEscrowVault.Transfer memory transfer = vault.getTransfer(transferId);
        assertEq(uint8(transfer.status), uint8(BridgeEscrowVault.TransferStatus.COMPLETED));
    }

    function test_XRPLBridgeFlow() public {
        // 1. User deposits for XRPL bridge
        vm.startPrank(user);
        bytes32 transferId = vault.depositNative{value: 1 ether}(
            BridgeEscrowVault.DestinationType.XRPL,
            abi.encodePacked(address(0x200)),
            3600,
            keccak256("xrpl-transfer")
        );
        vm.stopPrank();
        
        // 2. Operator processes XRPL transfer
        vm.startPrank(operator);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.DEPOSIT_CONFIRMED);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.ROUTE_SELECTED);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.EXECUTING);
        // In production, XRPL payment would be executed here
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.DESTINATION_SENT);
        vault.updateTransferStatus(transferId, BridgeEscrowVault.TransferStatus.COMPLETED);
        vm.stopPrank();
        
        BridgeEscrowVault.Transfer memory transfer = vault.getTransfer(transferId);
        assertEq(uint8(transfer.status), uint8(BridgeEscrowVault.TransferStatus.COMPLETED));
    }

    function test_wXRP_MintBurn() public {
        bytes32 xrplTxHash = keccak256("xrpl-lock-tx");
        
        // Mint wXRP
        vm.startPrank(admin);
        // In production, this would be called by controller with HSM signature
        wxrp.mint(user, 1000 * 10**18, xrplTxHash);
        vm.stopPrank();
        
        assertEq(wxrp.balanceOf(user), 1000 * 10**18);
        
        // Burn wXRP
        vm.startPrank(admin);
        wxrp.burnFrom(user, 500 * 10**18, keccak256("xrpl-unlock-tx"));
        vm.stopPrank();
        
        assertEq(wxrp.balanceOf(user), 500 * 10**18);
    }

    function test_AttestationQuorum() public {
        bytes32 transferId = keccak256("test-attestation");
        bytes32 proofHash = keccak256("proof-data");
        
        uint256 deadline = block.timestamp + 3600;

        // Attestor 1 submits attestation
        vm.startPrank(attestor1);
        bytes memory sig1 = _signAttestation(attestor1Pk, transferId, proofHash, 1, deadline);
        verifier.submitAttestation(
            BridgeVerifier.Attestation({
                transferId: transferId,
                proofHash: proofHash,
                nonce: 1,
                deadline: deadline,
                signature: sig1
            })
        );
        vm.stopPrank();
        
        // Attestor 2 submits attestation
        vm.startPrank(attestor2);
        bytes memory sig2 = _signAttestation(attestor2Pk, transferId, proofHash, 2, deadline);
        verifier.submitAttestation(
            BridgeVerifier.Attestation({
                transferId: transferId,
                proofHash: proofHash,
                nonce: 2,
                deadline: deadline,
                signature: sig2
            })
        );
        vm.stopPrank();
        
        // Check quorum (2/3 = 66.67% >= 66.67%)
        (bool quorumMet, uint256 totalWeight, uint256 requiredWeight) = verifier.verifyQuorum(transferId);
        assertTrue(quorumMet);
        assertGe(totalWeight, requiredWeight);
    }

    function _signAttestation(
        uint256 signerPk,
        bytes32 transferId,
        bytes32 proofHash,
        uint256 nonce,
        uint256 deadline
    ) internal view returns (bytes memory) {
        bytes32 typeHash = keccak256("Attestation(bytes32 transferId,bytes32 proofHash,uint256 nonce,uint256 deadline)");
        bytes32 structHash = keccak256(abi.encode(typeHash, transferId, proofHash, nonce, deadline));
        bytes32 domainSeparator = keccak256(abi.encode(
            keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
            keccak256(bytes("BridgeVerifier")),
            keccak256(bytes("1")),
            block.chainid,
            address(verifier)
        ));
        bytes32 hash = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
        (uint8 v, bytes32 r, bytes32 s) = vm.sign(signerPk, hash);
        return abi.encodePacked(r, s, v);
    }
}
