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

import "forge-std/Test.sol";
import "../../contracts/registry/UniversalAssetRegistry.sol";
import "../../contracts/registry/GRUAssetRegistryFacet.sol";
import "../../contracts/tokens/CompliantUSDT.sol";
import "../../contracts/tokens/CompliantUSDC.sol";
import "../../contracts/tokens/CompliantFiatToken.sol";
import "../../contracts/tokens/CompliantBTC.sol";
import "../../contracts/bridge/GRUCCIPBridge.sol";
import "../../contracts/bridge/UniversalCCIPBridge.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";

/**
 * @title GRU Compliant Tokens Registry Integration Tests
 * @notice Ensures all c* tokens are integrated with GRU ERC-2535 facet path: UniversalAssetRegistry (AssetType.GRU) and GRUAssetRegistryFacet.
 */
contract GRUCompliantTokensRegistryTest is Test {
    UniversalAssetRegistry public registry;
    GRUAssetRegistryFacet public facet;
    CompliantUSDT public cusdt;
    CompliantUSDC public cusdc;
    CompliantFiatToken public ceurc;
    CompliantBTC public cbtc;

    address public admin;

    function setUp() public {
        admin = makeAddr("admin");
        vm.startPrank(admin);

        UniversalAssetRegistry impl = new UniversalAssetRegistry();
        bytes memory initData = abi.encodeCall(UniversalAssetRegistry.initialize, (admin));
        ERC1967Proxy proxy = new ERC1967Proxy(address(impl), initData);
        registry = UniversalAssetRegistry(address(proxy));

        cusdt = new CompliantUSDT(admin, admin);
        cusdc = new CompliantUSDC(admin, admin);
        ceurc = new CompliantFiatToken(
            "Euro Coin (Compliant)",
            "cEURC",
            6,
            "EUR",
            admin,
            admin,
            1_000_000 * 10**6
        );
        cbtc = new CompliantBTC(admin, admin, 21_000_000 * 10**8);

        facet = new GRUAssetRegistryFacet();
        facet.setRegistry(address(registry));
        vm.stopPrank();
    }

    function test_setRegistry_oneTimeOnly() public {
        GRUAssetRegistryFacet localFacet = new GRUAssetRegistryFacet();
        localFacet.setRegistry(address(registry));
        vm.expectRevert("RegistryAlreadySet");
        localFacet.setRegistry(makeAddr("otherRegistry"));
    }

    function test_registerGRUCompliantAsset_cUSDT() public {
        vm.prank(admin);
        registry.registerGRUCompliantAsset(
            address(cusdt),
            "Tether USD (Compliant)",
            "cUSDT",
            6,
            "International"
        );
        assertTrue(registry.isAssetActive(address(cusdt)));
        assertEq(uint256(registry.getAssetType(address(cusdt))), uint256(UniversalAssetRegistry.AssetType.GRU));
        UniversalAssetRegistry.UniversalAsset memory asset = registry.getAsset(address(cusdt));
        assertEq(asset.symbol, "cUSDT");
        assertEq(asset.decimals, 6);
    }

    function test_registerGRUCompliantAsset_allThree() public {
        vm.startPrank(admin);
        registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International");
        registry.registerGRUCompliantAsset(address(cusdc), "USD Coin (Compliant)", "cUSDC", 6, "International");
        registry.registerGRUCompliantAsset(address(ceurc), "Euro Coin (Compliant)", "cEURC", 6, "International");
        vm.stopPrank();

        assertTrue(registry.isAssetActive(address(cusdt)));
        assertTrue(registry.isAssetActive(address(cusdc)));
        assertTrue(registry.isAssetActive(address(ceurc)));
        assertEq(uint256(registry.getAssetType(address(cusdt))), uint256(UniversalAssetRegistry.AssetType.GRU));
        assertEq(uint256(registry.getAssetType(address(cusdc))), uint256(UniversalAssetRegistry.AssetType.GRU));
        assertEq(uint256(registry.getAssetType(address(ceurc))), uint256(UniversalAssetRegistry.AssetType.GRU));

        address[] memory gruAssets = registry.getAssetsByType(UniversalAssetRegistry.AssetType.GRU);
        assertEq(gruAssets.length, 3);
    }

    function test_facet_delegates_to_registry() public {
        vm.prank(admin);
        registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International");

        assertTrue(facet.isAssetActive(address(cusdt)));
        assertEq(uint256(facet.getAssetType(address(cusdt))), uint256(UniversalAssetRegistry.AssetType.GRU));
        UniversalAssetRegistry.UniversalAsset memory viaFacet = facet.getAsset(address(cusdt));
        assertEq(viaFacet.symbol, "cUSDT");
    }

    function test_registerGRUMonetaryUnitAsset_cBTC() public {
        vm.prank(admin);
        registry.registerGRUCompliantAsset(address(cbtc), "Bitcoin (Compliant)", "cBTC", 8, "International");

        assertTrue(registry.isAssetActive(address(cbtc)));
        assertEq(uint256(registry.getAssetType(address(cbtc))), uint256(UniversalAssetRegistry.AssetType.GRU));
        UniversalAssetRegistry.UniversalAsset memory asset = registry.getAsset(address(cbtc));
        assertEq(asset.symbol, "cBTC");
        assertEq(asset.decimals, 8);
    }

    function test_GRUCCIPBridge_accepts_registered_GRU_asset() public {
        address mockRouter = makeAddr("router");
        vm.startPrank(admin);
        registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International");

        GRUCCIPBridge bridgeImpl = new GRUCCIPBridge();
        bytes memory bridgeInit = abi.encodeCall(UniversalCCIPBridge.initialize, (address(registry), mockRouter, admin));
        ERC1967Proxy bridgeProxy = new ERC1967Proxy(address(bridgeImpl), bridgeInit);
        GRUCCIPBridge bridge = GRUCCIPBridge(payable(address(bridgeProxy)));
        vm.stopPrank();

        UniversalAssetRegistry.UniversalAsset memory asset = registry.getAsset(address(cusdt));
        assertEq(uint256(asset.assetType), uint256(UniversalAssetRegistry.AssetType.GRU), "cUSDT must be GRU for bridge");
    }

    function test_revert_registerGRUCompliantAsset_notRegistrar() public {
        vm.prank(makeAddr("stranger"));
        vm.expectRevert();
        registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International");
    }

    function test_revert_registerGRUCompliantAsset_alreadyRegistered() public {
        vm.startPrank(admin);
        registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International");
        vm.expectRevert("Already registered");
        registry.registerGRUCompliantAsset(address(cusdt), "Tether USD (Compliant)", "cUSDT", 6, "International");
        vm.stopPrank();
    }
}
