import { describe, it, expect, afterEach } from "vitest";
import prisma from "../config/database";
import { createInvoice, getInvoiceStats } from "../services/invoices.service";
import { createOffer } from "../services/offers.service";

// These tests prove that DRAFT documents (a status introduced by the DB-drafts
// feature) are NOT counted in period reporting aggregations. A draft is not a
// real financial document, so its VAT/amount/created-count must be excluded.
//
// They hit the real `app_test` DB (per the suite convention) via the service
// layer directly. Every row we create is tracked and removed in afterEach so we
// leave the DB clean, and we reset the touched number sequences so a finalized
// (issued/active) doc created here doesn't leak a consumed number into other
// files.

const invoiceIds: number[] = [];
const offerIds: number[] = [];

afterEach(async () => {
  for (const id of invoiceIds)
    await prisma.invoices.deleteMany({ where: { id } });
  for (const id of offerIds)
    await prisma.quotations.deleteMany({ where: { id } });
  invoiceIds.length = 0;
  offerIds.length = 0;
  await prisma.number_sequences.deleteMany({
    where: { type: { in: ["invoice", "offer"] } },
  });
});

describe("drafts excluded from invoice monthly stats (VAT)", () => {
  it("a draft invoice's VAT is NOT in vat_month while an issued one IS", async () => {
    // Pick a far-future month so no seeded/other-test invoices fall in it and
    // the totals are deterministic. issue_date drives the stats window.
    const year = 2099;
    const month = 7;
    const issueDate = `${year}-0${month}-15`;

    // Single CZK line, 21% VAT: base 1000, VAT 210. Identical on both docs so
    // the only difference between "draft excluded" and "issued counted" is the
    // status filter under test.
    const item = { quantity: 1, unit_price: 1000, vat_rate: 21 };

    const draft = await createInvoice({
      status: "draft",
      currency: "CZK",
      apply_vat: true,
      vat_rate: 21,
      issue_date: issueDate,
      items: [item],
    });
    invoiceIds.push(draft.id);
    expect(draft.status).toBe("draft");
    expect(draft.invoice_number).toBeNull();

    // Sanity: with ONLY the draft present, vat_month must be empty (draft excluded).
    const draftOnly = await getInvoiceStats(month, year);
    expect(draftOnly.vat_month).toEqual([]);
    expect(draftOnly.vat_month_czk).toBe(0);

    const issued = await createInvoice({
      status: "issued",
      currency: "CZK",
      apply_vat: true,
      vat_rate: 21,
      issue_date: issueDate,
      items: [item],
    });
    invoiceIds.push(issued.id);
    expect(issued.status).toBe("issued");

    // Now the issued invoice's VAT (210 CZK) must be counted — and ONLY that
    // one, not the draft's (which would double it to 420 if drafts leaked in).
    const stats = await getInvoiceStats(month, year);
    const czk = stats.vat_month.find((v) => v.currency === "CZK");
    expect(czk?.amount).toBe(210);
    expect(stats.vat_month_czk).toBe(210);
  });
});

describe("drafts excluded from quotations created-this-month count", () => {
  it("a draft offer is NOT counted; an active one IS", async () => {
    // Mirror the dashboard route's exact aggregation: count quotations created
    // in the window, excluding drafts. Use a tight window around `now` (the
    // route uses the current month) and assert the delta this test introduces.
    const now = new Date();
    const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
    const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 1);

    const countNonDraft = () =>
      prisma.quotations.count({
        where: {
          status: { not: "draft" },
          created_at: { gte: monthStart, lt: monthEnd },
        },
      });

    const before = await countNonDraft();

    const draft = await createOffer({ status: "draft" });
    if ("error" in draft) throw new Error(`createOffer failed: ${draft.error}`);
    offerIds.push(draft.id);
    expect(draft.status).toBe("draft");

    // The draft must not move the non-draft count.
    expect(await countNonDraft()).toBe(before);

    const active = await createOffer({ status: "active" });
    if ("error" in active)
      throw new Error(`createOffer failed: ${active.error}`);
    offerIds.push(active.id);

    // The active offer is a real document → the count goes up by exactly 1.
    expect(await countNonDraft()).toBe(before + 1);
  });
});
