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

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

contract ISO4217WTokenTest is Test {
    ISO4217WToken public token;
    ComplianceGuard public complianceGuard;

    address public admin = address(0x1);
    address public custodian = address(0x2);
    address public mintController = address(0x3);
    address public burnController = address(0x4);
    address public user = address(0x5);

    string public constant CURRENCY_CODE = "USD";
    string public constant TOKEN_SYMBOL = "USDW";
    uint8 public constant DECIMALS = 2;

    function setUp() public {
        complianceGuard = new ComplianceGuard(admin);

        // Deploy implementation
        ISO4217WToken implementation = new ISO4217WToken();

        // Deploy proxy
        bytes memory initData = abi.encodeWithSelector(
            ISO4217WToken.initialize.selector,
            "USDW Token",
            TOKEN_SYMBOL,
            CURRENCY_CODE,
            DECIMALS,
            custodian,
            mintController,
            burnController,
            address(complianceGuard),
            admin
        );

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

        // Grant roles
        vm.prank(admin);
        token.grantRole(keccak256("MINTER_ROLE"), mintController);

        vm.prank(admin);
        token.grantRole(keccak256("BURNER_ROLE"), burnController);

        vm.prank(admin);
        token.grantRole(keccak256("RESERVE_UPDATE_ROLE"), admin);
    }

    function test_Initialize() public view {
        assertEq(keccak256(bytes(token.currencyCode())), keccak256(bytes(CURRENCY_CODE)));
        assertEq(token.custodian(), custodian);
        assertEq(token.decimals(), DECIMALS);
    }

    function test_Mint() public {
        vm.prank(admin);
        token.updateVerifiedReserve(10000e2); // 10,000 USD reserve

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

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

    function test_Mint_RevertIfReserveInsufficient() public {
        vm.prank(admin);
        token.updateVerifiedReserve(500e2); // 500 USD reserve

        vm.prank(mintController);
        vm.expectRevert("ISO4217WToken: reserve insufficient - money multiplier violation");
        token.mint(user, 1000e2); // Try to mint 1,000 USDW
    }

    function test_Burn() public {
        vm.prank(admin);
        token.updateVerifiedReserve(10000e2);

        vm.prank(mintController);
        token.mint(user, 1000e2);

        vm.prank(burnController);
        token.burn(user, 500e2);

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

    function test_UpdateVerifiedReserve() public {
        vm.prank(admin);
        token.updateVerifiedReserve(10000e2);

        assertEq(token.verifiedReserve(), 10000e2);
        assertTrue(token.isReserveSufficient()); // No supply yet

        vm.prank(mintController);
        token.mint(user, 5000e2);

        vm.prank(admin);
        token.updateVerifiedReserve(4000e2); // Reserve below supply

        assertFalse(token.isReserveSufficient()); // Should flag but not revert
    }

    function test_IsReserveSufficient() public {
        vm.prank(admin);
        token.updateVerifiedReserve(10000e2);

        assertTrue(token.isReserveSufficient());

        vm.prank(mintController);
        token.mint(user, 5000e2);

        assertTrue(token.isReserveSufficient());

        // Mint more (total 11k) - need reserve >= 11k first
        vm.prank(admin);
        token.updateVerifiedReserve(15000e2);
        vm.prank(mintController);
        token.mint(user, 6000e2); // Total: 11,000

        assertTrue(token.isReserveSufficient()); // 15k reserve >= 11k supply

        // Reduce reserve below supply - updateVerifiedReserve doesn't revert, just flags
        vm.prank(admin);
        token.updateVerifiedReserve(10000e2); // 10k < 11k supply

        assertFalse(token.isReserveSufficient());
    }
}
