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

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "../interfaces/IMintController.sol";
import "../interfaces/IISO4217WToken.sol";
import {IReserveOracle} from "../interfaces/IReserveOracle.sol";
import {IComplianceGuard} from "../interfaces/IComplianceGuard.sol";

/**
 * @title MintController
 * @notice Controls minting of ISO-4217 W tokens with reserve verification
 * @dev Minting requires: verified fiat settlement, custodian attestation, oracle quorum
 */
contract MintController is IMintController, AccessControl, ReentrancyGuard {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    IReserveOracle public reserveOracle;
    IComplianceGuard public complianceGuard;

    mapping(address => bool) public isApprovedToken;

    constructor(address admin, address reserveOracle_, address complianceGuard_) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(MINTER_ROLE, admin);
        reserveOracle = IReserveOracle(reserveOracle_);
        complianceGuard = IComplianceGuard(complianceGuard_);
    }

    /**
     * @notice Mint tokens (requires reserve verification)
     * @param token Token address
     * @param to Recipient address
     * @param amount Amount to mint (in token decimals)
     * @param settlementId Fiat settlement ID for audit trail
     */
    function mint(
        address token,
        address to,
        uint256 amount,
        bytes32 settlementId
    ) external override nonReentrant onlyRole(MINTER_ROLE) {
        require(isApprovedToken[token], "MintController: token not approved");
        require(amount > 0, "MintController: zero amount");
        require(to != address(0), "MintController: zero address");
        
        IISO4217WToken tokenContract = IISO4217WToken(token);
        string memory currencyCode = tokenContract.currencyCode();
        
        // Check if minting is allowed
        (bool allowed, bytes32 reasonCode) = this.canMint(token, amount);
        require(allowed, string(abi.encodePacked("MintController: mint not allowed: ", reasonCode)));
        
        // Mint tokens
        tokenContract.mint(to, amount);
        
        emit MintExecuted(token, to, amount, settlementId);
    }

    /**
     * @notice Check if minting is allowed
     * @param token Token address
     * @param amount Amount to mint
     * @return allowed True if minting is allowed
     * @return reasonCode Reason if not allowed
     */
    function canMint(address token, uint256 amount) external view override returns (bool allowed, bytes32 reasonCode) {
        require(isApprovedToken[token], "MintController: token not approved");
        
        IISO4217WToken tokenContract = IISO4217WToken(token);
        string memory currencyCode = tokenContract.currencyCode();
        
        // Check oracle quorum
        if (!this.isOracleQuorumMet(token)) {
            return (false, keccak256("ORACLE_QUORUM_NOT_MET"));
        }
        
        // Get verified reserve
        (uint256 verifiedReserve, ) = reserveOracle.getVerifiedReserve(currencyCode);
        uint256 currentSupply = tokenContract.totalSupply();
        
        // Validate mint with compliance guard
        (bool isValid, bytes32 complianceReason) = complianceGuard.validateMint(
            currencyCode,
            amount,
            currentSupply,
            verifiedReserve
        );
        
        if (!isValid) {
            return (false, complianceReason);
        }
        
        return (true, bytes32(0));
    }

    /**
     * @notice Check if oracle quorum is met
     * @param token Token address
     * @return quorumMet True if quorum is met
     */
    function isOracleQuorumMet(address token) external view override returns (bool quorumMet) {
        IISO4217WToken tokenContract = IISO4217WToken(token);
        string memory currencyCode = tokenContract.currencyCode();
        
        (quorumMet, ) = reserveOracle.isQuorumMet(currencyCode);
    }

    /**
     * @notice Approve a token for minting
     * @param token Token address
     */
    function approveToken(address token) external onlyRole(DEFAULT_ADMIN_ROLE) {
        isApprovedToken[token] = true;
    }

    /**
     * @notice Revoke token approval
     * @param token Token address
     */
    function revokeToken(address token) external onlyRole(DEFAULT_ADMIN_ROLE) {
        isApprovedToken[token] = false;
    }

    /**
     * @notice Set reserve oracle address
     * @param reserveOracle_ New oracle address
     */
    function setReserveOracle(address reserveOracle_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(reserveOracle_ != address(0), "MintController: zero address");
        reserveOracle = IReserveOracle(reserveOracle_);
    }

    /**
     * @notice Set compliance guard address
     * @param complianceGuard_ New guard address
     */
    function setComplianceGuard(address complianceGuard_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(complianceGuard_ != address(0), "MintController: zero address");
        complianceGuard = IComplianceGuard(complianceGuard_);
    }
}
