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

import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "./ISO4217WToken.sol";
import "./interfaces/ITokenRegistry.sol";
import "./interfaces/IComplianceGuard.sol";
import "./libraries/ISO4217WCompliance.sol";

/**
 * @title TokenFactory
 * @notice Factory for deploying ISO-4217 W tokens
 * @dev Creates UUPS upgradeable proxy tokens with proper configuration
 */
contract TokenFactory is AccessControl {
    bytes32 public constant DEPLOYER_ROLE = keccak256("DEPLOYER_ROLE");

    address public immutable tokenImplementation;
    ITokenRegistry public tokenRegistry;
    IComplianceGuard public complianceGuard;
    address public reserveOracle;
    address public mintController;
    address public burnController;

    event TokenDeployed(
        address indexed token,
        string indexed currencyCode,
        string tokenSymbol,
        address indexed custodian
    );

    constructor(
        address admin,
        address tokenImplementation_,
        address tokenRegistry_,
        address complianceGuard_,
        address reserveOracle_,
        address mintController_,
        address burnController_
    ) {
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(DEPLOYER_ROLE, admin);
        
        tokenImplementation = tokenImplementation_;
        tokenRegistry = ITokenRegistry(tokenRegistry_);
        complianceGuard = IComplianceGuard(complianceGuard_);
        reserveOracle = reserveOracle_;
        mintController = mintController_;
        burnController = burnController_;
    }

    /**
     * @notice Deploy a new ISO-4217 W token
     * @param currencyCode ISO-4217 currency code (e.g., "USD")
     * @param name Token name (e.g., "USDW Token")
     * @param symbol Token symbol (must be <CCC>W, e.g., "USDW")
     * @param decimals Token decimals (typically 2 for fiat)
     * @param custodian Custodian address
     * @return token Address of deployed token
     */
    function deployToken(
        string memory currencyCode,
        string memory name,
        string memory symbol,
        uint8 decimals,
        address custodian
    ) external onlyRole(DEPLOYER_ROLE) returns (address token) {
        // Validate ISO-4217 format
        require(
            ISO4217WCompliance.isValidISO4217Format(currencyCode),
            "TokenFactory: invalid ISO-4217 format"
        );
        
        // Validate GRU isolation
        require(
            !ISO4217WCompliance.violatesGRUIsolation(currencyCode),
            "TokenFactory: GRU isolation violation"
        );
        
        // Validate token symbol matches <CCC>W pattern
        require(
            ISO4217WCompliance.validateTokenSymbol(currencyCode, symbol),
            "TokenFactory: token symbol must be <CCC>W"
        );
        
        require(custodian != address(0), "TokenFactory: zero custodian");
        require(bytes(name).length > 0, "TokenFactory: empty name");
        require(bytes(symbol).length > 0, "TokenFactory: empty symbol");
        
        // Deploy UUPS proxy
        bytes memory initData = abi.encodeWithSelector(
            ISO4217WToken.initialize.selector,
            name,
            symbol,
            currencyCode,
            decimals,
            custodian,
            mintController,
            burnController,
            address(complianceGuard),
            msg.sender // Admin
        );
        
        ERC1967Proxy proxy = new ERC1967Proxy(tokenImplementation, initData);
        token = address(proxy);
        
        // Grant reserve update role to oracle
        ISO4217WToken(token).grantRole(keccak256("RESERVE_UPDATE_ROLE"), reserveOracle);
        
        // Register token in registry
        tokenRegistry.registerToken(currencyCode, token, symbol, decimals, custodian);
        tokenRegistry.setMintController(currencyCode, mintController);
        tokenRegistry.setBurnController(currencyCode, burnController);
        
        emit TokenDeployed(token, currencyCode, symbol, custodian);
    }

    /**
     * @notice Set system contract addresses
     */
    function setTokenRegistry(address tokenRegistry_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(tokenRegistry_ != address(0), "TokenFactory: zero address");
        tokenRegistry = ITokenRegistry(tokenRegistry_);
    }

    function setComplianceGuard(address complianceGuard_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(complianceGuard_ != address(0), "TokenFactory: zero address");
        complianceGuard = IComplianceGuard(complianceGuard_);
    }

    function setReserveOracle(address reserveOracle_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(reserveOracle_ != address(0), "TokenFactory: zero address");
        reserveOracle = reserveOracle_;
    }

    function setMintController(address mintController_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(mintController_ != address(0), "TokenFactory: zero address");
        mintController = mintController_;
    }

    function setBurnController(address burnController_) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(burnController_ != address(0), "TokenFactory: zero address");
        burnController = burnController_;
    }
}
