import type { Operation, SignedOperation } from "@ledgerhq/types-live";
import { broadcast } from "./broadcast";
import * as network from "./network";

jest.mock("./network");
jest.mock("@ledgerhq/ledger-wallet-framework/operation", () => ({
  patchOperationWithHash: jest.fn((op, hash) => ({ ...op, hash })),
}));

const mockBroadcastTransaction = network.broadcastTransaction as jest.MockedFunction<
  typeof network.broadcastTransaction
>;

describe("broadcast", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  it("should broadcast signed transaction and return patched operation", async () => {
    const txHash = "TX_HASH_123456";
    mockBroadcastTransaction.mockResolvedValue(txHash);

    const operation: Operation = {
      id: "op-1",
      hash: "",
      type: "OUT",
      value: BigInt(1000000),
      fee: BigInt(1000),
      senders: ["SENDER"],
      recipients: ["RECIPIENT"],
      accountId: "account-1",
      date: new Date(),
      blockHash: null,
      blockHeight: null,
      extra: {},
    };

    const signedOperation: SignedOperation = {
      operation,
      signature: "abcdef123456",
    };

    const result = await broadcast({ signedOperation, account: {} as never });

    expect(result.hash).toBe(txHash);
    expect(mockBroadcastTransaction).toHaveBeenCalledWith(Buffer.from("abcdef123456", "hex"));
  });

  it("should convert hex signature to buffer before broadcasting", async () => {
    mockBroadcastTransaction.mockResolvedValue("TX_HASH");

    const signedOperation: SignedOperation = {
      operation: {
        id: "op-2",
        hash: "",
        type: "OUT",
        value: BigInt(500000),
        fee: BigInt(1000),
        senders: ["SENDER"],
        recipients: ["RECIPIENT"],
        accountId: "account-2",
        date: new Date(),
        blockHash: null,
        blockHeight: null,
        extra: {},
      },
      signature: "48656c6c6f", // "Hello" in hex
    };

    await broadcast({ signedOperation, account: {} as never });

    const expectedBuffer = Buffer.from("48656c6c6f", "hex");
    expect(mockBroadcastTransaction).toHaveBeenCalledWith(expectedBuffer);
  });

  it("should propagate network errors", async () => {
    mockBroadcastTransaction.mockRejectedValue(new Error("Broadcast failed"));

    const signedOperation: SignedOperation = {
      operation: {
        id: "op-3",
        hash: "",
        type: "OUT",
        value: BigInt(100),
        fee: BigInt(1000),
        senders: ["S"],
        recipients: ["R"],
        accountId: "a",
        date: new Date(),
        blockHash: null,
        blockHeight: null,
        extra: {},
      },
      signature: "aabb",
    };

    await expect(broadcast({ signedOperation, account: {} as never })).rejects.toThrow(
      "Broadcast failed",
    );
  });

  it("should patch operation with returned hash", async () => {
    const uniqueHash = "UNIQUE_TX_HASH_789";
    mockBroadcastTransaction.mockResolvedValue(uniqueHash);

    const signedOperation: SignedOperation = {
      operation: {
        id: "op-4",
        hash: "",
        type: "IN",
        value: BigInt(2000000),
        fee: BigInt(2000),
        senders: ["SENDER_ADDR"],
        recipients: ["RECIPIENT_ADDR"],
        accountId: "account-4",
        date: new Date(),
        blockHash: null,
        blockHeight: null,
        extra: {},
      },
      signature: "deadbeef",
    };

    const result = await broadcast({ signedOperation, account: {} as never });

    expect(result.hash).toBe(uniqueHash);
  });
});
