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

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Base64.sol";

/**
 * @title GRUFormulasNFT
 * @notice ERC-721 NFT depicting the three GRU-related monetary formulas as on-chain SVG graphics
 * @dev Token IDs: 0 = Money Supply (GRU M00/M0/M1), 1 = Money Velocity (M×V=P×Y), 2 = Money Multiplier (m=1.0)
 */
contract GRUFormulasNFT is ERC721, ERC721URIStorage, AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    uint256 public constant TOKEN_ID_MONEY_SUPPLY = 0;
    uint256 public constant TOKEN_ID_MONEY_VELOCITY = 1;
    uint256 public constant TOKEN_ID_MONEY_MULTIPLIER = 2;
    uint256 public constant MAX_TOKEN_ID = 2;

    constructor(address admin) ERC721("GRU Formulas", "GRUF") {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(MINTER_ROLE, admin);
    }

    /**
     * @notice Mint one of the three formula NFTs (tokenId 0, 1, or 2)
     * @param to Recipient
     * @param tokenId 0 = Money Supply, 1 = Money Velocity, 2 = Money Multiplier
     */
    function mint(address to, uint256 tokenId) external onlyRole(MINTER_ROLE) {
        require(tokenId <= MAX_TOKEN_ID, "GRUFormulasNFT: invalid tokenId");
        _safeMint(to, tokenId);
    }

    function _baseURI() internal pure override returns (string memory) {
        return "";
    }

    /**
     * @notice Returns metadata URI with on-chain SVG image for the formula
     * @param tokenId 0 = Money Supply, 1 = Money Velocity, 2 = Money Multiplier
     */
    function tokenURI(uint256 tokenId) public view override(ERC721, ERC721URIStorage) returns (string memory) {
        require(_ownerOf(tokenId) != address(0), "ERC721: invalid token ID");
        (string memory name, string memory description, string memory svg) = _formulaData(tokenId);
        string memory imageData = string.concat("data:image/svg+xml;base64,", Base64.encode(bytes(svg)));
        string memory json = string.concat(
            '{"name":"', name,
            '","description":"', description,
            '","image":"', imageData,
            '"}'
        );
        return string.concat("data:application/json;base64,", Base64.encode(bytes(json)));
    }

    function _formulaData(uint256 tokenId) internal pure returns (string memory name, string memory description, string memory svg) {
        if (tokenId == TOKEN_ID_MONEY_SUPPLY) {
            name = "GRU Money Supply (M)";
            description = "GRU monetary layers: 1 M00 = 5 M0 = 25 M1 (base, collateral, credit). Non-ISO synthetic unit of account.";
            svg = _svgMoneySupply();
        } else if (tokenId == TOKEN_ID_MONEY_VELOCITY) {
            name = "Money Velocity (V)";
            description = "Equation of exchange: M x V = P x Y (money supply, velocity, price level, output).";
            svg = _svgMoneyVelocity();
        } else if (tokenId == TOKEN_ID_MONEY_MULTIPLIER) {
            name = "Money Multiplier (m)";
            description = "m = Reserve / Supply; GRU and ISO4217W enforce m = 1.0 (no fractional reserve).";
            svg = _svgMoneyMultiplier();
        } else {
            revert("GRUFormulasNFT: invalid tokenId");
        }
    }

    function _svgMoneySupply() internal pure returns (string memory) {
        return string.concat(
            "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 120' width='320' height='120'>",
            "<rect width='320' height='120' fill='#1a1a2e' rx='8'/>",
            "<text x='160' y='42' text-anchor='middle' fill='#eaeaea' font-family='sans-serif' font-size='14'>Money Supply (M) - GRU layers</text>",
            "<text x='160' y='68' text-anchor='middle' fill='#a0e0a0' font-family='monospace' font-size='16'>1 M00 = 5 M0 = 25 M1</text>",
            "<text x='160' y='92' text-anchor='middle' fill='#888' font-family='sans-serif' font-size='11'>M00 base | M0 collateral | M1 credit</text>",
            "</svg>"
        );
    }

    function _svgMoneyVelocity() internal pure returns (string memory) {
        return string.concat(
            "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 120' width='320' height='120'>",
            "<rect width='320' height='120' fill='#1a1a2e' rx='8'/>",
            "<text x='160' y='42' text-anchor='middle' fill='#eaeaea' font-family='sans-serif' font-size='14'>Money Velocity (V)</text>",
            "<text x='160' y='72' text-anchor='middle' fill='#a0c0ff' font-family='serif' font-size='20'>M * V = P * Y</text>",
            "<text x='160' y='98' text-anchor='middle' fill='#888' font-family='sans-serif' font-size='10'>Equation of exchange</text>",
            "</svg>"
        );
    }

    function _svgMoneyMultiplier() internal pure returns (string memory) {
        return string.concat(
            "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 120' width='320' height='120'>",
            "<rect width='320' height='120' fill='#1a1a2e' rx='8'/>",
            "<text x='160' y='42' text-anchor='middle' fill='#eaeaea' font-family='sans-serif' font-size='14'>Money Multiplier (m)</text>",
            "<text x='160' y='72' text-anchor='middle' fill='#ffc0a0' font-family='serif' font-size='18'>m = Reserve / Supply = 1.0</text>",
            "<text x='160' y='98' text-anchor='middle' fill='#888' font-family='sans-serif' font-size='10'>No fractional reserve (GRU / ISO4217W)</text>",
            "</svg>"
        );
    }

    function supportsInterface(bytes4 interfaceId) public view override(ERC721, ERC721URIStorage, AccessControl) returns (bool) {
        return super.supportsInterface(interfaceId) || AccessControl.supportsInterface(interfaceId);
    }
}
