// 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 "../../interfaces/IZedxionTransport.sol";

/**
 * @title ZedxionAdapter
 * @notice Bridge adapter for ZEDXION Smart Chain (ZED20), chainId 83872.
 * @dev CCIP does not list 83872. Wire ZedxionCustomBridge via setZedxionTransport().
 */
contract ZedxionAdapter is IChainAdapter, AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;

    bytes32 public constant BRIDGE_OPERATOR_ROLE = keccak256("BRIDGE_OPERATOR_ROLE");

    uint256 public constant ZEDXION_CHAIN_ID = 83872;

    IZedxionTransport public zedxionTransport;
    bool public isActive;
    uint256 public bridgeFee;

    mapping(bytes32 => BridgeRequest) public bridgeRequests;
    mapping(address => uint256) public nonces;

    event ZedxionAdapterBridge(
        bytes32 indexed requestId,
        address indexed sender,
        address indexed token,
        uint256 amount,
        address recipient
    );

    event ZedxionBridgeConfirmed(bytes32 indexed requestId, bytes32 indexed destinationTxHash);

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

    function setZedxionTransport(address transport) external onlyRole(DEFAULT_ADMIN_ROLE) {
        zedxionTransport = IZedxionTransport(transport);
    }

    function setActive(bool active) external onlyRole(DEFAULT_ADMIN_ROLE) {
        isActive = active;
    }

    function setBridgeFee(uint256 fee) external onlyRole(DEFAULT_ADMIN_ROLE) {
        bridgeFee = fee;
    }

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

    function getChainIdentifier() external pure override returns (uint256 chainId, string memory identifier) {
        return (ZEDXION_CHAIN_ID, "ZEDXION");
    }

    function validateDestination(bytes calldata destination) external pure override returns (bool) {
        if (destination.length != 20) return false;
        return address(bytes20(destination)) != address(0);
    }

    function bridge(
        address token,
        uint256 amount,
        bytes calldata destination,
        bytes calldata /* recipient */
    ) external payable override nonReentrant returns (bytes32 requestId) {
        require(isActive, "ZedxionAdapter: inactive");
        require(amount > 0, "ZedxionAdapter: zero amount");
        require(address(zedxionTransport) != address(0), "ZedxionAdapter: transport not set");
        require(this.validateDestination(destination), "ZedxionAdapter: invalid destination");

        address recipientAddr = address(bytes20(destination));

        requestId = keccak256(abi.encodePacked(
            msg.sender,
            token,
            amount,
            recipientAddr,
            nonces[msg.sender]++,
            block.timestamp
        ));

        if (token == address(0)) {
            require(msg.value >= amount, "ZedxionAdapter: insufficient value");
        } else {
            IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
            IERC20(token).forceApprove(address(zedxionTransport), amount);
        }

        bridgeRequests[requestId] = BridgeRequest({
            sender: msg.sender,
            token: token,
            amount: amount,
            destinationData: destination,
            requestId: requestId,
            status: BridgeStatus.Locked,
            createdAt: block.timestamp,
            completedAt: 0
        });

        zedxionTransport.bridgeToZedxion{value: msg.value}(token, amount, recipientAddr);

        emit ZedxionAdapterBridge(requestId, msg.sender, token, amount, recipientAddr);
        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,
            "ZedxionAdapter: cannot cancel"
        );
        require(msg.sender == request.sender, "ZedxionAdapter: not 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) {
        return bridgeFee;
    }

    function confirmBridge(bytes32 requestId, bytes32 destinationTxHash)
        external
        onlyRole(BRIDGE_OPERATOR_ROLE)
    {
        BridgeRequest storage request = bridgeRequests[requestId];
        require(request.status == BridgeStatus.Locked, "ZedxionAdapter: invalid status");
        request.status = BridgeStatus.Confirmed;
        request.completedAt = block.timestamp;
        emit ZedxionBridgeConfirmed(requestId, destinationTxHash);
    }
}
