// 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";
import "../../UniversalCCIPBridge.sol";

/**
 * @title XDCAdapter
 * @notice Bridge adapter for XDC Network (EVM-compatible with xdc address prefix)
 * @dev XDC uses xdc prefix instead of 0x for addresses
 */
contract XDCAdapter is IChainAdapter, AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;

    bytes32 public constant BRIDGE_OPERATOR_ROLE = keccak256("BRIDGE_OPERATOR_ROLE");
    
    uint256 public constant XDC_MAINNET = 50;
    uint256 public constant XDC_APOTHEM_TESTNET = 51;
    
    UniversalCCIPBridge public universalBridge;
    bool public isActive;
    
    mapping(bytes32 => BridgeRequest) public bridgeRequests;
    mapping(address => uint256) public nonces;

    event XDCBridgeInitiated(
        bytes32 indexed requestId,
        address indexed sender,
        address indexed token,
        uint256 amount,
        string xdcDestination
    );
    
    event XDCBridgeConfirmed(
        bytes32 indexed requestId,
        bytes32 indexed xdcTxHash
    );

    constructor(address admin, address _bridge) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(BRIDGE_OPERATOR_ROLE, admin);
        universalBridge = UniversalCCIPBridge(payable(_bridge));
        isActive = true;
    }

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

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

    function validateDestination(bytes calldata destination) external pure override returns (bool) {
        string memory addr = string(destination);
        bytes memory addrBytes = bytes(addr);
        
        if (addrBytes.length != 43) return false;
        if (addrBytes[0] != 'x' || addrBytes[1] != 'd' || addrBytes[2] != 'c') return false;
        
        for (uint256 i = 3; i < 43; i++) {
            bytes1 char = addrBytes[i];
            if (!((char >= 0x30 && char <= 0x39) || (char >= 0x61 && char <= 0x66))) {
                return false;
            }
        }
        return true;
    }

    function convertXdcToEth(string memory xdcAddr) public pure returns (address) {
        bytes memory xdcBytes = bytes(xdcAddr);
        require(xdcBytes.length == 43, "Invalid XDC address length");
        require(xdcBytes[0] == 'x' && xdcBytes[1] == 'd' && xdcBytes[2] == 'c', "Invalid XDC prefix");
        // Parse 40 hex chars (20 bytes) to address; do not use bytes32(hexBytes) which truncates 42 bytes
        uint160 result = 0;
        for (uint256 i = 0; i < 40; i++) {
            result = result * 16 + _hexCharToNibble(xdcBytes[i + 3]);
        }
        return address(result);
    }

    function _hexCharToNibble(bytes1 c) internal pure returns (uint8) {
        if (c >= 0x30 && c <= 0x39) return uint8(c) - 0x30; // '0'-'9'
        if (c >= 0x61 && c <= 0x66) return uint8(c) - 0x61 + 10; // 'a'-'f'
        if (c >= 0x41 && c <= 0x46) return uint8(c) - 0x41 + 10; // 'A'-'F'
        revert("Invalid hex character");
    }

    function convertEthToXdc(address ethAddr) public pure returns (string memory) {
        bytes20 addr = bytes20(ethAddr);
        bytes memory hexString = new bytes(43);
        hexString[0] = 'x';
        hexString[1] = 'd';
        hexString[2] = 'c';
        
        for (uint256 i = 0; i < 20; i++) {
            uint8 byteValue = uint8(addr[i]);
            uint8 high = byteValue >> 4;
            uint8 low = byteValue & 0x0f;
            
            hexString[3 + i * 2] = bytes1(high < 10 ? 48 + high : 87 + high);
            hexString[4 + i * 2] = bytes1(low < 10 ? 48 + low : 87 + low);
        }
        
        return string(hexString);
    }

    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");
        
        string memory xdcDestination = string(destination);
        require(this.validateDestination(destination), "Invalid XDC address");
        
        address evmRecipient = convertXdcToEth(xdcDestination);
        
        requestId = keccak256(abi.encodePacked(
            msg.sender,
            token,
            amount,
            xdcDestination,
            nonces[msg.sender]++,
            block.timestamp
        ));
        
        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
        });
        
        UniversalCCIPBridge.BridgeOperation memory op = UniversalCCIPBridge.BridgeOperation({
            token: token,
            amount: amount,
            destinationChain: uint64(XDC_MAINNET),
            recipient: evmRecipient,
            assetType: bytes32(0),
            usePMM: false,
            useVault: false,
            complianceProof: "",
            vaultInstructions: ""
        });
        
        bytes32 messageId = universalBridge.bridge{value: msg.value}(op);
        
        emit XDCBridgeInitiated(requestId, msg.sender, token, amount, xdcDestination);
        
        return requestId;
    }

    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");
        
        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 view override returns (uint256 fee) {
        return 1000000000000000;
    }

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

    function confirmBridge(bytes32 requestId, bytes32 xdcTxHash) 
        external onlyRole(BRIDGE_OPERATOR_ROLE) {
        BridgeRequest storage request = bridgeRequests[requestId];
        require(request.status == BridgeStatus.Locked, "Invalid status");
        
        request.status = BridgeStatus.Confirmed;
        request.completedAt = block.timestamp;
        
        emit XDCBridgeConfirmed(requestId, xdcTxHash);
    }
}
