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

import {Test} from "forge-std/Test.sol";
import {AccountWalletRegistryExtended} from "../../contracts/smart-accounts/AccountWalletRegistryExtended.sol";
import {IAccountWalletRegistry} from "../../contracts/emoney/interfaces/IAccountWalletRegistry.sol";

/**
 * @title AccountWalletRegistryExtendedTest
 * @notice Tests for AccountWalletRegistryExtended with Smart Account support
 */
contract AccountWalletRegistryExtendedTest is Test {
    AccountWalletRegistryExtended public registry;
    address public admin;
    address public accountManager;
    address public smartAccountFactory;
    address public entryPoint;
    
    // Mock smart account (contract address)
    address public mockSmartAccount;
    
    bytes32 public accountRefId1 = keccak256("account1");
    bytes32 public providerSmart = keccak256("METAMASK_SMART_ACCOUNT");
    bytes32 public providerEOA = keccak256("METAMASK");

    function setUp() public {
        admin = address(0x1);
        accountManager = address(0x2);
        smartAccountFactory = address(0x3);
        entryPoint = address(0x4);
        
        // Deploy mock smart account (contract)
        mockSmartAccount = address(new MockSmartAccount());
        
        registry = new AccountWalletRegistryExtended(
            admin,
            smartAccountFactory,
            entryPoint
        );

        vm.startPrank(admin);
        registry.grantRole(registry.ACCOUNT_MANAGER_ROLE(), accountManager);
        registry.grantRole(registry.ACCOUNT_MANAGER_ROLE(), address(registry)); // Self-call from linkSmartAccount->linkAccountToWallet
        vm.stopPrank();
    }

    function test_linkSmartAccount() public {
        vm.prank(accountManager);
        registry.linkSmartAccount(accountRefId1, mockSmartAccount, providerSmart);

        bytes32 walletRefId = keccak256(abi.encodePacked(mockSmartAccount));
        assertTrue(registry.isLinked(accountRefId1, walletRefId));
        assertTrue(registry.isActive(accountRefId1, walletRefId));
        assertTrue(registry.isSmartAccount(walletRefId));
        assertTrue(registry.isSmartAccountAddress(mockSmartAccount));
    }

    function test_linkSmartAccount_revertsIfNotContract() public {
        address eoa = address(0x100);
        
        vm.prank(accountManager);
        vm.expectRevert("AccountWalletRegistryExtended: not a contract");
        registry.linkSmartAccount(accountRefId1, eoa, providerSmart);
    }

    function test_linkSmartAccount_revertsIfZeroAddress() public {
        vm.prank(accountManager);
        vm.expectRevert("AccountWalletRegistryExtended: zero smartAccount");
        registry.linkSmartAccount(accountRefId1, address(0), providerSmart);
    }

    function test_isSmartAccount() public {
        bytes32 walletRefId = keccak256(abi.encodePacked(mockSmartAccount));
        
        // Initially false
        assertFalse(registry.isSmartAccount(walletRefId));
        
        // Link smart account
        vm.prank(accountManager);
        registry.linkSmartAccount(accountRefId1, mockSmartAccount, providerSmart);
        
        // Now true
        assertTrue(registry.isSmartAccount(walletRefId));
    }

    function test_isSmartAccountAddress() public {
        // Initially false
        assertFalse(registry.isSmartAccountAddress(mockSmartAccount));
        
        // Link smart account
        vm.prank(accountManager);
        registry.linkSmartAccount(accountRefId1, mockSmartAccount, providerSmart);
        
        // Now true
        assertTrue(registry.isSmartAccountAddress(mockSmartAccount));
    }

    function test_setSmartAccountFactory() public {
        address newFactory = address(0x5);
        
        vm.prank(admin);
        registry.setSmartAccountFactory(newFactory);
        
        assertEq(registry.smartAccountFactory(), newFactory);
    }

    function test_setSmartAccountFactory_revertsIfNotAdmin() public {
        address newFactory = address(0x5);
        
        vm.prank(accountManager);
        vm.expectRevert();
        registry.setSmartAccountFactory(newFactory);
    }

    function test_setEntryPoint() public {
        address newEntryPoint = address(0x6);
        
        vm.prank(admin);
        registry.setEntryPoint(newEntryPoint);
        
        assertEq(registry.entryPoint(), newEntryPoint);
    }

    function test_setEntryPoint_revertsIfNotAdmin() public {
        address newEntryPoint = address(0x6);
        
        vm.prank(accountManager);
        vm.expectRevert();
        registry.setEntryPoint(newEntryPoint);
    }

    function test_supportsBothEOAAndSmartAccount() public {
        address eoa = address(0x100);
        bytes32 eoaWalletRefId = keccak256(abi.encodePacked(eoa));
        
        // Link EOA (using parent function)
        vm.prank(accountManager);
        registry.linkAccountToWallet(accountRefId1, eoaWalletRefId, providerEOA);
        
        // Link Smart Account
        vm.prank(accountManager);
        registry.linkSmartAccount(accountRefId1, mockSmartAccount, providerSmart);
        
        // Both should be linked
        assertTrue(registry.isLinked(accountRefId1, eoaWalletRefId));
        assertTrue(registry.isLinked(accountRefId1, keccak256(abi.encodePacked(mockSmartAccount))));
        
        // Only smart account should be marked as smart account
        assertFalse(registry.isSmartAccount(eoaWalletRefId));
        assertTrue(registry.isSmartAccount(keccak256(abi.encodePacked(mockSmartAccount))));
    }

    // Events
    event SmartAccountLinked(
        bytes32 indexed accountRefId,
        address indexed smartAccount,
        bytes32 provider
    );
}

// Mock Smart Account contract for testing
contract MockSmartAccount {
    // Empty contract to simulate smart account
}
