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

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "../compliance/LegallyCompliantBase.sol";

/**
 * @title CompliantFiatToken
 * @notice Generic ISO-4217 compliant fiat/commodity token (ERC-20, DEX-ready)
 * @dev Use for cEURC, cGBPC, cAUDC, cJPYC, cCHFC, cCADC, cXAUC, cXAUT, etc.
 *      Full ERC-20 for DEX liquidity pools; inherits LegallyCompliantBase.
 *
 *      XAU (gold) invariant — enforced by policy and integrators, not by ERC-20 math:
 *      When `currencyCode()` is `"XAU"` (e.g. cXAUC, cXAUT), **one full token** means
 *      **exactly one troy ounce** of fine gold: balances and `mint`/`transfer` amounts use
 *      `10 ** decimals()` base units per troy ounce (with 6 decimals, `1_000_000` base = 1 oz).
 *      Fiat codes (USD, EUR, …) use one full token as one unit of that currency at `decimals`.
 */
contract CompliantFiatToken is ERC20, Pausable, Ownable, LegallyCompliantBase {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    uint8 private immutable _decimalsStorage;
    string private _currencyCode;

    /**
     * @notice Constructor
     * @param name_ Token name (e.g. "Euro Coin (Compliant)")
     * @param symbol_ Token symbol (e.g. "cEURC")
     * @param decimals_ Token decimals (e.g. 6)
     * @param currencyCode_ ISO-4217 code (e.g. "EUR")
     * @param initialOwner Owner address
     * @param admin Compliance admin (DEFAULT_ADMIN_ROLE)
     * @param initialSupply Initial supply to mint to deployer
     */
    constructor(
        string memory name_,
        string memory symbol_,
        uint8 decimals_,
        string memory currencyCode_,
        address initialOwner,
        address admin,
        uint256 initialSupply
    )
        ERC20(name_, symbol_)
        Ownable(initialOwner)
        LegallyCompliantBase(admin)
    {
        _decimalsStorage = decimals_;
        _currencyCode = currencyCode_;
        _grantRole(MINTER_ROLE, initialOwner);
        if (initialSupply > 0) {
            _mint(msg.sender, initialSupply);
        }
    }

    function decimals() public view override returns (uint8) {
        return _decimalsStorage;
    }

    function currencyCode() external view returns (string memory) {
        return _currencyCode;
    }

    /**
     * @notice Internal transfer with compliance tracking
     */
    function _update(
        address from,
        address to,
        uint256 amount
    ) internal override whenNotPaused {
        super._update(from, to, amount);
        if (from != address(0) && to != address(0)) {
            bytes32 legalRefHash = _generateLegalReferenceHash(
                from,
                to,
                amount,
                abi.encodePacked(symbol(), " Transfer")
            );
            emit ValueTransferDeclared(from, to, amount, legalRefHash);
        }
    }

    function pause() public onlyOwner {
        _pause();
    }

    function unpause() public onlyOwner {
        _unpause();
    }

    /// @notice Mint (DBIS Rail: grant MINTER_ROLE only to DBIS_GRU_MintController; revoke from others)
    function mint(address to, uint256 amount) public onlyRole(MINTER_ROLE) {
        _mint(to, amount);
    }

    function burn(uint256 amount) public {
        _burn(msg.sender, amount);
    }
}
