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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";

/**
 * @title wXRP
 * @notice Wrapped XRP token (ERC-20) representing XRP locked on XRPL
 * @dev Mintable/burnable by authorized bridge controller only
 */
contract wXRP is ERC20, ERC20Burnable, AccessControl, Pausable {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    uint8 private constant DECIMALS = 18;

    event Minted(address indexed to, uint256 amount, bytes32 xrplTxHash);
    event Burned(address indexed from, uint256 amount, bytes32 xrplTxHash);

    error ZeroAmount();
    error ZeroAddress();

    constructor(address admin) ERC20("Wrapped XRP", "wXRP") {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(MINTER_ROLE, admin);
        _grantRole(BURNER_ROLE, admin);
    }

    /**
     * @notice Mint wXRP tokens (bridge controller only)
     * @param to Recipient address
     * @param amount Amount to mint
     * @param xrplTxHash XRPL transaction hash that locked the XRP
     */
    function mint(
        address to,
        uint256 amount,
        bytes32 xrplTxHash
    ) external onlyRole(MINTER_ROLE) whenNotPaused {
        if (to == address(0)) revert ZeroAddress();
        if (amount == 0) revert ZeroAmount();

        _mint(to, amount);
        emit Minted(to, amount, xrplTxHash);
    }

    /**
     * @notice Burn wXRP tokens to unlock XRP on XRPL (bridge controller only)
     * @param from Address to burn from
     * @param amount Amount to burn
     * @param xrplTxHash XRPL transaction hash for the unlock
     */
    function burnFrom(
        address from,
        uint256 amount,
        bytes32 xrplTxHash
    ) external onlyRole(BURNER_ROLE) whenNotPaused {
        if (from == address(0)) revert ZeroAddress();
        if (amount == 0) revert ZeroAmount();

        _burn(from, amount);
        emit Burned(from, amount, xrplTxHash);
    }

    /**
     * @notice Override decimals to return 18
     */
    function decimals() public pure override returns (uint8) {
        return DECIMALS;
    }

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

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