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

import {Test} from "forge-std/Test.sol";
import {CompliantWrappedToken} from "../../contracts/tokens/CompliantWrappedToken.sol";

contract CompliantWrappedTokenTest is Test {
    CompliantWrappedToken public token;
    address public admin;
    address public bridge;
    address public user1;
    address public governanceExecutor;

    bytes32 constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 constant BURNER_ROLE = keccak256("BURNER_ROLE");

    function setUp() public {
        admin = address(this);
        bridge = address(0xb);
        user1 = address(0x1);
        governanceExecutor = address(0x7777);
        token = new CompliantWrappedToken("Wrapped cUSDT", "cWUSDT", 6, admin);
        token.grantRole(MINTER_ROLE, bridge);
        token.grantRole(BURNER_ROLE, bridge);
        token.setGovernanceController(governanceExecutor);
    }

    function testDecimals() public view {
        assertEq(token.decimals(), 6);
    }

    function testMint() public {
        vm.prank(bridge);
        token.mint(user1, 1000e6);
        assertEq(token.balanceOf(user1), 1000e6);
    }

    function testMintRevertsNonMinter() public {
        vm.prank(user1);
        vm.expectRevert();
        token.mint(user1, 1000e6);
    }

    function testBurn() public {
        vm.prank(bridge);
        token.mint(user1, 1000e6);
        vm.prank(bridge);
        token.burn(user1, 400e6);
        assertEq(token.balanceOf(user1), 600e6);
    }

    function testBurnFrom() public {
        vm.prank(bridge);
        token.mint(user1, 1000e6);
        vm.prank(bridge);
        token.burnFrom(user1, 400e6);
        assertEq(token.balanceOf(user1), 600e6);
    }

    function testBurnFromRevertsNonBurner() public {
        vm.prank(bridge);
        token.mint(user1, 1000e6);
        vm.prank(user1);
        vm.expectRevert();
        token.burnFrom(user1, 400e6);
    }

    function testFreezeOperationalRolesBlocksFutureMinterChanges() public {
        token.freezeOperationalRoles();

        vm.expectRevert();
        token.grantRole(MINTER_ROLE, address(0xcafe));
    }

    function testFreezeOperationalRolesBlocksFutureBurnerRevocations() public {
        token.freezeOperationalRoles();

        vm.expectRevert();
        token.revokeRole(BURNER_ROLE, bridge);
    }

    function testFreezeOperationalRolesDoesNotBreakExistingBridgePermissions() public {
        token.freezeOperationalRoles();

        vm.prank(bridge);
        token.mint(user1, 250e6);
        vm.prank(bridge);
        token.burnFrom(user1, 100e6);

        assertEq(token.balanceOf(user1), 150e6);
    }

    function testGovernanceAndSupervisionMetadataCanBeManaged() public {
        vm.expectRevert();
        token.setGovernanceProfileId(keccak256("cw-gov"));

        vm.startPrank(governanceExecutor);
        token.setGovernanceProfileId(keccak256("cw-gov"));
        token.setSupervisionProfileId(keccak256("cw-sup"));
        token.setStorageNamespace(keccak256("cw-storage"));
        token.setPrimaryJurisdiction("Avalanche");
        token.setRegulatoryDisclosureURI("ipfs://cw-disclosure");
        token.setReportingURI("ipfs://cw-reporting");
        token.setCanonicalUnderlyingAsset(address(0x1234));
        token.setSupervisionConfiguration(true, true, 21 days);
        vm.stopPrank();

        assertEq(token.primaryJurisdiction(), "Avalanche");
        assertEq(token.regulatoryDisclosureURI(), "ipfs://cw-disclosure");
        assertEq(token.reportingURI(), "ipfs://cw-reporting");
        assertEq(token.canonicalUnderlyingAsset(), address(0x1234));
        assertTrue(token.supervisionRequired());
        assertTrue(token.governmentApprovalRequired());
        assertEq(token.minimumUpgradeNoticePeriod(), 21 days);
        assertTrue(token.wrappedTransport());
    }

    function testEmergencyMetadataOverridesRemainAvailableOutsideGovernance() public {
        token.emergencySetGovernanceMetadata(
            keccak256("emergency-cw-gov"),
            keccak256("emergency-cw-sup"),
            keccak256("emergency-cw-storage"),
            "Emergency Wrapped Jurisdiction",
            address(0x5678),
            true,
            true,
            30 days
        );
        token.emergencySetDisclosureMetadata("ipfs://cw-emergency-disclosure", "ipfs://cw-emergency-reporting");

        assertEq(token.primaryJurisdiction(), "Emergency Wrapped Jurisdiction");
        assertEq(token.canonicalUnderlyingAsset(), address(0x5678));
        assertEq(token.regulatoryDisclosureURI(), "ipfs://cw-emergency-disclosure");
        assertEq(token.reportingURI(), "ipfs://cw-emergency-reporting");
        assertEq(token.minimumUpgradeNoticePeriod(), 30 days);
    }

    function testWrappedBTCCanUseEightDecimalsAndFrozenBridgeRoles() public {
        CompliantWrappedToken wrappedBtc = new CompliantWrappedToken("Wrapped cBTC", "cWBTC", 8, admin);
        wrappedBtc.grantRole(MINTER_ROLE, bridge);
        wrappedBtc.grantRole(BURNER_ROLE, bridge);

        vm.prank(bridge);
        wrappedBtc.mint(user1, 125_000_000);
        assertEq(wrappedBtc.decimals(), 8);
        assertEq(wrappedBtc.balanceOf(user1), 125_000_000);

        wrappedBtc.freezeOperationalRoles();

        vm.prank(bridge);
        wrappedBtc.burnFrom(user1, 25_000_000);
        assertEq(wrappedBtc.balanceOf(user1), 100_000_000);

        vm.expectRevert();
        wrappedBtc.grantRole(MINTER_ROLE, address(0xCAFE));
    }
}
