import { getCryptoCurrencyById } from "@ledgerhq/ledger-wallet-framework/currencies";
import { AccountShapeInfo } from "@ledgerhq/ledger-wallet-framework/bridge/jsHelpers";
import {
  getDerivationModesForCurrency,
  getDerivationScheme,
  runDerivationScheme,
} from "@ledgerhq/ledger-wallet-framework/derivation";
import { Operation } from "@ledgerhq/types-live";
import BigNumber from "bignumber.js";
import coinConfig from "../config";
import * as gateway from "../network/gateway";
import { generateMockKeyPair, createMockSigner } from "../test/cantonTestUtils";
import { CantonAccount } from "../types";
import { makeGetAccountShape } from "./sync";

const TEST_ADDRESS =
  "b6400f93ea1c74aea86be39b0ccc846fc5de01f12b2ad0d7c31848d6fb6eb6d9::1220c81315e2bf2524a9141bcc6cbf19b61c151e0dcaa95343c0ccf53aed7415c4ec";
const currency = getCryptoCurrencyById("canton_network");
const derivationMode = getDerivationModesForCurrency(currency)[0];
const derivationPath = runDerivationScheme(
  getDerivationScheme({ derivationMode, currency }),
  currency,
  {
    account: 0,
  },
);
const xpub = TEST_ADDRESS;
const ACCOUNT_SHAPE_INFO: AccountShapeInfo<CantonAccount> = {
  address: TEST_ADDRESS,
  currency,
  derivationMode,
  derivationPath,
  index: 0,
  initialAccount: {
    xpub,
  } as CantonAccount,
};
const TIMEOUT = 30000;

// Mock signer context for testing
const keyPair = generateMockKeyPair();
const mockSigner = createMockSigner(keyPair);
const mockSignerContext = jest.fn().mockImplementation((_deviceId, callback) => {
  return callback(mockSigner);
});

describe.skip("sync (devnet)", () => {
  beforeAll(async () => {
    coinConfig.setCoinConfig(() => ({
      gatewayUrl: "https://canton-gateway-devnet.api.live.ledger-test.com",
      useGateway: true,
      networkType: "devnet",
      nativeInstrumentId: "Amulet",
      status: {
        type: "active",
      },
    }));
  });

  describe("makeGetAccountShape", () => {
    it(
      "should fetch account shape for a valid address",
      async () => {
        const getAccountShape = makeGetAccountShape(mockSignerContext);
        const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });

        expect(result.id).toEqual(expect.any(String));
        expect(result.xpub).toBe(TEST_ADDRESS);
        expect(result.blockHeight).toBeGreaterThan(0);
        expect(result.balance?.toNumber()).toBeGreaterThanOrEqual(0);
        expect(result.spendableBalance?.toNumber()).toBeGreaterThanOrEqual(0);
        expect(result.operations).toBeInstanceOf(Array);
        expect(result.operationsCount).toBeGreaterThanOrEqual(0);

        expect(result.spendableBalance?.toNumber()).toBeLessThanOrEqual(
          result.balance?.toNumber() || 0,
        );
      },
      TIMEOUT,
    );

    it(
      "should handle address with colons correctly",
      async () => {
        const getAccountShape = makeGetAccountShape(mockSignerContext);
        const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });

        expect(result.xpub).toContain("::");
      },
      TIMEOUT,
    );

    it(
      "should merge operations correctly with initial account",
      async () => {
        const getAccountShape = makeGetAccountShape(mockSignerContext);

        const operations: Operation[] = [
          {
            id: "test-op-1",
            hash: "test-hash-1",
            accountId: "test-account",
            type: "OUT" as const,
            value: new BigNumber(1000000),
            fee: new BigNumber(100000),
            blockHash: "block-hash-1",
            blockHeight: 100,
            senders: [TEST_ADDRESS],
            recipients: ["recipient-1"],
            date: new Date("2023-01-01"),
            transactionSequenceNumber: BigNumber(100),
            extra: { uid: "uid-1" },
          },
        ];

        const result = await getAccountShape(
          {
            ...ACCOUNT_SHAPE_INFO,
            initialAccount: {
              xpub,
              operations,
              cantonResources: {
                isOnboarded: true,
                instrumentUtxoCounts: {},
                pendingTransferProposals: [],
              },
            } as unknown as CantonAccount,
          },
          { paginationConfig: {} },
        );

        expect(result.operationsCount).toBeGreaterThanOrEqual(1);
        expect(result.operations?.find(op => op.id === "test-op-1")).toMatchObject({
          id: "test-op-1",
        });
      },
      TIMEOUT,
    );

    it(
      "should take locked balance into account when calculating spendable balance",
      async () => {
        const mockGetBalance = jest.spyOn(gateway, "getBalance");

        mockGetBalance.mockResolvedValue([
          {
            instrument_id: "Amulet",
            admin_id: "native-admin",
            amount: "500",
            locked: false,
            utxo_count: 1,
          },
          {
            instrument_id: "LockedAmulet",
            admin_id: "native-admin",
            amount: "1000000",
            locked: true,
            utxo_count: 1,
          },
        ]);

        const getAccountShape = makeGetAccountShape(mockSignerContext);
        const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });

        expect(result.balance?.toNumber()).toBe(1000500);
        expect(result.spendableBalance?.toNumber()).toBe(500);

        mockGetBalance.mockRestore();
      },
      TIMEOUT,
    );

    it(
      "should call getOperations with correct cursor based on initial account",
      async () => {
        const mockGetOperations = jest.spyOn(gateway, "getOperations");
        const operation: Operation = {
          id: "test-op-1",
          hash: "test-hash-1",
          accountId: "test-account",
          type: "OUT" as const,
          value: new BigNumber(1000000),
          fee: new BigNumber(100000),
          blockHash: "block-hash-1",
          blockHeight: 100,
          senders: [TEST_ADDRESS],
          recipients: ["recipient-1"],
          date: new Date("2023-01-01"),
          transactionSequenceNumber: BigNumber(100),
          extra: { uid: "uid-1" },
        };

        const initialAccount = {
          xpub,
          operations: [operation],
          cantonResources: {
            isOnboarded: true,
            instrumentUtxoCounts: {},
            pendingTransferProposals: [],
          },
        } as unknown as CantonAccount;

        const getAccountShape = makeGetAccountShape(mockSignerContext);
        const result = await getAccountShape(
          {
            ...ACCOUNT_SHAPE_INFO,
            initialAccount,
          },
          { paginationConfig: {} },
        );

        expect(mockGetOperations).toHaveBeenCalledWith(currency, TEST_ADDRESS, {
          cursor: (operation.blockHeight || 0) + 1,
          limit: 100,
        });
        expect(result.operationsCount).toBeGreaterThan(1);
      },
      TIMEOUT,
    );

    it(
      "should call getOperations with cursor 0 when no initial account",
      async () => {
        const mockGetOperations = jest.spyOn(gateway, "getOperations");

        const getAccountShape = makeGetAccountShape(mockSignerContext);
        const result = await getAccountShape(ACCOUNT_SHAPE_INFO, { paginationConfig: {} });

        expect(result.operationsCount).toBeGreaterThanOrEqual(1);

        expect(mockGetOperations).toHaveBeenCalledWith(currency, TEST_ADDRESS, {
          cursor: 0,
          limit: 100,
        });

        mockGetOperations.mockRestore();
      },
      TIMEOUT,
    );
  });
});
