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

import {Test} from "forge-std/Test.sol";
import {AccountWalletRegistry} from "@emoney/AccountWalletRegistry.sol";
import {IAccountWalletRegistry} from "@emoney/interfaces/IAccountWalletRegistry.sol";

contract AccountWalletRegistryTest is Test {
    AccountWalletRegistry public registry;
    address public admin;
    address public accountManager;

    bytes32 public accountRefId1 = keccak256("account1");
    bytes32 public walletRefId1 = keccak256("wallet1");
    bytes32 public walletRefId2 = keccak256("wallet2");
    bytes32 public provider1 = keccak256("METAMASK");
    bytes32 public provider2 = keccak256("FIREBLOCKS");

    function setUp() public {
        admin = address(0x1);
        accountManager = address(0x2);

        registry = new AccountWalletRegistry(admin);

        vm.startPrank(admin);
        registry.grantRole(registry.ACCOUNT_MANAGER_ROLE(), accountManager);
        vm.stopPrank();
    }

    function test_linkAccountToWallet() public {
        vm.expectEmit(true, true, false, true);
        emit AccountWalletLinked(accountRefId1, walletRefId1, provider1, uint64(block.timestamp));

        vm.prank(accountManager);
        registry.linkAccountToWallet(accountRefId1, walletRefId1, provider1);

        assertTrue(registry.isLinked(accountRefId1, walletRefId1));
        assertTrue(registry.isActive(accountRefId1, walletRefId1));

        IAccountWalletRegistry.WalletLink[] memory wallets = registry.getWallets(accountRefId1);
        assertEq(wallets.length, 1);
        assertEq(wallets[0].walletRefId, walletRefId1);
        assertEq(wallets[0].provider, provider1);
        assertTrue(wallets[0].active);
    }

    function test_linkMultipleWallets() public {
        vm.prank(accountManager);
        registry.linkAccountToWallet(accountRefId1, walletRefId1, provider1);

        vm.prank(accountManager);
        registry.linkAccountToWallet(accountRefId1, walletRefId2, provider2);

        IAccountWalletRegistry.WalletLink[] memory wallets = registry.getWallets(accountRefId1);
        assertEq(wallets.length, 2);
        assertEq(wallets[0].walletRefId, walletRefId1);
        assertEq(wallets[1].walletRefId, walletRefId2);
    }

    function test_unlinkAccountFromWallet() public {
        vm.prank(accountManager);
        registry.linkAccountToWallet(accountRefId1, walletRefId1, provider1);

        assertTrue(registry.isActive(accountRefId1, walletRefId1));

        vm.expectEmit(true, true, false, false);
        emit AccountWalletUnlinked(accountRefId1, walletRefId1);

        vm.prank(accountManager);
        registry.unlinkAccountFromWallet(accountRefId1, walletRefId1);

        assertTrue(registry.isLinked(accountRefId1, walletRefId1)); // Still linked
        assertFalse(registry.isActive(accountRefId1, walletRefId1)); // But inactive
    }

    function test_getAccounts() public {
        bytes32 accountRefId2 = keccak256("account2");

        vm.prank(accountManager);
        registry.linkAccountToWallet(accountRefId1, walletRefId1, provider1);

        vm.prank(accountManager);
        registry.linkAccountToWallet(accountRefId2, walletRefId1, provider1);

        bytes32[] memory accounts = registry.getAccounts(walletRefId1);
        assertEq(accounts.length, 2);
    }

    function test_linkAccountToWallet_reactivate() public {
        vm.prank(accountManager);
        registry.linkAccountToWallet(accountRefId1, walletRefId1, provider1);

        vm.prank(accountManager);
        registry.unlinkAccountFromWallet(accountRefId1, walletRefId1);

        assertFalse(registry.isActive(accountRefId1, walletRefId1));

        // Reactivate
        vm.prank(accountManager);
        registry.linkAccountToWallet(accountRefId1, walletRefId1, provider1);

        assertTrue(registry.isActive(accountRefId1, walletRefId1));
    }
    
    // Helper events for testing (match IAccountWalletRegistry events)
    event AccountWalletLinked(
        bytes32 indexed accountRefId,
        bytes32 indexed walletRefId,
        bytes32 provider,
        uint64 linkedAt
    );
    
    event AccountWalletUnlinked(bytes32 indexed accountRefId, bytes32 indexed walletRefId);
}

