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

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../../interfaces/IChainAdapter.sol";

/**
 * @title XRPLAdapter
 * @notice Bridge adapter for XRP Ledger (XRPL)
 * @dev Uses oracle/relayer pattern for non-EVM chain integration
 */
contract XRPLAdapter is IChainAdapter, AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;

    bytes32 public constant BRIDGE_OPERATOR_ROLE = keccak256("BRIDGE_OPERATOR_ROLE");
    bytes32 public constant ORACLE_ROLE = keccak256("ORACLE_ROLE");
    
    bool public isActive;
    
    mapping(bytes32 => BridgeRequest) public bridgeRequests;
    mapping(bytes32 => bytes32) public xrplTxHashes; // requestId => xrplTxHash
    mapping(address => uint256) public nonces;
    
    // XRPL address validation: starts with 'r', 25-35 chars
    mapping(string => bool) public validatedXRPLAddresses;

    event XRPLBridgeInitiated(
        bytes32 indexed requestId,
        address indexed sender,
        address indexed token,
        uint256 amount,
        string xrplDestination,
        uint32 destinationTag
    );
    
    event XRPLBridgeConfirmed(
        bytes32 indexed requestId,
        bytes32 indexed xrplTxHash,
        uint256 ledgerIndex
    );

    constructor(address admin) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(BRIDGE_OPERATOR_ROLE, admin);
        _grantRole(ORACLE_ROLE, admin);
        isActive = true;
    }

    function getChainType() external pure override returns (string memory) {
        return "XRPL";
    }

    function getChainIdentifier() external pure override returns (uint256 chainId, string memory identifier) {
        return (0, "XRPL-Mainnet");
    }

    function validateDestination(bytes calldata destination) external pure override returns (bool) {
        string memory addr = string(destination);
        bytes memory addrBytes = bytes(addr);
        
        // XRPL addresses: r + 25-34 base58 chars
        if (addrBytes.length < 26 || addrBytes.length > 35) return false;
        if (addrBytes[0] != 'r') return false;
        
        // Basic validation - full validation requires base58 decoding
        return true;
    }

    /**
     * @notice Convert XRP drops to wei (1 XRP = 1,000,000 drops)
     */
    function dropsToWei(uint64 drops) public pure returns (uint256) {
        return uint256(drops) * 1e12; // 1 drop = 0.000001 XRP, 1 XRP = 1e18 wei
    }

    /**
     * @notice Convert wei to XRP drops
     */
    function weiToDrops(uint256 weiAmount) public pure returns (uint64) {
        return uint64(weiAmount / 1e12);
    }

    function bridge(
        address token,
        uint256 amount,
        bytes calldata destination,
        bytes calldata recipient
    ) external payable override nonReentrant returns (bytes32 requestId) {
        require(isActive, "Adapter inactive");
        require(amount > 0, "Zero amount");
        require(this.validateDestination(destination), "Invalid XRPL address");
        
        string memory xrplDestination = string(destination);
        uint32 destinationTag = 0;
        
        // Parse destination tag if provided in recipient bytes
        if (recipient.length >= 4) {
            destinationTag = abi.decode(recipient, (uint32));
        }
        
        requestId = keccak256(abi.encodePacked(
            msg.sender,
            token,
            amount,
            xrplDestination,
            destinationTag,
            nonces[msg.sender]++,
            block.timestamp
        ));
        
        // Lock tokens on EVM side
        if (token == address(0)) {
            require(msg.value >= amount, "Insufficient ETH");
        } else {
            IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
        }
        
        bridgeRequests[requestId] = BridgeRequest({
            sender: msg.sender,
            token: token,
            amount: amount,
            destinationData: destination,
            requestId: requestId,
            status: BridgeStatus.Locked,
            createdAt: block.timestamp,
            completedAt: 0
        });
        
        emit XRPLBridgeInitiated(requestId, msg.sender, token, amount, xrplDestination, destinationTag);
        
        return requestId;
    }

    /**
     * @notice Oracle confirms XRPL transaction
     */
    function confirmXRPLTransaction(
        bytes32 requestId,
        bytes32 xrplTxHash,
        uint256 ledgerIndex
    ) external onlyRole(ORACLE_ROLE) {
        BridgeRequest storage request = bridgeRequests[requestId];
        require(request.status == BridgeStatus.Locked, "Invalid status");
        
        request.status = BridgeStatus.Confirmed;
        request.completedAt = block.timestamp;
        xrplTxHashes[requestId] = xrplTxHash;
        
        emit XRPLBridgeConfirmed(requestId, xrplTxHash, ledgerIndex);
    }

    function getBridgeStatus(bytes32 requestId) 
        external view override returns (BridgeRequest memory) {
        return bridgeRequests[requestId];
    }

    function cancelBridge(bytes32 requestId) external override returns (bool) {
        BridgeRequest storage request = bridgeRequests[requestId];
        require(request.status == BridgeStatus.Pending || request.status == BridgeStatus.Locked, "Cannot cancel");
        require(msg.sender == request.sender, "Not request sender");
        
        // Refund tokens
        if (request.token == address(0)) {
            payable(request.sender).transfer(request.amount);
        } else {
            IERC20(request.token).safeTransfer(request.sender, request.amount);
        }
        
        request.status = BridgeStatus.Cancelled;
        return true;
    }

    function estimateFee(
        address token,
        uint256 amount,
        bytes calldata destination
    ) external pure override returns (uint256 fee) {
        // XRPL fees are very low (~0.000012 XRP per transaction)
        return 12000; // 0.000012 XRP in drops
    }

    function setIsActive(bool _isActive) external onlyRole(DEFAULT_ADMIN_ROLE) {
        isActive = _isActive;
    }
}
