/**
 * @jest-environment jsdom
 */

// Mock the deviceTransactionConfig module before importing anything else
jest.mock("../transaction/deviceTransactionConfig", () => ({
  getDeviceTransactionConfig: jest.fn(),
}));

import { renderHook, waitFor } from "@testing-library/react";
import { useDeviceTransactionConfig } from "./useDeviceTransactionConfig";
import { getDeviceTransactionConfig } from "../transaction/deviceTransactionConfig";
import { Account } from "@ledgerhq/types-live";
import { Transaction, TransactionStatus } from "../generated/types";
import BigNumber from "bignumber.js";
import { getCryptoCurrencyById } from "../currencies/index";

const mockGetDeviceTransactionConfig = getDeviceTransactionConfig as jest.MockedFunction<
  typeof getDeviceTransactionConfig
>;

const btc = getCryptoCurrencyById("bitcoin");

describe("useDeviceTransactionConfig", () => {
  const mockAccount: Account = {
    type: "Account",
    id: "test-account-id",
    seedIdentifier: "seed-id",
    derivationMode: "" as const,
    index: 0,
    freshAddress: "test-address",
    freshAddressPath: "44'/0'/0'/0/0",
    used: true,
    balance: new BigNumber(1000000),
    spendableBalance: new BigNumber(1000000),
    creationDate: new Date(),
    blockHeight: 100,
    currency: btc,
    operationsCount: 0,
    operations: [],
    pendingOperations: [],
    lastSyncDate: new Date(),
    balanceHistoryCache: {
      HOUR: { latestDate: null, balances: [] },
      DAY: { latestDate: null, balances: [] },
      WEEK: { latestDate: null, balances: [] },
    },
    swapHistory: [],
  };

  const mockTransaction: Transaction = {
    family: "bitcoin" as any,
    amount: new BigNumber(100),
    recipient: "test-recipient",
    useAllAmount: false,
  } as Transaction;

  const mockStatus: TransactionStatus = {
    errors: {},
    warnings: {},
    estimatedFees: new BigNumber(10),
    amount: new BigNumber(100),
    totalSpent: new BigNumber(110),
  } as TransactionStatus;

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it("should load device transaction config fields successfully", async () => {
    const mockFields = [
      { type: "amount", label: "Amount" },
      { type: "fees", label: "Fees" },
    ];

    mockGetDeviceTransactionConfig.mockResolvedValue(mockFields as any);

    const { result } = renderHook(() =>
      useDeviceTransactionConfig({
        account: mockAccount,
        parentAccount: null,
        transaction: mockTransaction,
        status: mockStatus,
      }),
    );

    // Initially loading
    expect(result.current.loading).toBe(true);
    expect(result.current.fields).toEqual([]);

    // Wait for async operation
    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(result.current.fields).toEqual(mockFields);
    expect(mockGetDeviceTransactionConfig).toHaveBeenCalledWith({
      account: mockAccount,
      parentAccount: null,
      transaction: mockTransaction,
      status: mockStatus,
    });
  });

  it("should handle errors gracefully", async () => {
    const consoleErrorSpy = jest.spyOn(console, "error").mockImplementation();
    mockGetDeviceTransactionConfig.mockRejectedValue(new Error("Test error"));

    const { result } = renderHook(() =>
      useDeviceTransactionConfig({
        account: mockAccount,
        parentAccount: null,
        transaction: mockTransaction,
        status: mockStatus,
      }),
    );

    expect(result.current.loading).toBe(true);

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(result.current.fields).toEqual([]);
    expect(consoleErrorSpy).toHaveBeenCalledWith(
      "Failed to load device transaction config:",
      expect.any(Error),
    );

    consoleErrorSpy.mockRestore();
  });

  it("should reload fields when dependencies change", async () => {
    const mockFields1 = [{ type: "amount", label: "Amount 1" }];
    const mockFields2 = [{ type: "amount", label: "Amount 2" }];

    mockGetDeviceTransactionConfig
      .mockResolvedValueOnce(mockFields1 as any)
      .mockResolvedValueOnce(mockFields2 as any);

    const { result, rerender } = renderHook(
      ({ transaction }) =>
        useDeviceTransactionConfig({
          account: mockAccount,
          parentAccount: null,
          transaction,
          status: mockStatus,
        }),
      {
        initialProps: { transaction: mockTransaction },
      },
    );

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(result.current.fields).toEqual(mockFields1);

    // Change transaction
    const newTransaction = {
      ...mockTransaction,
      amount: new BigNumber(200),
    };

    rerender({ transaction: newTransaction });

    await waitFor(() => {
      expect(result.current.loading).toBe(false);
    });

    expect(result.current.fields).toEqual(mockFields2);
    expect(mockGetDeviceTransactionConfig).toHaveBeenCalledTimes(2);
  });

  it("should cleanup on unmount", async () => {
    const mockFields = [{ type: "amount", label: "Amount" }];
    mockGetDeviceTransactionConfig.mockResolvedValue(mockFields as any);

    const { unmount } = renderHook(() =>
      useDeviceTransactionConfig({
        account: mockAccount,
        parentAccount: null,
        transaction: mockTransaction,
        status: mockStatus,
      }),
    );

    unmount();

    // Should not throw any errors
    expect(mockGetDeviceTransactionConfig).toHaveBeenCalled();
  });
});
