import type { CryptoCurrency, Unit } from "@ledgerhq/types-cryptoassets";
import type { Account, SubAccount } from "@ledgerhq/types-live";
import { BigNumber } from "bignumber.js";
import transactionModule, { formatTransaction } from "./transaction";
import type { AlgorandTransaction, AlgorandTransactionRaw } from "./types";

// Mock dependencies
jest.mock("@ledgerhq/ledger-wallet-framework/account/index", () => ({
  getAccountCurrency: jest.fn().mockImplementation((account: Account) => account.currency),
}));

jest.mock("@ledgerhq/coin-module-framework/currencies/index", () => ({
  formatCurrencyUnit: jest.fn().mockImplementation((unit: Unit, amount: BigNumber) => {
    return `${amount.toString()} ${unit.code}`;
  }),
}));

describe("transaction", () => {
  const mockUnit: Unit = {
    name: "Algo",
    code: "ALGO",
    magnitude: 6,
  };

  const mockCurrency: CryptoCurrency = {
    type: "CryptoCurrency",
    id: "algorand",
    name: "Algorand",
    ticker: "ALGO",
    scheme: "algorand",
    color: "#000000",
    family: "algorand",
    units: [mockUnit],
    explorerViews: [],
    managerAppName: "Algorand",
  };

  const mockMainAccount = {
    id: "algorand-account-1",
    currency: mockCurrency,
    subAccounts: [] as SubAccount[],
  } as Account;

  describe("formatTransaction", () => {
    it("should format a SEND transaction", () => {
      const transaction: AlgorandTransaction = {
        family: "algorand",
        mode: "send",
        subAccountId: undefined,
        amount: new BigNumber("1000000"),
        recipient: "RECIPIENT_ADDRESS",
        fees: new BigNumber("1000"),
        useAllAmount: false,
      };

      const result = formatTransaction(transaction, mockMainAccount);

      expect(result).toContain("SEND");
      expect(result).toContain("1000000 ALGO");
      expect(result).toContain("TO RECIPIENT_ADDRESS");
      expect(result).toContain("fees=1000 ALGO");
    });

    it("should format a claimReward transaction", () => {
      const transaction: AlgorandTransaction = {
        family: "algorand",
        mode: "claimReward",
        subAccountId: undefined,
        amount: new BigNumber("0"),
        recipient: "RECIPIENT_ADDRESS",
        fees: new BigNumber("1000"),
        useAllAmount: false,
      };

      const result = formatTransaction(transaction, mockMainAccount);

      expect(result).toContain("CLAIM REWARD");
    });

    it("should format an optIn transaction", () => {
      const transaction: AlgorandTransaction = {
        family: "algorand",
        mode: "optIn",
        subAccountId: undefined,
        amount: new BigNumber("0"),
        recipient: "RECIPIENT_ADDRESS",
        fees: new BigNumber("1000"),
        useAllAmount: false,
      };

      const result = formatTransaction(transaction, mockMainAccount);

      expect(result).toContain("OPT_IN");
    });

    it("should format MAX amount when useAllAmount is true", () => {
      const transaction: AlgorandTransaction = {
        family: "algorand",
        mode: "send",
        subAccountId: undefined,
        amount: new BigNumber("1000000"),
        recipient: "RECIPIENT_ADDRESS",
        fees: new BigNumber("1000"),
        useAllAmount: true,
      };

      const result = formatTransaction(transaction, mockMainAccount);

      expect(result).toContain("MAX");
    });

    it("should show ? for undefined fees", () => {
      const transaction: AlgorandTransaction = {
        family: "algorand",
        mode: "send",
        subAccountId: undefined,
        amount: new BigNumber("1000000"),
        recipient: "RECIPIENT_ADDRESS",
        fees: null,
        useAllAmount: false,
      };

      const result = formatTransaction(transaction, mockMainAccount);

      expect(result).toContain("fees=?");
    });
  });

  describe("fromTransactionRaw", () => {
    it("should convert raw transaction to transaction", () => {
      const raw: AlgorandTransactionRaw = {
        family: "algorand",
        mode: "send",
        amount: "1000000",
        recipient: "RECIPIENT_ADDRESS",
        fees: "1000",
        memo: "test memo",
        assetId: "12345",
      };

      const result = transactionModule.fromTransactionRaw(raw);

      expect(result.family).toBe("algorand");
      expect(result.mode).toBe("send");
      expect(result.fees).toBeInstanceOf(BigNumber);
      expect(result.fees?.toString()).toBe("1000");
      expect(result.memo).toBe("test memo");
      expect(result.assetId).toBe("12345");
    });

    it("should handle null fees", () => {
      const raw: AlgorandTransactionRaw = {
        family: "algorand",
        mode: "send",
        amount: "1000000",
        recipient: "RECIPIENT_ADDRESS",
        fees: null,
        memo: undefined,
        assetId: undefined,
      };

      const result = transactionModule.fromTransactionRaw(raw);

      expect(result.fees).toBeNull();
    });
  });

  describe("toTransactionRaw", () => {
    it("should convert transaction to raw transaction", () => {
      const transaction: AlgorandTransaction = {
        family: "algorand",
        mode: "send",
        amount: new BigNumber("1000000"),
        recipient: "RECIPIENT_ADDRESS",
        fees: new BigNumber("1000"),
        memo: "test memo",
        assetId: "12345",
      };

      const result = transactionModule.toTransactionRaw(transaction);

      expect(result.family).toBe("algorand");
      expect(result.mode).toBe("send");
      expect(result.fees).toBe("1000");
      expect(result.memo).toBe("test memo");
      expect(result.assetId).toBe("12345");
    });

    it("should handle null fees", () => {
      const transaction: AlgorandTransaction = {
        family: "algorand",
        mode: "send",
        amount: new BigNumber("1000000"),
        recipient: "RECIPIENT_ADDRESS",
        fees: null,
        memo: undefined,
        assetId: undefined,
      };

      const result = transactionModule.toTransactionRaw(transaction);

      expect(result.fees).toBeNull();
    });
  });

  describe("round-trip conversion", () => {
    it("should preserve data through toRaw and fromRaw", () => {
      const original: AlgorandTransaction = {
        family: "algorand",
        mode: "optIn",
        amount: new BigNumber("500000"),
        recipient: "SOME_ADDRESS",
        fees: new BigNumber("2000"),
        memo: "round trip test",
        assetId: "99999",
      };

      const raw = transactionModule.toTransactionRaw(original);
      const restored = transactionModule.fromTransactionRaw(raw);

      expect(restored.family).toBe(original.family);
      expect(restored.mode).toBe(original.mode);
      expect(restored.fees?.toString()).toBe(original.fees?.toString());
      expect(restored.memo).toBe(original.memo);
      expect(restored.assetId).toBe(original.assetId);
    });
  });
});
