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

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "./wXRP.sol";

/**
 * @title MintBurnController
 * @notice HSM-backed controller for wXRP mint/burn operations
 * @dev Uses EIP-712 signatures for HSM authorization
 */
contract MintBurnController is AccessControl, Pausable, EIP712 {
    using ECDSA for bytes32;

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

    bytes32 private constant MINT_TYPEHASH =
        keccak256("MintRequest(address to,uint256 amount,bytes32 xrplTxHash,uint256 nonce,uint256 deadline)");
    bytes32 private constant BURN_TYPEHASH =
        keccak256("BurnRequest(address from,uint256 amount,bytes32 xrplTxHash,uint256 nonce,uint256 deadline)");

    wXRP public immutable wXRP_TOKEN;

    mapping(uint256 => bool) public usedNonces;
    address public hsmSigner;

    event MintExecuted(
        address indexed to,
        uint256 amount,
        bytes32 xrplTxHash,
        address executor
    );

    event BurnExecuted(
        address indexed from,
        uint256 amount,
        bytes32 xrplTxHash,
        address executor
    );

    event HSMSignerUpdated(address oldSigner, address newSigner);

    struct MintRequest {
        address to;
        uint256 amount;
        bytes32 xrplTxHash;
        uint256 nonce;
        uint256 deadline;
        bytes hsmSignature;
    }

    struct BurnRequest {
        address from;
        uint256 amount;
        bytes32 xrplTxHash;
        uint256 nonce;
        uint256 deadline;
        bytes hsmSignature;
    }

    error ZeroAmount();
    error ZeroAddress();
    error InvalidSignature();
    error NonceAlreadyUsed();
    error DeadlineExpired();
    error InvalidNonce();

    constructor(address admin, address _wXRP, address _hsmSigner) EIP712("MintBurnController", "1") {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(OPERATOR_ROLE, admin);
        wXRP_TOKEN = wXRP(_wXRP);
        hsmSigner = _hsmSigner;
    }

    /**
     * @notice Update HSM signer address
     * @param newSigner New HSM signer address
     */
    function setHSMSigner(address newSigner) external onlyRole(DEFAULT_ADMIN_ROLE) {
        if (newSigner == address(0)) revert ZeroAddress();
        address oldSigner = hsmSigner;
        hsmSigner = newSigner;
        emit HSMSignerUpdated(oldSigner, newSigner);
    }

    /**
     * @notice Execute mint with HSM signature
     * @param request Mint request with HSM signature
     */
    function executeMint(MintRequest calldata request) external onlyRole(OPERATOR_ROLE) whenNotPaused {
        if (request.to == address(0)) revert ZeroAddress();
        if (request.amount == 0) revert ZeroAmount();
        if (block.timestamp > request.deadline) revert DeadlineExpired();
        if (usedNonces[request.nonce]) revert NonceAlreadyUsed();

        // Verify HSM signature
        bytes32 structHash = keccak256(
            abi.encode(
                MINT_TYPEHASH,
                request.to,
                request.amount,
                request.xrplTxHash,
                request.nonce,
                request.deadline
            )
        );
        bytes32 hash = _hashTypedDataV4(structHash);

        if (hash.recover(request.hsmSignature) != hsmSigner) {
            revert InvalidSignature();
        }

        usedNonces[request.nonce] = true;

        wXRP_TOKEN.mint(request.to, request.amount, request.xrplTxHash);

        emit MintExecuted(request.to, request.amount, request.xrplTxHash, msg.sender);
    }

    /**
     * @notice Execute burn with HSM signature
     * @param request Burn request with HSM signature
     */
    function executeBurn(BurnRequest calldata request) external onlyRole(OPERATOR_ROLE) whenNotPaused {
        if (request.from == address(0)) revert ZeroAddress();
        if (request.amount == 0) revert ZeroAmount();
        if (block.timestamp > request.deadline) revert DeadlineExpired();
        if (usedNonces[request.nonce]) revert NonceAlreadyUsed();

        // Verify HSM signature
        bytes32 structHash = keccak256(
            abi.encode(
                BURN_TYPEHASH,
                request.from,
                request.amount,
                request.xrplTxHash,
                request.nonce,
                request.deadline
            )
        );
        bytes32 hash = _hashTypedDataV4(structHash);

        if (hash.recover(request.hsmSignature) != hsmSigner) {
            revert InvalidSignature();
        }

        usedNonces[request.nonce] = true;

        wXRP_TOKEN.burnFrom(request.from, request.amount, request.xrplTxHash);

        emit BurnExecuted(request.from, request.amount, request.xrplTxHash, msg.sender);
    }

    /**
     * @notice Pause controller
     */
    function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _pause();
    }

    /**
     * @notice Unpause controller
     */
    function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        _unpause();
    }
}
