// SPDX-License-Identifier: Apache-2.0
import { createMock } from "@golevelup/ts-jest";
import { GetTokenHoldersAtSnapshotRequest, GetTotalTokenHoldersAtSnapshotRequest, TakeSnapshotRequest, BalancesOfAtSnapshotRequest, } from "../../request";
import { TransactionIdFixture } from "../../../../../__tests__/fixtures/shared/DataFixture";
import LogService from "../../../../app/service/log/LogService";
import ValidatedRequest from "../../../../core/validation/ValidatedArgs";
import { ValidationError } from "../../../../core/validation/ValidationError";
import Security from "../../../in/security/Security";
import { GetTokenHoldersAtSnapshotRequestFixture, GetTotalTokenHoldersAtSnapshotRequestFixture, TakeSnapshotRequestFixture, BalancesOfAtSnapshotRequestFixture, } from "../../../../../__tests__/fixtures/snapshot/SnapshotFixture";
import { TakeSnapshotCommand } from "../../../../app/usecase/command/security/operations/snapshot/takeSnapshot/TakeSnapshotCommand";
import { GetTokenHoldersAtSnapshotQuery } from "../../../../app/usecase/query/security/snapshot/getTokenHoldersAtSnapshot/GetTokenHoldersAtSnapshotQuery";
import { GetTotalTokenHoldersAtSnapshotQuery } from "../../../../app/usecase/query/security/snapshot/getTotalTokenHoldersAtSnapshot/GetTotalTokenHoldersAtSnapshotQuery";
import { BalancesOfAtSnapshotQuery } from "../../../../app/usecase/query/security/snapshot/balancesOfAtSnapshot/BalancesOfAtSnapshotQuery";
describe("Snapshot", () => {
    let commandBusMock;
    let queryBusMock;
    let mirrorNodeMock;
    let takeSnapshotRequest;
    let getTokenHoldersAtSnapshotRequest;
    let getTotalTokenHoldersAtSnapshotRequest;
    let balancesOfAtSnapshotRequest;
    let handleValidationSpy;
    const transactionId = TransactionIdFixture.create().id;
    beforeEach(() => {
        commandBusMock = createMock();
        queryBusMock = createMock();
        mirrorNodeMock = createMock();
        handleValidationSpy = jest.spyOn(ValidatedRequest, "handleValidation");
        jest.spyOn(LogService, "logError").mockImplementation(() => { });
        Security.commandBus = commandBusMock;
        Security.queryBus = queryBusMock;
        Security.mirrorNode = mirrorNodeMock;
    });
    afterEach(() => {
        jest.resetAllMocks();
        jest.restoreAllMocks();
    });
    describe("takeSnapshot", () => {
        takeSnapshotRequest = new TakeSnapshotRequest(TakeSnapshotRequestFixture.create());
        const expectedResponse = {
            payload: true,
            transactionId: transactionId,
        };
        it("should take snapshot successfully", async () => {
            commandBusMock.execute.mockResolvedValue(expectedResponse);
            const result = await Security.takeSnapshot(takeSnapshotRequest);
            expect(handleValidationSpy).toHaveBeenCalledWith(TakeSnapshotRequest.name, takeSnapshotRequest);
            expect(commandBusMock.execute).toHaveBeenCalledWith(new TakeSnapshotCommand(takeSnapshotRequest.securityId));
            expect(result).toEqual(expectedResponse);
        });
        it("should throw an error if command execution fails", async () => {
            const error = new Error("Command execution failed");
            commandBusMock.execute.mockRejectedValue(error);
            await expect(Security.takeSnapshot(takeSnapshotRequest)).rejects.toThrow("Command execution failed");
            expect(handleValidationSpy).toHaveBeenCalledWith(TakeSnapshotRequest.name, takeSnapshotRequest);
            expect(commandBusMock.execute).toHaveBeenCalledWith(new TakeSnapshotCommand(takeSnapshotRequest.securityId));
        });
        it("should throw error if securityId is invalid", async () => {
            takeSnapshotRequest = new TakeSnapshotRequest({
                ...TakeSnapshotRequestFixture.create({
                    securityId: "invalid",
                }),
            });
            await expect(Security.takeSnapshot(takeSnapshotRequest)).rejects.toThrow(ValidationError);
        });
    });
    describe("getTokenHoldersAtSnapshot", () => {
        getTokenHoldersAtSnapshotRequest = new GetTokenHoldersAtSnapshotRequest(GetTokenHoldersAtSnapshotRequestFixture.create());
        it("should get token holders at snapshot successfully", async () => {
            const expectedResponse = {
                payload: [transactionId],
            };
            queryBusMock.execute.mockResolvedValue(expectedResponse);
            const result = await Security.getTokenHoldersAtSnapshot(getTokenHoldersAtSnapshotRequest);
            expect(handleValidationSpy).toHaveBeenCalledWith(GetTokenHoldersAtSnapshotRequest.name, getTokenHoldersAtSnapshotRequest);
            expect(queryBusMock.execute).toHaveBeenCalledTimes(1);
            expect(queryBusMock.execute).toHaveBeenCalledWith(new GetTokenHoldersAtSnapshotQuery(getTokenHoldersAtSnapshotRequest.securityId, getTokenHoldersAtSnapshotRequest.snapshotId, getTokenHoldersAtSnapshotRequest.start, getTokenHoldersAtSnapshotRequest.end));
            expect(result).toStrictEqual(expectedResponse.payload);
        });
        it("should throw an error if query execution fails", async () => {
            const error = new Error("Query execution failed");
            queryBusMock.execute.mockRejectedValue(error);
            await expect(Security.getTokenHoldersAtSnapshot(getTokenHoldersAtSnapshotRequest)).rejects.toThrow("Query execution failed");
            expect(handleValidationSpy).toHaveBeenCalledWith(GetTokenHoldersAtSnapshotRequest.name, getTokenHoldersAtSnapshotRequest);
            expect(queryBusMock.execute).toHaveBeenCalledTimes(1);
            expect(queryBusMock.execute).toHaveBeenCalledWith(new GetTokenHoldersAtSnapshotQuery(getTokenHoldersAtSnapshotRequest.securityId, getTokenHoldersAtSnapshotRequest.snapshotId, getTokenHoldersAtSnapshotRequest.start, getTokenHoldersAtSnapshotRequest.end));
        });
        it("should throw error if securityId is invalid", async () => {
            getTokenHoldersAtSnapshotRequest = new GetTokenHoldersAtSnapshotRequest({
                ...GetTokenHoldersAtSnapshotRequestFixture.create(),
                securityId: "invalid",
            });
            await expect(Security.getTokenHoldersAtSnapshot(getTokenHoldersAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
        it("should throw error if snapshotId is invalid", async () => {
            getTokenHoldersAtSnapshotRequest = new GetTokenHoldersAtSnapshotRequest({
                ...GetTokenHoldersAtSnapshotRequestFixture.create(),
                snapshotId: -1,
            });
            await expect(Security.getTokenHoldersAtSnapshot(getTokenHoldersAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
        it("should throw error if start is invalid", async () => {
            getTokenHoldersAtSnapshotRequest = new GetTokenHoldersAtSnapshotRequest({
                ...GetTokenHoldersAtSnapshotRequestFixture.create(),
                start: -1,
            });
            await expect(Security.getTokenHoldersAtSnapshot(getTokenHoldersAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
        it("should throw error if end is invalid", async () => {
            getTokenHoldersAtSnapshotRequest = new GetTokenHoldersAtSnapshotRequest({
                ...GetTokenHoldersAtSnapshotRequestFixture.create(),
                end: -1,
            });
            await expect(Security.getTokenHoldersAtSnapshot(getTokenHoldersAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
    });
    describe("getTotalTokenHoldersAtSnapshot", () => {
        getTotalTokenHoldersAtSnapshotRequest = new GetTotalTokenHoldersAtSnapshotRequest(GetTotalTokenHoldersAtSnapshotRequestFixture.create());
        it("should get total token holders successfully", async () => {
            const expectedResponse = {
                payload: 1,
            };
            queryBusMock.execute.mockResolvedValue(expectedResponse);
            const result = await Security.getTotalTokenHoldersAtSnapshot(getTotalTokenHoldersAtSnapshotRequest);
            expect(handleValidationSpy).toHaveBeenCalledWith(GetTotalTokenHoldersAtSnapshotRequest.name, getTotalTokenHoldersAtSnapshotRequest);
            expect(queryBusMock.execute).toHaveBeenCalledTimes(1);
            expect(queryBusMock.execute).toHaveBeenCalledWith(new GetTotalTokenHoldersAtSnapshotQuery(getTotalTokenHoldersAtSnapshotRequest.securityId, getTotalTokenHoldersAtSnapshotRequest.snapshotId));
            expect(result).toEqual(expectedResponse.payload);
        });
        it("should throw an error if query execution fails", async () => {
            const error = new Error("Query execution failed");
            queryBusMock.execute.mockRejectedValue(error);
            await expect(Security.getTotalTokenHoldersAtSnapshot(getTotalTokenHoldersAtSnapshotRequest)).rejects.toThrow("Query execution failed");
            expect(handleValidationSpy).toHaveBeenCalledWith(GetTotalTokenHoldersAtSnapshotRequest.name, getTotalTokenHoldersAtSnapshotRequest);
            expect(queryBusMock.execute).toHaveBeenCalledTimes(1);
            expect(queryBusMock.execute).toHaveBeenCalledWith(new GetTotalTokenHoldersAtSnapshotQuery(getTotalTokenHoldersAtSnapshotRequest.securityId, getTotalTokenHoldersAtSnapshotRequest.snapshotId));
        });
        it("should throw error if securityId is invalid", async () => {
            getTotalTokenHoldersAtSnapshotRequest = new GetTotalTokenHoldersAtSnapshotRequest({
                ...GetTotalTokenHoldersAtSnapshotRequestFixture.create(),
                securityId: "invalid",
            });
            await expect(Security.getTotalTokenHoldersAtSnapshot(getTotalTokenHoldersAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
        it("should throw error if snapshotId is invalid", async () => {
            getTotalTokenHoldersAtSnapshotRequest = new GetTotalTokenHoldersAtSnapshotRequest({
                ...GetTotalTokenHoldersAtSnapshotRequestFixture.create(),
                snapshotId: -1,
            });
            await expect(Security.getTotalTokenHoldersAtSnapshot(getTotalTokenHoldersAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
    });
    describe("balancesOfAtSnapshot", () => {
        balancesOfAtSnapshotRequest = new BalancesOfAtSnapshotRequest(BalancesOfAtSnapshotRequestFixture.create());
        const mockBalances = [
            { holder: "0x1234567890123456789012345678901234567890", balance: BigInt(1000) },
            { holder: "0x0987654321098765432109876543210987654321", balance: BigInt(2000) },
        ];
        it("should get balances at snapshot successfully", async () => {
            const expectedResponse = {
                payload: mockBalances,
            };
            queryBusMock.execute.mockResolvedValue(expectedResponse);
            const result = await Security.balancesOfAtSnapshot(balancesOfAtSnapshotRequest);
            expect(handleValidationSpy).toHaveBeenCalledWith(BalancesOfAtSnapshotRequest.name, balancesOfAtSnapshotRequest);
            expect(queryBusMock.execute).toHaveBeenCalledTimes(1);
            expect(queryBusMock.execute).toHaveBeenCalledWith(new BalancesOfAtSnapshotQuery(balancesOfAtSnapshotRequest.securityId, balancesOfAtSnapshotRequest.snapshotId, balancesOfAtSnapshotRequest.pageIndex, balancesOfAtSnapshotRequest.pageLength));
            expect(result).toStrictEqual(expectedResponse.payload);
        });
        it("should throw an error if query execution fails", async () => {
            const error = new Error("Query execution failed");
            queryBusMock.execute.mockRejectedValue(error);
            await expect(Security.balancesOfAtSnapshot(balancesOfAtSnapshotRequest)).rejects.toThrow("Query execution failed");
            expect(handleValidationSpy).toHaveBeenCalledWith(BalancesOfAtSnapshotRequest.name, balancesOfAtSnapshotRequest);
            expect(queryBusMock.execute).toHaveBeenCalledTimes(1);
            expect(queryBusMock.execute).toHaveBeenCalledWith(new BalancesOfAtSnapshotQuery(balancesOfAtSnapshotRequest.securityId, balancesOfAtSnapshotRequest.snapshotId, balancesOfAtSnapshotRequest.pageIndex, balancesOfAtSnapshotRequest.pageLength));
        });
        it("should throw error if securityId is invalid", async () => {
            balancesOfAtSnapshotRequest = new BalancesOfAtSnapshotRequest({
                ...BalancesOfAtSnapshotRequestFixture.create(),
                securityId: "invalid",
            });
            await expect(Security.balancesOfAtSnapshot(balancesOfAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
        it("should throw error if snapshotId is invalid", async () => {
            balancesOfAtSnapshotRequest = new BalancesOfAtSnapshotRequest({
                ...BalancesOfAtSnapshotRequestFixture.create(),
                snapshotId: -1,
            });
            await expect(Security.balancesOfAtSnapshot(balancesOfAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
        it("should throw error if pageIndex is invalid", async () => {
            balancesOfAtSnapshotRequest = new BalancesOfAtSnapshotRequest({
                ...BalancesOfAtSnapshotRequestFixture.create(),
                pageIndex: -1,
            });
            await expect(Security.balancesOfAtSnapshot(balancesOfAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
        it("should throw error if pageLength is invalid", async () => {
            balancesOfAtSnapshotRequest = new BalancesOfAtSnapshotRequest({
                ...BalancesOfAtSnapshotRequestFixture.create(),
                pageLength: 0,
            });
            await expect(Security.balancesOfAtSnapshot(balancesOfAtSnapshotRequest)).rejects.toThrow(ValidationError);
        });
    });
});
