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

import "forge-std/Test.sol";
import "../../contracts/iso4217w/controllers/MintController.sol";
import "../../contracts/iso4217w/ISO4217WToken.sol";
import "../../contracts/iso4217w/ComplianceGuard.sol";
import "../../contracts/iso4217w/oracle/ReserveOracle.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

contract MintControllerTest is Test {
    MintController public mintController;
    ISO4217WToken public token;
    ComplianceGuard public complianceGuard;
    ReserveOracle public reserveOracle;

    address public admin = address(0x1);
    address public minter = address(0x2);
    address public user = address(0x3);
    address public oracle1 = address(0x4);
    address public custodian = address(0x5);

    string public constant CURRENCY_CODE = "USD";
    string public constant TOKEN_SYMBOL = "USDW";

    function setUp() public {
        vm.warp(3601); // Avoid ReserveOracle underflow (block.timestamp - stalenessThreshold)
        vm.startPrank(admin);

        complianceGuard = new ComplianceGuard(admin);
        reserveOracle = new ReserveOracle(admin, 1, 3600); // quorum=1 for tests, 1 hour staleness

        // Add oracle to reserve oracle
        reserveOracle.addOracle(oracle1);

        mintController = new MintController(
            admin,
            address(reserveOracle),
            address(complianceGuard)
        );

        // Deploy token
        ISO4217WToken implementation = new ISO4217WToken();
        bytes memory initData = abi.encodeWithSelector(
            ISO4217WToken.initialize.selector,
            "USDW Token",
            TOKEN_SYMBOL,
            CURRENCY_CODE,
            2,
            custodian,
            address(mintController),
            address(0x999), // burn controller placeholder
            address(complianceGuard),
            admin
        );

        ERC1967Proxy proxy = new ERC1967Proxy(address(implementation), initData);
        token = ISO4217WToken(address(proxy));

        // Grant roles
        token.grantRole(keccak256("MINTER_ROLE"), address(mintController));
        token.grantRole(keccak256("RESERVE_UPDATE_ROLE"), oracle1);

        // Grant minter role and approve token
        mintController.grantRole(keccak256("MINTER_ROLE"), minter);
        mintController.approveToken(address(token));

        vm.stopPrank();
    }

    function test_CanMint() public {
        // Set reserve
        vm.prank(oracle1);
        token.updateVerifiedReserve(10000e2); // 10,000 USD reserve

        // Submit reserve report to oracle
        vm.prank(oracle1);
        reserveOracle.submitReserveReport(CURRENCY_CODE, 10000e2, keccak256("attestation"));

        // Check if can mint
        (bool canMint, ) = mintController.canMint(address(token), 1000e2);

        assertTrue(canMint, "Should be able to mint with sufficient reserve");
    }

    function test_CanMint_RevertIfReserveInsufficient() public {
        // Set reserve below supply
        vm.prank(oracle1);
        token.updateVerifiedReserve(500e2); // 500 USD reserve

        vm.prank(oracle1);
        reserveOracle.submitReserveReport(CURRENCY_CODE, 500e2, keccak256("attestation"));

        // Try to mint more than reserve allows
        (bool canMint, ) = mintController.canMint(address(token), 1000e2);

        assertFalse(canMint, "Should not be able to mint with insufficient reserve");
    }

    function test_Mint() public {
        // Set reserve
        vm.prank(oracle1);
        token.updateVerifiedReserve(10000e2);

        vm.prank(oracle1);
        reserveOracle.submitReserveReport(CURRENCY_CODE, 10000e2, keccak256("attestation"));

        bytes32 settlementId = keccak256("SETTLEMENT_001");

        vm.prank(minter);
        mintController.mint(
            address(token),
            user,
            1000e2, // 1,000 USDW
            settlementId
        );

        assertEq(token.balanceOf(user), 1000e2);
        assertEq(token.totalSupply(), 1000e2);
    }

    function test_Mint_RevertIfNotAuthorized() public {
        address unauthorized = address(0x999);

        vm.prank(unauthorized);
        vm.expectRevert();
        mintController.mint(
            address(token),
            user,
            1000e2,
            keccak256("SETTLEMENT_001")
        );
    }
}
