import { describe, expect, it } from "vitest";
import { createMockDb } from "../../../testing/index";
import type { DB } from "../generated/kysely-tailordb";
import { AccountingPeriodNotFoundError } from "../lib/errors.generated";
import { baseAccountingPeriod, baseJournalEntry, postedJournalEntry } from "../testing/fixtures";
import { run } from "./getSubledgerTransferStatus";

describe("getSubledgerTransferStatus", () => {
  it("returns error when accounting period does not exist", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(undefined); // AccountingPeriod lookup

    const result = await run(db, { accountingPeriodId: "nonexistent" });

    expect(result.ok).toBe(false);
    if (!result.ok) {
      expect(result.error).toBeInstanceOf(AccountingPeriodNotFoundError);
    }
  });

  it("returns status for all 4 modules", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseAccountingPeriod); // AccountingPeriod lookup
    spies.select.mockReturnValue([]);

    const result = await run(db, { accountingPeriodId: "period-1" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      expect(result.value.statuses).toHaveLength(4);
      const modules = result.value.statuses.map((s) => s.module);
      expect(modules).toEqual(["purchase", "sales", "inventory", "manufacturing"]);
    }
  });

  it("returns zero counts when no entries exist", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseAccountingPeriod); // AccountingPeriod lookup
    spies.select.mockReturnValue([]);

    const result = await run(db, { accountingPeriodId: "period-1" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      for (const status of result.value.statuses) {
        expect(status.totalExpected).toBe(0);
        expect(status.acknowledged).toBe(0);
        expect(status.outstanding).toBe(0);
      }
    }
  });

  it("correctly counts posted vs draft entries", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseAccountingPeriod); // AccountingPeriod lookup
    // First module (purchase) has 2 entries: 1 draft + 1 posted
    spies.select
      .mockReturnValueOnce([baseJournalEntry, postedJournalEntry])
      // Remaining modules have no entries
      .mockReturnValueOnce([])
      .mockReturnValueOnce([])
      .mockReturnValueOnce([]);

    const result = await run(db, { accountingPeriodId: "period-1" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      const purchaseStatus = result.value.statuses[0];
      expect(purchaseStatus.totalExpected).toBe(2);
      expect(purchaseStatus.acknowledged).toBe(1);
      expect(purchaseStatus.outstanding).toBe(1);
    }
  });

  it("returns correct outstanding counts", async () => {
    const { db, spies } = createMockDb<DB>();
    spies.select.mockReturnValueOnce(baseAccountingPeriod); // AccountingPeriod lookup
    // All entries posted for purchase module
    spies.select
      .mockReturnValueOnce([postedJournalEntry])
      // Sales has one draft
      .mockReturnValueOnce([baseJournalEntry])
      .mockReturnValueOnce([])
      .mockReturnValueOnce([]);

    const result = await run(db, { accountingPeriodId: "period-1" });

    expect(result.ok).toBe(true);
    if (result.ok) {
      const purchaseStatus = result.value.statuses[0];
      expect(purchaseStatus.totalExpected).toBe(1);
      expect(purchaseStatus.acknowledged).toBe(1);
      expect(purchaseStatus.outstanding).toBe(0);

      const salesStatus = result.value.statuses[1];
      expect(salesStatus.totalExpected).toBe(1);
      expect(salesStatus.acknowledged).toBe(0);
      expect(salesStatus.outstanding).toBe(1);
    }
  });
});
