import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { expectOk } from "../testing/commandTestUtils";
import {
  baseSubmittedAdjustment,
  baseDraftAdjustment,
  baseConfirmedAdjustment,
  correctionIncreaseLine,
} from "../testing/fixtures";
import { run } from "./listStockAdjustments";

describe("listStockAdjustments", () => {
  it("lists all stock adjustments", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([baseSubmittedAdjustment, baseDraftAdjustment]) // StockAdjustments
      .mockReturnValueOnce([]); // Lines

    const result = await run(db, {});
    const value = expectOk<{
      items: { stockAdjustment: { id: string }; stockAdjustmentLines: unknown[] }[];
      hasNextPage: boolean;
    }>(result as never);

    expect(value.items).toHaveLength(2);
    expect(value.hasNextPage).toBe(false);
  });

  it("filters by adjustmentType", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([baseSubmittedAdjustment]) // CORRECTION type
      .mockReturnValueOnce([]); // Lines

    const result = await run(db, { adjustmentType: "CORRECTION" });
    const value = expectOk<{
      items: { stockAdjustment: { id: string; adjustmentType: string } }[];
      hasNextPage: boolean;
    }>(result as never);

    expect(value.items).toHaveLength(1);
    expect(value.items[0].stockAdjustment.adjustmentType).toBe("CORRECTION");
  });

  it("filters by status", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([baseSubmittedAdjustment]) // SUBMITTED status
      .mockReturnValueOnce([]); // Lines

    const result = await run(db, { status: "SUBMITTED" });
    const value = expectOk<{
      items: { stockAdjustment: { status: string } }[];
      hasNextPage: boolean;
    }>(result as never);

    expect(value.items).toHaveLength(1);
    expect(value.items[0].stockAdjustment.status).toBe("SUBMITTED");
  });

  it("filters by date range", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([baseSubmittedAdjustment]).mockReturnValueOnce([]); // Lines

    const result = await run(db, {
      dateFrom: new Date("2024-01-01"),
      dateTo: new Date("2024-12-31"),
    });
    const value = expectOk<{
      items: { stockAdjustment: { id: string } }[];
      hasNextPage: boolean;
    }>(result as never);

    expect(value.items).toHaveLength(1);
  });

  it("returns empty list when no matches", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce([]); // No adjustments

    const result = await run(db, { status: "SUBMITTED" });
    const value = expectOk<{
      items: unknown[];
      hasNextPage: boolean;
    }>(result as never);

    expect(value.items).toHaveLength(0);
    expect(value.hasNextPage).toBe(false);
  });

  it("paginates results", async () => {
    const { db, spies } = createMockDb<DB>();
    // Return 3 items when limit is 2 (limit+1 strategy)
    const adj1 = { ...baseDraftAdjustment, id: "adj-a" };
    const adj2 = { ...baseSubmittedAdjustment, id: "adj-b" };
    const adj3 = { ...baseConfirmedAdjustment, id: "adj-c" }; // Extra item signals hasNextPage
    spies.select
      .mockReturnValueOnce([adj1, adj2, adj3]) // 3 adjustments (limit=2 + 1 extra)
      .mockReturnValueOnce([]); // Lines

    const result = await run(db, { limit: 2 });
    const value = expectOk<{
      items: { stockAdjustment: { id: string } }[];
      hasNextPage: boolean;
    }>(result as never);

    expect(value.items).toHaveLength(2);
    expect(value.hasNextPage).toBe(true);
    expect(value.items[0].stockAdjustment.id).toBe("adj-a");
    expect(value.items[1].stockAdjustment.id).toBe("adj-b");
  });

  it("includes stockAdjustmentLines grouped by adjustment", async () => {
    const { db, spies } = createMockDb<DB>();
    const adj1 = { ...baseDraftAdjustment, id: "adj-x" };
    const adj2 = { ...baseSubmittedAdjustment, id: "adj-y" };
    const line1 = { ...correctionIncreaseLine, stockAdjustmentId: "adj-x" };
    const line2 = { ...correctionIncreaseLine, id: "adj-line-b", stockAdjustmentId: "adj-y" };

    spies.select
      .mockReturnValueOnce([adj1, adj2]) // Adjustments
      .mockReturnValueOnce([line1, line2]); // Lines

    const result = await run(db, {});
    const value = expectOk<{
      items: {
        stockAdjustment: { id: string };
        stockAdjustmentLines: { stockAdjustmentId: string }[];
      }[];
    }>(result as never);

    expect(value.items).toHaveLength(2);
    expect(value.items[0].stockAdjustmentLines).toHaveLength(1);
    expect(value.items[0].stockAdjustmentLines[0].stockAdjustmentId).toBe("adj-x");
    expect(value.items[1].stockAdjustmentLines).toHaveLength(1);
    expect(value.items[1].stockAdjustmentLines[0].stockAdjustmentId).toBe("adj-y");
  });

  it("paginates results with hasNextPage=false when no more results", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select
      .mockReturnValueOnce([baseDraftAdjustment]) // Only 1 item (limit=2)
      .mockReturnValueOnce([]); // Lines

    const result = await run(db, { limit: 2 });
    const value = expectOk<{
      items: unknown[];
      hasNextPage: boolean;
    }>(result as never);

    expect(value.items).toHaveLength(1);
    expect(value.hasNextPage).toBe(false);
  });
});
