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

import "forge-std/Test.sol";
import "../../contracts/rwa/RWAToken.sol";
import "../../contracts/rwa/RWATokenRegistry.sol";
import "../../contracts/rwa/RWATokenFactory.sol";
import "../../contracts/rwa/IRWATokenFactory.sol";
import "../../contracts/rwa/IRWAToken.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "../../contracts/rwa/RWATokenInterfaces.sol";
/// @dev Minimal UAR stub so rwa-scoped `forge test` does not pull the full upgradeable registry tree.
contract MockUniversalAssetRegistry {
    bytes32 public constant REGISTRAR_ROLE = keccak256("REGISTRAR_ROLE");
    mapping(address => bool) private _active;

    function grantRole(bytes32, address) external {}

    function registerRWAIndexAsset(
        address tokenAddress,
        string calldata,
        string calldata,
        uint8,
        string calldata
    ) external {
        _active[tokenAddress] = true;
    }

    function isAssetActive(address token) external view returns (bool) {
        return _active[token];
    }
}

contract RWATokenFactoryTest is Test {
    bytes32 internal constant METHODOLOGY_HASH =
        0x6b6e599d0ba31d048b49302e263a12f0c59502f67a35100ad5c65b503b1d4b82;

    RWATokenRegistry public registry;
    RWATokenFactory public factory;
    MockUniversalAssetRegistry public uar;

    address public admin = address(0xA11CE);
    address public owner = address(0xB0B);
    address public publisher = address(0xC0DE);

    function setUp() public {
        vm.startPrank(admin);
        uar = new MockUniversalAssetRegistry();
        registry = new RWATokenRegistry(admin);
        factory = new RWATokenFactory(admin, address(registry), address(uar));
        registry.grantRole(registry.REGISTRAR_ROLE(), address(factory));
        uar.grantRole(uar.REGISTRAR_ROLE(), address(factory));
        factory.grantRole(factory.DEPLOYER_ROLE(), admin);
        vm.stopPrank();
    }

    function test_deployLiXAU_registersInRegistryAndUAR() public {
        vm.prank(admin);
        address token = factory.deployRWAIndex(
            IRWATokenFactory.RWAProductConfig({
                indexTicker: "LiXAU",
                name: "XAU Liquidity Index (M00)",
                symbol: "LiXAU",
                decimals: 6,
                assetClass: "Commodities",
                assetGroup: "Precious Metals",
                instrumentType: "Commodity Index",
                underlyingAsset: "Gold",
                gruLayer: "M00",
                jurisdiction: "International",
                initialOwner: owner,
                complianceAdmin: admin,
                indexPublisher: publisher,
                initialIndexValue: 1_050_000,
                initialSupply: 0,
                methodologyDocumentHash: METHODOLOGY_HASH,
                registerInUniversalAssetRegistry: true
            })
        );

        assertEq(IRWAToken(token).methodologyDocumentHash(), METHODOLOGY_HASH);
        assertTrue(registry.isRegistered("LiXAU"));
        assertEq(registry.getToken("LiXAU"), token);

        IRWAToken rwa = IRWAToken(token);
        assertTrue(rwa.isRwaIndex());
        assertFalse(rwa.isEmoney());
        assertEq(rwa.indexValue(), 1_050_000);
        assertEq(keccak256(bytes(rwa.indexTicker())), keccak256("LiXAU"));

        assertTrue(uar.isAssetActive(token));
    }

    function test_revert_duplicateTicker() public {
        IRWATokenFactory.RWAProductConfig memory cfg = _liXauConfig();
        vm.prank(admin);
        factory.deployRWAIndex(cfg);
        vm.prank(admin);
        vm.expectRevert("RWATokenFactory: already registered");
        factory.deployRWAIndex(cfg);
    }

    function test_revert_invalidTicker() public {
        IRWATokenFactory.RWAProductConfig memory cfg = _liXauConfig();
        cfg.indexTicker = "cUSDT";
        vm.prank(admin);
        vm.expectRevert();
        factory.deployRWAIndex(cfg);
    }

    function test_indexPublisherUpdatesValue() public {
        vm.prank(admin);
        address token = factory.deployRWAIndex(_liXauConfig());
        vm.prank(publisher);
        IRWAToken(token).updateIndexValue(2_000_000);
        assertEq(IRWAToken(token).indexValue(), 2_000_000);
    }

    function test_eip165_and_eip712_domain() public {
        vm.prank(admin);
        address token = factory.deployRWAIndex(_liXauConfig());
        IRWAToken165 rwa = IRWAToken165(token);
        assertTrue(rwa.supportsInterface(type(IRWAToken).interfaceId));
        assertTrue(rwa.supportsInterface(type(IERC20).interfaceId));
        assertTrue(rwa.supportsInterface(type(IERC20Metadata).interfaceId));
        assertEq(rwa.eip20Version(), "1.0.0");
        assertTrue(rwa.eip712DomainSeparator() != bytes32(0));
        bytes32 h = rwa.hashIndexValueAttestation("LiXAU", 1_100_000, 1, block.timestamp + 3600);
        assertTrue(h != bytes32(0));
    }

    function test_revert_zeroMethodologyHash() public {
        IRWATokenFactory.RWAProductConfig memory cfg = _liXauConfig();
        cfg.methodologyDocumentHash = bytes32(0);
        vm.prank(admin);
        vm.expectRevert("RWATokenFactory: methodology");
        factory.deployRWAIndex(cfg);
    }

    function _liXauConfig() internal view returns (IRWATokenFactory.RWAProductConfig memory) {
        return IRWATokenFactory.RWAProductConfig({
            indexTicker: "LiXAU",
            name: "XAU Liquidity Index (M00)",
            symbol: "LiXAU",
            decimals: 6,
            assetClass: "Commodities",
            assetGroup: "Precious Metals",
            instrumentType: "Commodity Index",
            underlyingAsset: "Gold",
            gruLayer: "M00",
            jurisdiction: "International",
            initialOwner: owner,
            complianceAdmin: admin,
            indexPublisher: publisher,
            initialIndexValue: 1_000_000,
            initialSupply: 0,
            methodologyDocumentHash: METHODOLOGY_HASH,
            registerInUniversalAssetRegistry: false
        });
    }
}
