import { describe, it, expect, vi, afterEach } from "vitest";
import {
  shouldRevalidate,
  printYearReport,
  type YearResult,
} from "../../tools/load-quickbooks.js";

describe("shouldRevalidate — which pre-existing batch statuses get re-run", () => {
  it("re-runs resolve/validate for a batch that died mid-pipeline", () => {
    expect(shouldRevalidate("loaded")).toBe(true);
    expect(shouldRevalidate("resolved")).toBe(true);
  });

  it("re-runs resolve/validate for a completed batch (chart may have changed since)", () => {
    expect(shouldRevalidate("validated")).toBe(true);
    expect(shouldRevalidate("failed")).toBe(true);
  });

  it("leaves posted/rolled_back batches alone — the pipeline functions reject them", () => {
    expect(shouldRevalidate("posted")).toBe(false);
    expect(shouldRevalidate("rolled_back")).toBe(false);
  });
});

describe("printYearReport — operator-facing report format", () => {
  function baseResult(overrides: Partial<YearResult> = {}): YearResult {
    return {
      year: 2019, batchId: 1, isNew: true, status: "validated",
      rowCount: 1466, entryCount: 480, errorCount: 0, errorGroups: [],
      voidCount: 0, fullyVoidEntryCount: 0,
      ...overrides,
    };
  }

  afterEach(() => { vi.restoreAllMocks(); });

  it("reports a freshly-loaded year without an 'already loaded' label", () => {
    const spy = vi.spyOn(console, "log").mockImplementation(() => {});
    printYearReport(baseResult({ isNew: true, batchId: 5 }));
    expect(spy.mock.calls[0]![0]).toBe(
      "year 2019: batch 5 — 480 entries, 1466 rows, 0 errors",
    );
  });

  it("FIX 1: a skipped (already-loaded) year still reports current validation state, with status", () => {
    const spy = vi.spyOn(console, "log").mockImplementation(() => {});
    printYearReport(baseResult({
      isNew: false, batchId: 5, status: "validated",
      entryCount: 480, rowCount: 1466, errorCount: 0,
    }));
    expect(spy.mock.calls[0]![0]).toBe(
      "year 2019: already loaded (batch 5, status validated) — 480 entries, 1466 rows, 0 errors",
    );
  });

  it("surfaces distinct error messages with counts for a dirty skipped year", () => {
    const spy = vi.spyOn(console, "log").mockImplementation(() => {});
    printYearReport(baseResult({
      isNew: false, status: "failed", errorCount: 2,
      errorGroups: [
        { error: "unmapped account (or ambiguous name): Mystery Account", count: 2 },
      ],
    }));
    expect(spy.mock.calls[0]![0]).toBe(
      "year 2019: already loaded (batch 1, status failed) — 480 entries, 1466 rows, 2 errors",
    );
    expect(spy.mock.calls[1]![0]).toBe(
      "  [2x] unmapped account (or ambiguous name): Mystery Account",
    );
  });

  it("surfaces void counts when zero-amount lines were skipped", () => {
    const spy = vi.spyOn(console, "log").mockImplementation(() => {});
    printYearReport(baseResult({ voidCount: 14, fullyVoidEntryCount: 3 }));
    expect(spy.mock.calls[1]![0]).toBe(
      "  14 zero-amount lines skipped (void); 3 fully-voided entries not posted",
    );
  });

  it("says nothing about void lines when there are none", () => {
    const spy = vi.spyOn(console, "log").mockImplementation(() => {});
    printYearReport(baseResult({ voidCount: 0, fullyVoidEntryCount: 0 }));
    expect(spy.mock.calls.length).toBe(1);
  });
});
