import { createKyselyMock } from "@tailor-platform/sdk/vitest";
import { describe, expect, it } from "vitest";
import type { Namespace } 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 mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseSubmittedAdjustment, baseDraftAdjustment], []);

    const result = await run(mock.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 mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseSubmittedAdjustment], []);

    const result = await run(mock.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 mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseSubmittedAdjustment], []);

    const result = await run(mock.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 mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseSubmittedAdjustment], []);

    const result = await run(mock.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 mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([]);

    const result = await run(mock.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 mock = createKyselyMock<Namespace["main-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
    mock.enqueueResults([adj1, adj2, adj3], []);

    const result = await run(mock.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 mock = createKyselyMock<Namespace["main-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" };

    mock.enqueueResults([adj1, adj2], [line1, line2]);

    const result = await run(mock.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 mock = createKyselyMock<Namespace["main-db"]>();
    mock.enqueueResults([baseDraftAdjustment], []);

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

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