"use strict";
// SPDX-License-Identifier: Apache-2.0
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
require("reflect-metadata");
const HederaWalletConnectTransactionAdapter_1 = require("./HederaWalletConnectTransactionAdapter");
const sdk_1 = require("@hiero-ledger/sdk");
const ethers_1 = require("ethers");
const WalletEvent_1 = require("../../../../app/service/event/WalletEvent");
const Wallet_1 = require("../../../../domain/context/network/Wallet");
const NotInitialized_1 = require("./error/NotInitialized");
const AccountNotSet_1 = require("./error/AccountNotSet");
const NoSettings_1 = require("./error/NoSettings");
const ConsensusNodesNotSet_1 = require("./error/ConsensusNodesNotSet");
const SigningError_1 = require("../../../out/error/SigningError");
const Account_1 = __importDefault(require("../../../../domain/context/account/Account"));
const Environment_1 = require("../../../../domain/context/network/Environment");
const TransactionResponseEnums_1 = require("../../../out/TransactionResponseEnums");
/* eslint-disable @typescript-eslint/no-explicit-any */
// Mock dependencies
const mockEventService = {
    emit: jest.fn(),
    on: jest.fn(),
};
const mockNetworkService = {
    environment: Environment_1.testnet,
    configuration: { factoryAddress: "0.0.123" },
    consensusNodes: ["0.0.3"],
    rpcNode: { baseUrl: "https://testnet.hashio.io/api" },
};
const mockMirrorNodeAdapter = {
    getAccountInfo: jest.fn(),
    getContractInfo: jest.fn(),
};
describe("HederaWalletConnectTransactionAdapter", () => {
    let adapter;
    beforeEach(() => {
        jest.clearAllMocks();
        // Create adapter with mocked dependencies
        adapter = new HederaWalletConnectTransactionAdapter_1.HederaWalletConnectTransactionAdapter(mockEventService, mockNetworkService, mockMirrorNodeAdapter);
    });
    describe("constructor", () => {
        it("should initialize with default values", () => {
            expect(adapter.projectId).toBe("");
            expect(adapter.dappMetadata).toEqual({
                name: "",
                description: "",
                url: "",
                icons: [],
            });
        });
    });
    describe("init", () => {
        it("should emit walletInit event and return current network", async () => {
            const result = await adapter.init();
            expect(result).toBe(Environment_1.testnet);
            expect(mockEventService.emit).toHaveBeenCalledWith(WalletEvent_1.WalletEvents.walletInit, expect.objectContaining({
                wallet: Wallet_1.SupportedWallets.HWALLETCONNECT,
            }));
        });
        it("should use provided network if given", async () => {
            const result = await adapter.init("mainnet");
            expect(result).toBe("mainnet");
        });
    });
    describe("register", () => {
        it("should throw NoSettings if hwcSettings is falsy", async () => {
            await expect(adapter.register(null)).rejects.toThrow(NoSettings_1.NoSettings);
        });
        it("should set projectId and dappMetadata from settings", async () => {
            const settings = {
                projectId: "test-project-id",
                dappName: "Test DApp",
                dappDescription: "Test Description",
                dappURL: "https://test.com",
            };
            // Mock connectWalletConnect to avoid actual connection
            adapter.connectWalletConnect = jest.fn().mockResolvedValue(Environment_1.testnet);
            adapter.getAccount = jest
                .fn()
                .mockReturnValue(new Account_1.default({ id: "0.0.123", publicKey: undefined, evmAddress: "0xabc" }));
            await adapter.register(settings);
            expect(adapter.projectId).toBe("test-project-id");
            expect(adapter.dappMetadata.name).toBe("Test DApp");
            expect(adapter.dappMetadata.description).toBe("Test Description");
            expect(adapter.dappMetadata.url).toBe("https://test.com");
        });
    });
    describe("processTransaction", () => {
        it("should throw NotInitialized if hederaProvider is not set", async () => {
            const tx = new sdk_1.Transaction();
            await expect(adapter.processTransaction(tx, TransactionResponseEnums_1.TransactionType.RECEIPT)).rejects.toThrow(NotInitialized_1.NotInitialized);
        });
        it("should throw AccountNotSet if account is not set", async () => {
            adapter.hederaProvider = { request: jest.fn() };
            const tx = new sdk_1.Transaction();
            await expect(adapter.processTransaction(tx, TransactionResponseEnums_1.TransactionType.RECEIPT)).rejects.toThrow(AccountNotSet_1.AccountNotSet);
        });
        it("should freeze transaction and send via hedera_signAndExecuteTransaction", async () => {
            const mockResult = { transactionId: "0.0.123@1234567890.000" };
            const mockRequest = jest.fn().mockResolvedValue(mockResult);
            adapter.hederaProvider = {
                request: mockRequest,
                session: { namespaces: { hedera: {} } },
            };
            adapter.account = new Account_1.default({ id: "0.0.456", evmAddress: "0xabc" });
            const mockTx = {
                isFrozen: jest.fn().mockReturnValue(true),
                toBytes: jest.fn().mockReturnValue(new Uint8Array([1, 2, 3])),
            };
            // Need to bypass instanceof check since we're mocking
            Object.setPrototypeOf(mockTx, sdk_1.Transaction.prototype);
            const result = await adapter.processTransaction(mockTx, TransactionResponseEnums_1.TransactionType.RECEIPT);
            expect(mockRequest).toHaveBeenCalledWith({
                method: "hedera_signAndExecuteTransaction",
                params: expect.objectContaining({
                    transactionList: expect.any(String),
                    signerAccountId: expect.stringContaining("hedera:testnet:0.0.456"),
                }),
            }, "hedera:testnet");
            expect(result.id).toBe("0.0.123@1234567890.000");
        });
        it("should use mainnet chain ref when network is mainnet", async () => {
            const mockResult = { transactionId: "0.0.123@1234567890.000" };
            const mockRequest = jest.fn().mockResolvedValue(mockResult);
            mockNetworkService.environment = Environment_1.mainnet;
            adapter.hederaProvider = {
                request: mockRequest,
                session: { namespaces: { hedera: {} } },
            };
            adapter.account = new Account_1.default({ id: "0.0.456", evmAddress: "0xabc" });
            const mockTx = {
                isFrozen: jest.fn().mockReturnValue(true),
                toBytes: jest.fn().mockReturnValue(new Uint8Array([1, 2, 3])),
            };
            Object.setPrototypeOf(mockTx, sdk_1.Transaction.prototype);
            await adapter.processTransaction(mockTx, TransactionResponseEnums_1.TransactionType.RECEIPT);
            expect(mockRequest).toHaveBeenCalledWith(expect.objectContaining({
                method: "hedera_signAndExecuteTransaction",
            }), "hedera:mainnet");
            // Reset for other tests
            mockNetworkService.environment = Environment_1.testnet;
        });
        it("should throw SigningError on request failure", async () => {
            adapter.hederaProvider = {
                request: jest.fn().mockRejectedValue(new Error("Request failed")),
                session: { namespaces: { hedera: {} } },
            };
            adapter.account = new Account_1.default({ id: "0.0.456", evmAddress: "0xabc" });
            const mockTx = {
                isFrozen: jest.fn().mockReturnValue(true),
                toBytes: jest.fn().mockReturnValue(new Uint8Array([1, 2, 3])),
            };
            Object.setPrototypeOf(mockTx, sdk_1.Transaction.prototype);
            await expect(adapter.processTransaction(mockTx, TransactionResponseEnums_1.TransactionType.RECEIPT)).rejects.toThrow(SigningError_1.SigningError);
        });
    });
    describe("supportsEvmOperations", () => {
        it("should return true when in EVM session", () => {
            adapter.hederaProvider = {
                session: { namespaces: { eip155: {} } },
            };
            expect(adapter.supportsEvmOperations()).toBe(true);
        });
        it("should return false when in native Hedera session", () => {
            adapter.hederaProvider = {
                session: { namespaces: { hedera: {}, eip155: {} } },
            };
            expect(adapter.supportsEvmOperations()).toBe(false);
        });
    });
    describe("executeContractCall (EVM session override)", () => {
        const mockIface = new ethers_1.ethers.Interface(["function transfer(address,uint256)"]);
        it("should route through eth_sendTransaction when in EVM session", async () => {
            const mockTxHash = "0xabc123";
            const mockRequest = jest.fn().mockResolvedValue(mockTxHash);
            const mockWaitForTransaction = jest.fn().mockResolvedValue({ status: 1 });
            adapter.hederaProvider = {
                request: mockRequest,
                session: { namespaces: { eip155: {} } },
            };
            adapter.account = new Account_1.default({ id: "0.0.456", evmAddress: "0xdeadbeef" });
            mockMirrorNodeAdapter.getContractInfo = jest.fn().mockResolvedValue({
                evmAddress: "0x1234567890abcdef",
            });
            // Spy on the private rpcProvider method to return the mock provider
            const rpcProviderSpy = jest
                .spyOn(adapter, "rpcProvider")
                .mockReturnValue({ waitForTransaction: mockWaitForTransaction });
            try {
                await adapter.executeContractCall("0.0.100", mockIface, "transfer", ["0x1234567890123456789012345678901234567890", 1000n], 300000);
                expect(mockRequest).toHaveBeenCalledWith(expect.objectContaining({ method: "eth_sendTransaction" }), "eip155:296");
            }
            finally {
                rpcProviderSpy.mockRestore();
            }
        });
        it("should delegate to super.executeContractCall when in native session", async () => {
            adapter.hederaProvider = {
                session: { namespaces: { hedera: {} } },
                request: jest.fn(),
            };
            adapter.account = new Account_1.default({ id: "0.0.456", evmAddress: "0xabc" });
            const superSpy = jest
                .spyOn(Object.getPrototypeOf(Object.getPrototypeOf(adapter)), "executeContractCall")
                .mockResolvedValue({ id: "mock-response" });
            await adapter.executeContractCall("0.0.100", mockIface, "transfer", ["0xrecipient", 1000n], 300000);
            expect(superSpy).toHaveBeenCalled();
            superSpy.mockRestore();
        });
    });
    describe("sign", () => {
        it("should throw NotInitialized if hederaProvider is not set", async () => {
            const tx = new sdk_1.Transaction();
            await expect(adapter.sign(tx)).rejects.toThrow(NotInitialized_1.NotInitialized);
        });
        it("should throw SigningError if message is not a Transaction", async () => {
            adapter.hederaProvider = {};
            adapter.account = new Account_1.default({ id: "0.0.456" });
            await expect(adapter.sign("some string")).rejects.toThrow(SigningError_1.SigningError);
        });
        it("should throw ConsensusNodesNotSet if no consensus nodes", async () => {
            adapter.hederaProvider = {};
            adapter.account = new Account_1.default({ id: "0.0.456" });
            mockNetworkService.consensusNodes = [];
            const tx = new sdk_1.Transaction();
            await expect(adapter.sign(tx)).rejects.toThrow(ConsensusNodesNotSet_1.ConsensusNodesNotSet);
            // Reset
            mockNetworkService.consensusNodes = ["0.0.3"];
        });
    });
    describe("stop", () => {
        it("should disconnect provider and appKit, reset session state, and emit walletDisconnect", async () => {
            const mockDisconnect = jest.fn().mockResolvedValue(undefined);
            const mockAppKitDisconnect = jest.fn().mockResolvedValue(undefined);
            adapter.hederaProvider = { disconnect: mockDisconnect };
            adapter.appKit = { disconnect: mockAppKitDisconnect };
            adapter.injectedEip155Provider = {};
            adapter._hederaSessionCaptured = true;
            const result = await adapter.stop();
            expect(result).toBe(true);
            expect(mockDisconnect).toHaveBeenCalled();
            expect(mockAppKitDisconnect).toHaveBeenCalled();
            // hederaProvider, appKit and hederaAdapter are kept alive as singletons
            // so that reconnect can reuse them without re-initialising AppKit.
            expect(adapter.hederaProvider).toBeDefined();
            expect(adapter.appKit).toBeDefined();
            // Session-specific state IS cleared on stop.
            expect(adapter.injectedEip155Provider).toBeUndefined();
            expect(adapter._hederaSessionCaptured).toBe(false);
            expect(mockEventService.emit).toHaveBeenCalledWith(WalletEvent_1.WalletEvents.walletDisconnect, expect.objectContaining({
                wallet: Wallet_1.SupportedWallets.HWALLETCONNECT,
            }));
        });
        it("should return false and handle 'No active session' errors gracefully", async () => {
            adapter.hederaProvider = {
                disconnect: jest.fn().mockRejectedValue(new Error("No active session")),
            };
            const result = await adapter.stop();
            expect(result).toBe(false);
        });
        it("should return false and log unknown errors", async () => {
            adapter.hederaProvider = {
                disconnect: jest.fn().mockRejectedValue(new Error("Unknown error")),
            };
            const result = await adapter.stop();
            expect(result).toBe(false);
        });
    });
    describe("getAccount", () => {
        it("should return the account", () => {
            const account = new Account_1.default({ id: "0.0.123", evmAddress: "0xabc" });
            adapter.account = account;
            expect(adapter.getAccount()).toBe(account);
        });
    });
    describe("isEvmSession", () => {
        it("should return true when hedera namespace is not present", () => {
            adapter.hederaProvider = {
                session: { namespaces: { eip155: {} } },
            };
            expect(adapter.isEvmSession()).toBe(true);
        });
        it("should return false when hedera namespace is present", () => {
            adapter.hederaProvider = {
                session: { namespaces: { hedera: {}, eip155: {} } },
            };
            expect(adapter.isEvmSession()).toBe(false);
        });
        it("should return true when provider has no session", () => {
            adapter.hederaProvider = {};
            expect(adapter.isEvmSession()).toBe(true);
        });
    });
    describe("isTestnet", () => {
        it("should return true when environment is testnet", () => {
            mockNetworkService.environment = Environment_1.testnet;
            expect(adapter.isTestnet()).toBe(true);
        });
        it("should return false when environment is mainnet", () => {
            mockNetworkService.environment = Environment_1.mainnet;
            expect(adapter.isTestnet()).toBe(false);
            // Reset
            mockNetworkService.environment = Environment_1.testnet;
        });
    });
    describe("evmChainId", () => {
        it("should return 296 for testnet", () => {
            mockNetworkService.environment = Environment_1.testnet;
            expect(adapter.evmChainId()).toBe("296");
        });
        it("should return 295 for mainnet", () => {
            mockNetworkService.environment = Environment_1.mainnet;
            expect(adapter.evmChainId()).toBe("295");
            // Reset
            mockNetworkService.environment = Environment_1.testnet;
        });
    });
    describe("subscribe", () => {
        it("should not throw when hederaProvider is not set", () => {
            adapter.hederaProvider = undefined;
            expect(() => adapter.subscribe()).not.toThrow();
        });
        it("should subscribe to provider events when initialized", () => {
            const mockOn = jest.fn();
            adapter.hederaProvider = { on: mockOn };
            adapter.subscribe();
            expect(mockOn).toHaveBeenCalledWith("session_delete", expect.any(Function));
            expect(mockOn).toHaveBeenCalledWith("session_update", expect.any(Function));
            expect(mockOn).toHaveBeenCalledWith("disconnect", expect.any(Function));
        });
        it("should subscribe to appKit state when appKit is available", () => {
            const mockOn = jest.fn();
            const mockSubscribeState = jest.fn();
            adapter.hederaProvider = { on: mockOn };
            adapter.appKit = { subscribeState: mockSubscribeState };
            adapter.subscribe();
            expect(mockSubscribeState).toHaveBeenCalledWith(expect.any(Function));
        });
    });
    describe("ensureInitialized", () => {
        it("should throw NotInitialized when hederaProvider is not set", () => {
            expect(() => adapter.ensureInitialized()).toThrow(NotInitialized_1.NotInitialized);
        });
        it("should throw AccountNotSet when account is not set", () => {
            adapter.hederaProvider = {};
            expect(() => adapter.ensureInitialized()).toThrow(AccountNotSet_1.AccountNotSet);
        });
        it("should not throw when both are set", () => {
            adapter.hederaProvider = {};
            adapter.account = new Account_1.default({ id: "0.0.123" });
            expect(() => adapter.ensureInitialized()).not.toThrow();
        });
    });
    describe("ensureFrozen", () => {
        it("should freeze transaction if not frozen", () => {
            adapter.account = new Account_1.default({ id: "0.0.456" });
            const mockFreeze = jest.fn();
            const mockTx = {
                isFrozen: jest.fn().mockReturnValue(false),
                _freezeWithAccountId: mockFreeze,
            };
            adapter.ensureFrozen(mockTx);
            expect(mockFreeze).toHaveBeenCalled();
        });
        it("should not freeze if already frozen", () => {
            adapter.account = new Account_1.default({ id: "0.0.456" });
            const mockFreeze = jest.fn();
            const mockTx = {
                isFrozen: jest.fn().mockReturnValue(true),
                _freezeWithAccountId: mockFreeze,
            };
            adapter.ensureFrozen(mockTx);
            expect(mockFreeze).not.toHaveBeenCalled();
        });
    });
});
