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

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";

interface ICompliantTokenOwnerControl {
    function owner() external view returns (address);
    function pause() external;
    function unpause() external;
    function transferOwnership(address newOwner) external;
    function mint(address to, uint256 amount) external;
    function burn(uint256 amount) external;
    function totalSupply() external view returns (uint256);
}

/**
 * @title StablecoinReserveVault
 * @notice 1:1 backing mechanism for CompliantUSDT and CompliantUSDC with official tokens
 * @dev Locks official USDT/USDC, mints cUSDT/cUSDC 1:1. Can be deployed on Ethereum Mainnet
 *      or connected via cross-chain bridge to Chain 138 for minting.
 * 
 * IMPORTANT: This contract should be deployed on Ethereum Mainnet where official USDT/USDC exist.
 * For Chain 138 deployment, tokens would be bridged/locked via cross-chain infrastructure.
 */
contract StablecoinReserveVault is AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;

    bytes32 public constant RESERVE_OPERATOR_ROLE = keccak256("RESERVE_OPERATOR_ROLE");
    bytes32 public constant REDEMPTION_OPERATOR_ROLE = keccak256("REDEMPTION_OPERATOR_ROLE");

    // Official token addresses on Ethereum Mainnet
    // These can be overridden in constructor for different networks
    address public immutable officialUSDT;
    address public immutable officialUSDC;

    // Compliant token contracts (on Chain 138 or same network)
    ICompliantTokenOwnerControl public immutable compliantUSDT;
    ICompliantTokenOwnerControl public immutable compliantUSDC;

    // Reserve tracking
    uint256 public usdtReserveBalance;
    uint256 public usdcReserveBalance;

    // Total minted (for verification)
    uint256 public totalCUSDTMinted;
    uint256 public totalCUSDCMinted;

    // Pause mechanism
    bool public paused;

    event ReserveDeposited(address indexed token, uint256 amount, address indexed depositor);
    event ReserveWithdrawn(address indexed token, uint256 amount, address indexed recipient);
    event CompliantTokensMinted(address indexed token, uint256 amount, address indexed recipient);
    event CompliantTokensBurned(address indexed token, uint256 amount, address indexed redeemer);
    event CompliantTokenPaused(address indexed token, address indexed operator);
    event CompliantTokenUnpaused(address indexed token, address indexed operator);
    event CompliantTokenOwnershipTransferred(address indexed token, address indexed newOwner);
    event Paused(address indexed account);
    event Unpaused(address indexed account);

    modifier whenNotPaused() {
        require(!paused, "StablecoinReserveVault: paused");
        _;
    }

    /**
     * @notice Constructor
     * @param admin Admin address (will receive DEFAULT_ADMIN_ROLE)
     * @param officialUSDT_ Official USDT token address (on Ethereum Mainnet: 0xdAC17F958D2ee523a2206206994597C13D831ec7)
     * @param officialUSDC_ Official USDC token address (on Ethereum Mainnet: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48)
     * @param compliantUSDT_ CompliantUSDT contract address
     * @param compliantUSDC_ CompliantUSDC contract address
     */
    constructor(
        address admin,
        address officialUSDT_,
        address officialUSDC_,
        address compliantUSDT_,
        address compliantUSDC_
    ) {
        require(admin != address(0), "StablecoinReserveVault: zero admin");
        require(officialUSDT_ != address(0), "StablecoinReserveVault: zero USDT");
        require(officialUSDC_ != address(0), "StablecoinReserveVault: zero USDC");
        require(compliantUSDT_ != address(0), "StablecoinReserveVault: zero cUSDT");
        require(compliantUSDC_ != address(0), "StablecoinReserveVault: zero cUSDC");

        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(RESERVE_OPERATOR_ROLE, admin);
        _grantRole(REDEMPTION_OPERATOR_ROLE, admin);

        officialUSDT = officialUSDT_;
        officialUSDC = officialUSDC_;
        compliantUSDT = ICompliantTokenOwnerControl(compliantUSDT_);
        compliantUSDC = ICompliantTokenOwnerControl(compliantUSDC_);
    }

    /**
     * @notice Deposit official USDT and mint cUSDT 1:1
     * @dev Transfers USDT from caller, mints cUSDT to caller
     * @param amount Amount of USDT to deposit (6 decimals)
     */
    function depositUSDT(uint256 amount) external whenNotPaused nonReentrant {
        require(amount > 0, "StablecoinReserveVault: zero amount");

        // Transfer official USDT from caller
        IERC20(officialUSDT).safeTransferFrom(msg.sender, address(this), amount);

        // Update reserve
        usdtReserveBalance += amount;
        totalCUSDTMinted += amount;

        // Mint cUSDT to caller
        compliantUSDT.mint(msg.sender, amount);

        emit ReserveDeposited(officialUSDT, amount, msg.sender);
        emit CompliantTokensMinted(address(compliantUSDT), amount, msg.sender);
    }

    /**
     * @notice Seed official USDT reserves without minting new cUSDT
     * @dev Used to retrofit backing for pre-existing canonical supply
     */
    function seedUSDTReserve(uint256 amount) external onlyRole(RESERVE_OPERATOR_ROLE) nonReentrant {
        require(amount > 0, "StablecoinReserveVault: zero amount");

        IERC20(officialUSDT).safeTransferFrom(msg.sender, address(this), amount);
        usdtReserveBalance += amount;

        emit ReserveDeposited(officialUSDT, amount, msg.sender);
    }

    /**
     * @notice Deposit official USDC and mint cUSDC 1:1
     * @dev Transfers USDC from caller, mints cUSDC to caller
     * @param amount Amount of USDC to deposit (6 decimals)
     */
    function depositUSDC(uint256 amount) external whenNotPaused nonReentrant {
        require(amount > 0, "StablecoinReserveVault: zero amount");

        // Transfer official USDC from caller
        IERC20(officialUSDC).safeTransferFrom(msg.sender, address(this), amount);

        // Update reserve
        usdcReserveBalance += amount;
        totalCUSDCMinted += amount;

        // Mint cUSDC to caller
        compliantUSDC.mint(msg.sender, amount);

        emit ReserveDeposited(officialUSDC, amount, msg.sender);
        emit CompliantTokensMinted(address(compliantUSDC), amount, msg.sender);
    }

    /**
     * @notice Seed official USDC reserves without minting new cUSDC
     * @dev Used to retrofit backing for pre-existing canonical supply
     */
    function seedUSDCReserve(uint256 amount) external onlyRole(RESERVE_OPERATOR_ROLE) nonReentrant {
        require(amount > 0, "StablecoinReserveVault: zero amount");

        IERC20(officialUSDC).safeTransferFrom(msg.sender, address(this), amount);
        usdcReserveBalance += amount;

        emit ReserveDeposited(officialUSDC, amount, msg.sender);
    }

    /**
     * @notice Redeem cUSDT for official USDT 1:1
     * @dev Burns cUSDT from caller, transfers USDT to caller
     * @param amount Amount of cUSDT to redeem (6 decimals)
     */
    function redeemUSDT(uint256 amount) external whenNotPaused nonReentrant {
        require(amount > 0, "StablecoinReserveVault: zero amount");
        require(usdtReserveBalance >= amount, "StablecoinReserveVault: insufficient reserve");

        // Pull cUSDT from the redeemer, then burn from the vault balance as token owner.
        IERC20(address(compliantUSDT)).safeTransferFrom(msg.sender, address(this), amount);
        compliantUSDT.burn(amount);

        // Update reserve
        usdtReserveBalance -= amount;
        totalCUSDTMinted -= amount;

        // Transfer official USDT to caller
        IERC20(officialUSDT).safeTransfer(msg.sender, amount);

        emit CompliantTokensBurned(address(compliantUSDT), amount, msg.sender);
        emit ReserveWithdrawn(officialUSDT, amount, msg.sender);
    }

    /**
     * @notice Redeem cUSDC for official USDC 1:1
     * @dev Burns cUSDC from caller, transfers USDC to caller
     * @param amount Amount of cUSDC to redeem (6 decimals)
     */
    function redeemUSDC(uint256 amount) external whenNotPaused nonReentrant {
        require(amount > 0, "StablecoinReserveVault: zero amount");
        require(usdcReserveBalance >= amount, "StablecoinReserveVault: insufficient reserve");

        // Pull cUSDC from the redeemer, then burn from the vault balance as token owner.
        IERC20(address(compliantUSDC)).safeTransferFrom(msg.sender, address(this), amount);
        compliantUSDC.burn(amount);

        // Update reserve
        usdcReserveBalance -= amount;
        totalCUSDCMinted -= amount;

        // Transfer official USDC to caller
        IERC20(officialUSDC).safeTransfer(msg.sender, amount);

        emit CompliantTokensBurned(address(compliantUSDC), amount, msg.sender);
        emit ReserveWithdrawn(officialUSDC, amount, msg.sender);
    }

    /**
     * @notice Get reserve backing ratio
     * @param token Address of compliant token (cUSDT or cUSDC)
     * @return reserveBalance Current reserve balance
     * @return tokenSupply Current token supply
     * @return backingRatio Backing ratio (10000 = 100%)
     */
    function getBackingRatio(address token) external view returns (
        uint256 reserveBalance,
        uint256 tokenSupply,
        uint256 backingRatio
    ) {
        if (token == address(compliantUSDT)) {
            reserveBalance = usdtReserveBalance;
            tokenSupply = compliantUSDT.totalSupply();
        } else if (token == address(compliantUSDC)) {
            reserveBalance = usdcReserveBalance;
            tokenSupply = compliantUSDC.totalSupply();
        } else {
            revert("StablecoinReserveVault: unsupported token");
        }

        backingRatio = tokenSupply > 0 
            ? (reserveBalance * 10000) / tokenSupply 
            : 0;
    }

    /**
     * @notice Check if reserves are adequate
     * @return usdtAdequate True if USDT reserves are adequate
     * @return usdcAdequate True if USDC reserves are adequate
     */
    function checkReserveAdequacy() external view returns (bool usdtAdequate, bool usdcAdequate) {
        uint256 cUSDTSupply = compliantUSDT.totalSupply();
        uint256 cUSDCSupply = compliantUSDC.totalSupply();
        
        usdtAdequate = usdtReserveBalance >= cUSDTSupply;
        usdcAdequate = usdcReserveBalance >= cUSDCSupply;
    }

    /**
     * @notice Pause all operations
     */
    function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        paused = true;
        emit Paused(msg.sender);
    }

    /**
     * @notice Unpause all operations
     */
    function unpause() external onlyRole(DEFAULT_ADMIN_ROLE) {
        paused = false;
        emit Unpaused(msg.sender);
    }

    /**
     * @notice Pause one of the compliant tokens currently owned by the vault
     */
    function pauseCompliantToken(address token) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _requireSupportedCompliantToken(token);
        ICompliantTokenOwnerControl(token).pause();
        emit CompliantTokenPaused(token, msg.sender);
    }

    /**
     * @notice Unpause one of the compliant tokens currently owned by the vault
     */
    function unpauseCompliantToken(address token) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _requireSupportedCompliantToken(token);
        ICompliantTokenOwnerControl(token).unpause();
        emit CompliantTokenUnpaused(token, msg.sender);
    }

    /**
     * @notice Move compliant token ownership away from the vault if governance needs to recover control
     */
    function transferCompliantTokenOwnership(
        address token,
        address newOwner
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(newOwner != address(0), "StablecoinReserveVault: zero new owner");
        _requireSupportedCompliantToken(token);
        ICompliantTokenOwnerControl(token).transferOwnership(newOwner);
        emit CompliantTokenOwnershipTransferred(token, newOwner);
    }

    /**
     * @notice Emergency withdrawal (admin only, after pause)
     * @dev Can be used to recover funds in emergency situations
     */
    function emergencyWithdraw(address token, uint256 amount, address recipient) 
        external 
        onlyRole(DEFAULT_ADMIN_ROLE)
        whenPaused
    {
        require(recipient != address(0), "StablecoinReserveVault: zero recipient");
        IERC20(token).safeTransfer(recipient, amount);
    }

    modifier whenPaused() {
        require(paused, "StablecoinReserveVault: not paused");
        _;
    }

    function _requireSupportedCompliantToken(address token) internal view {
        require(
            token == address(compliantUSDT) || token == address(compliantUSDC),
            "StablecoinReserveVault: unsupported compliant token"
        );
    }
}
