import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { baseAvailableSerial, baseMoveLine } from "../testing/fixtures";
import { expectOk, expectErr } from "../testing/commandTestUtils";
import { SerialNumberNotFoundError } from "../lib/errors.generated";
import { run } from "./getSerialNumberHistory";

const doneMoveLine = {
  ...baseMoveLine,
  movementType: "GOODS_RECEIPT",
  executedAt: new Date("2024-01-02T00:00:00.000Z"),
  status: "DONE",
  sourceLocationId: null,
  destinationLocationId: "location-1",
};

describe("getSerialNumberHistory", () => {
  it("returns full history ordered by date", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseAvailableSerial);
    spies.select.mockReturnValueOnce([doneMoveLine]);

    const result = await run(db, { serialNumberId: "serial-1" });
    const { moveLines } = expectOk<{ moveLines: unknown[] }>(result as never);

    expect(moveLines).toHaveLength(1);
  });

  it("includes state transitions", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseAvailableSerial);
    spies.select.mockReturnValueOnce([doneMoveLine]);

    const result = await run(db, { serialNumberId: "serial-1" });
    const { moveLines } = expectOk<{
      moveLines: { movementType: string; status: string }[];
    }>(result as never);

    expect(moveLines[0].movementType).toBe("GOODS_RECEIPT");
    expect(moveLines[0].status).toBe("DONE");
  });

  it("includes location changes", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseAvailableSerial);
    spies.select.mockReturnValueOnce([doneMoveLine]);

    const result = await run(db, { serialNumberId: "serial-1" });
    const { moveLines } = expectOk<{
      moveLines: {
        sourceLocationId: string | null;
        destinationLocationId: string | null;
      }[];
    }>(result as never);

    expect(moveLines[0].sourceLocationId).toBeNull();
    expect(moveLines[0].destinationLocationId).toBe("location-1");
  });

  it("returns error when serial not found", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(undefined);

    const result = await run(db, { serialNumberId: "non-existent" });

    expectErr(result as never, SerialNumberNotFoundError);
  });

  it("returns empty history for newly created serial", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseAvailableSerial);
    spies.select.mockReturnValueOnce([]);

    const result = await run(db, { serialNumberId: "serial-1" });
    const { moveLines } = expectOk<{ moveLines: unknown[] }>(result as never);

    expect(moveLines).toEqual([]);
  });
});
