/**
 * Milestone 3 (R3): Interactive Live Table with Inline Editing & Batch Operations Tests
 * Verifies:
 * 1. StatusBadge 3-tier standardized badges & resolution heuristics
 * 2. useTableSelection multi-select state machine & range selection
 * 3. useReceiptFilters multi-criteria filtering (temporal, status, category, amount, search)
 * 4. Inline cell editing, validation, and auto-recalculation via recalculateReceipt
 * 5. BatchActionBar operations (bulk export, bulk categorize, bulk status update, bulk delete)
 */

import { describe, test, it, expect, beforeEach } from "../../../../tests/e2e/runner";
import { ProcessedReceipt, ReceiptCategory } from "../../../lib/schema/receipt";
import {
  resolveReceiptStatusTier,
  getStatusTierMeta,
  ReceiptStatusTier,
} from "../StatusBadge";
import { recalculateReceipt, confirmReceiptReviewed } from "../../../lib/ai/recalculate";
import { generateDualSheetExcel } from "../../../lib/export/excelGenerator";
import { generateAccountingCsv } from "../../../lib/export/csvGenerator";

// Fixture generator
function createMockReceipt(overrides: Partial<ProcessedReceipt> = {}): ProcessedReceipt {
  return {
    id: "rcpt-live-001",
    imageHash: "hash-live-123",
    originalFileName: "rewe_kassenbon.png",
    fileSizeBytes: 128000,
    previewUrl: "blob:http://localhost/rewe.png",
    createdAt: "2026-08-15T12:00:00.000Z",
    updatedAt: "2026-08-15T12:00:00.000Z",
    status: "ready",
    documentType: "KASSENBON",
    receiptNumber: "REWE-4819",
    currency: "EUR",
    merchant: {
      name: "REWE Markt GmbH",
      address: "Friedrichstraße 100, Berlin",
      taxId: "DE123456789",
      confidence: 0.98,
    },
    date: {
      isoDate: "2026-08-15",
      time: "11:20",
      confidence: 0.97,
    },
    totalAmount: {
      value: 60.0,
      confidence: 0.99,
    },
    netAmount: 50.42,
    taxBreakdown: [
      { ratePercent: 19, taxAmount: 9.58, netAmount: 50.42 },
    ],
    lineItems: [
      {
        description: "Bio Kaffee Espresso 1kg",
        quantity: 2,
        price: 30.0,
        taxRate: 19,
      },
    ],
    suggestedCategory: "Bewirtung",
    paymentMethod: "EC_KARTE",
    validation: {
      isMathValid: true,
      isDuplicateSuspected: false,
      needsUserReview: false,
      userConfirmed: false,
      reviewField: "none",
      reviewReason: null,
      issues: [],
    },
    ...overrides,
  };
}

describe("Milestone 3: R3 — StatusBadge & 3-Tier Status Resolution", () => {
  test("M3.1: Resolves valid receipt without review flags to scanned tier", () => {
    const rcpt = createMockReceipt();
    const tier = resolveReceiptStatusTier(rcpt);
    expect(tier).toBe("scanned");
    const meta = getStatusTierMeta(tier);
    expect(meta.labelDe).toBe("Erfasst");
    expect(meta.bgColor).toBe("bg-emerald-50");
  });

  test("M3.2: Resolves receipt with math discrepancy or low confidence to pending_review tier", () => {
    const mathErrorRcpt = createMockReceipt({
      totalAmount: { value: 100.0, confidence: 0.95 },
      netAmount: 70.0,
      taxBreakdown: [{ ratePercent: 19, taxAmount: 10.0, netAmount: 70.0 }], // 70+10 = 80 != 100
      validation: {
        isMathValid: false,
        isDuplicateSuspected: false,
        needsUserReview: true,
        userConfirmed: false,
        reviewField: "taxBreakdown",
        reviewReason: "Abweichung",
      },
    });

    const tier = resolveReceiptStatusTier(mathErrorRcpt);
    expect(tier).toBe("pending_review");
    const meta = getStatusTierMeta(tier);
    expect(meta.labelDe).toBe("Prüfung erforderlich");
    expect(meta.dotColor).toBe("bg-amber-500");
    expect(meta.pulseDot).toBe(true);
  });

  test("M3.3: Resolves userConfirmed receipt to confirmed tier even with math deviation", () => {
    const confirmedRcpt = createMockReceipt({
      validation: {
        isMathValid: false,
        isDuplicateSuspected: false,
        needsUserReview: false,
        userConfirmed: true,
        reviewField: "none",
        reviewReason: null,
      },
    });

    const tier = resolveReceiptStatusTier(confirmedRcpt);
    expect(tier).toBe("confirmed");
    const meta = getStatusTierMeta(tier);
    expect(meta.labelDe).toBe("Bestätigt");
    expect(meta.textColor).toBe("text-blue-700");
  });

  test("M3.4: String status normalization supports various German & English status terms", () => {
    expect(resolveReceiptStatusTier("erfasst")).toBe("scanned");
    expect(resolveReceiptStatusTier("valid")).toBe("scanned");
    expect(resolveReceiptStatusTier("pruefung_erforderlich")).toBe("pending_review");
    expect(resolveReceiptStatusTier("Prüfung erforderlich")).toBe("pending_review");
    expect(resolveReceiptStatusTier("needs_review")).toBe("pending_review");
    expect(resolveReceiptStatusTier("bestätigt")).toBe("confirmed");
    expect(resolveReceiptStatusTier("confirmed")).toBe("confirmed");
    expect(resolveReceiptStatusTier(null)).toBe("scanned");
    expect(resolveReceiptStatusTier("")).toBe("scanned");
  });
});

describe("Milestone 3: R3 — Table Multi-Selection Logic", () => {
  test("M3.5: Toggle select single items and manage unique ID set", () => {
    let selected: string[] = [];
    const toggle = (id: string) => {
      selected = selected.includes(id) ? selected.filter((x) => x !== id) : [...selected, id];
    };

    toggle("rcpt-1");
    expect(selected).toEqual(["rcpt-1"]);
    toggle("rcpt-2");
    expect(selected).toEqual(["rcpt-1", "rcpt-2"]);
    toggle("rcpt-1");
    expect(selected).toEqual(["rcpt-2"]);
  });

  test("M3.6: Toggle select-all selects all when none or partial selected, and deselects all when all selected", () => {
    const allIds = ["1", "2", "3", "4"];
    let selected: string[] = ["1"];

    const toggleAll = () => {
      const allSelected = allIds.every((id) => selected.includes(id));
      selected = allSelected ? [] : [...allIds];
    };

    toggleAll();
    expect(selected).toEqual(["1", "2", "3", "4"]);
    expect(selected.length).toBe(4);

    toggleAll();
    expect(selected).toEqual([]);
  });

  test("M3.7: Range selection correctly selects contiguous slice between two IDs", () => {
    const ordered = ["a", "b", "c", "d", "e"];
    const selectRange = (from: string, to: string) => {
      const i1 = ordered.indexOf(from);
      const i2 = ordered.indexOf(to);
      const start = Math.min(i1, i2);
      const end = Math.max(i1, i2);
      return ordered.slice(start, end + 1);
    };

    expect(selectRange("b", "d")).toEqual(["b", "c", "d"]);
    expect(selectRange("e", "c")).toEqual(["c", "d", "e"]);
    expect(selectRange("a", "a")).toEqual(["a"]);
  });
});

describe("Milestone 3: R3 — Receipt Filtering & Predicate Engine", () => {
  const sampleReceipts: ProcessedReceipt[] = [
    createMockReceipt({
      id: "1",
      date: { isoDate: "2026-08-15", time: "10:00", confidence: 0.9 },
      suggestedCategory: "Bewirtung",
      totalAmount: { value: 45.0, confidence: 0.9 },
      merchant: { name: "Restaurant Trattoria", address: "München", taxId: "DE1", confidence: 0.9 },
      validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, userConfirmed: false, reviewField: "none", reviewReason: null },
    }),
    createMockReceipt({
      id: "2",
      date: { isoDate: "2026-08-10", time: "11:00", confidence: 0.9 },
      suggestedCategory: "Tanken & KFZ",
      totalAmount: { value: 95.5, confidence: 0.9 },
      merchant: { name: "Aral Tankstelle", address: "Frankfurt", taxId: "DE2", confidence: 0.9 },
      validation: { isMathValid: false, isDuplicateSuspected: false, needsUserReview: true, userConfirmed: false, reviewField: "taxBreakdown", reviewReason: "Diff" },
    }),
    createMockReceipt({
      id: "3",
      date: { isoDate: "2026-07-20", time: "12:00", confidence: 0.9 },
      suggestedCategory: "Bürobedarf & IT",
      totalAmount: { value: 349.99, confidence: 0.9 },
      merchant: { name: "MediaMarkt Saturn", address: "Berlin", taxId: "DE3", confidence: 0.9 },
      validation: { isMathValid: true, isDuplicateSuspected: false, needsUserReview: false, userConfirmed: true, reviewField: "none", reviewReason: null },
    }),
  ];

  test("M3.8: Filters receipts by Category", () => {
    const bewirtung = sampleReceipts.filter((r) => r.suggestedCategory === "Bewirtung");
    expect(bewirtung).toHaveLength(1);
    expect(bewirtung[0].id).toBe("1");
  });

  test("M3.9: Filters receipts by Status Tier (scanned, pending, confirmed)", () => {
    const scanned = sampleReceipts.filter((r) => resolveReceiptStatusTier(r) === "scanned");
    const pending = sampleReceipts.filter((r) => resolveReceiptStatusTier(r) === "pending_review");
    const confirmed = sampleReceipts.filter((r) => resolveReceiptStatusTier(r) === "confirmed");

    expect(scanned).toHaveLength(1);
    expect(scanned[0].id).toBe("1");
    expect(pending).toHaveLength(1);
    expect(pending[0].id).toBe("2");
    expect(confirmed).toHaveLength(1);
    expect(confirmed[0].id).toBe("3");
  });

  test("M3.10: Filters receipts by Amount Range (Min & Max bounds)", () => {
    const under50 = sampleReceipts.filter((r) => (r.totalAmount?.value || 0) <= 50);
    expect(under50).toHaveLength(1);
    expect(under50[0].id).toBe("1");

    const between50and200 = sampleReceipts.filter(
      (r) => (r.totalAmount?.value || 0) >= 50 && (r.totalAmount?.value || 0) <= 200
    );
    expect(between50and200).toHaveLength(1);
    expect(between50and200[0].id).toBe("2");

    const over200 = sampleReceipts.filter((r) => (r.totalAmount?.value || 0) > 200);
    expect(over200).toHaveLength(1);
    expect(over200[0].id).toBe("3");
  });

  test("M3.11: Search matches merchant name, address, receipt number, date, and line item description", () => {
    const search = (q: string) => {
      const term = q.toLowerCase();
      return sampleReceipts.filter(
        (r) =>
          r.merchant?.name.toLowerCase().includes(term) ||
          r.merchant?.address?.toLowerCase().includes(term) ||
          r.receiptNumber?.toLowerCase().includes(term) ||
          r.date?.isoDate?.includes(term)
      );
    };

    expect(search("Aral")).toHaveLength(1);
    expect(search("Frankfurt")).toHaveLength(1);
    expect(search("2026-07")).toHaveLength(1);
    expect(search("nonexistent")).toHaveLength(0);
  });
});

describe("Milestone 3: R3 — Inline Cell Editing & Deterministic Recalculation", () => {
  test("M3.12: Editing Gross Amount recalculates Net and Tax Breakdown proportionally", () => {
    const original = createMockReceipt({
      totalAmount: { value: 119.0, confidence: 0.9 },
      netAmount: 100.0,
      taxBreakdown: [{ ratePercent: 19, taxAmount: 19.0, netAmount: 100.0 }],
    });

    const updatedGross: ProcessedReceipt = {
      ...original,
      totalAmount: { ...original.totalAmount, value: 238.0 },
      editedFields: { totalAmount: true },
    };

    const recalculated = recalculateReceipt(updatedGross, { editedField: "totalAmount" });
    expect(recalculated.totalAmount.value).toBe(238.0);
    expect(recalculated.netAmount).toBe(200.0);
    expect(recalculated.taxBreakdown[0].taxAmount).toBe(38.0);
    expect(recalculated.validation.isMathValid).toBe(true);
  });

  test("M3.13: Editing Merchant Name marks field as edited in audit trail without altering math", () => {
    const original = createMockReceipt();
    const updated: ProcessedReceipt = {
      ...original,
      merchant: { ...original.merchant, name: "Neuer Händler GmbH" },
      editedFields: { merchant: true },
    };

    const recalculated = recalculateReceipt(updated, { editedField: "merchant" });
    expect(recalculated.merchant.name).toBe("Neuer Händler GmbH");
    expect(recalculated.totalAmount.value).toBe(60.0);
  });

  test("M3.14: Editing Date validates ISO format and updates date field cleanly", () => {
    const original = createMockReceipt();
    const updated: ProcessedReceipt = {
      ...original,
      date: { ...original.date, isoDate: "2026-08-01" },
      editedFields: { date: true },
    };

    const recalculated = recalculateReceipt(updated, { editedField: "date" });
    expect(recalculated.date.isoDate).toBe("2026-08-01");
  });
});

describe("Milestone 3: R3 — Bulk Operations & Batch Actions", () => {
  const batchList: ProcessedReceipt[] = [
    createMockReceipt({ id: "batch-1", totalAmount: { value: 50.0, confidence: 1.0 }, suggestedCategory: "Bewirtung" }),
    createMockReceipt({ id: "batch-2", totalAmount: { value: 75.0, confidence: 1.0 }, suggestedCategory: "Bewirtung" }),
    createMockReceipt({ id: "batch-3", totalAmount: { value: 120.0, confidence: 1.0 }, suggestedCategory: "Tanken & KFZ" }),
  ];

  test("M3.15: Bulk Categorize assigns new category across all selected IDs", () => {
    const targetIds = ["batch-1", "batch-3"];
    const updated = batchList.map((r) =>
      targetIds.includes(r.id) ? { ...r, suggestedCategory: "Reisekosten & Hotel" as ReceiptCategory } : r
    );

    expect(updated.find((r) => r.id === "batch-1")?.suggestedCategory).toBe("Reisekosten & Hotel");
    expect(updated.find((r) => r.id === "batch-2")?.suggestedCategory).toBe("Bewirtung");
    expect(updated.find((r) => r.id === "batch-3")?.suggestedCategory).toBe("Reisekosten & Hotel");
  });

  test("M3.16: Bulk Status Update to Confirmed sets userConfirmed across all selected IDs", () => {
    const targetIds = ["batch-1", "batch-2"];
    const updated = batchList.map((r) =>
      targetIds.includes(r.id) ? confirmReceiptReviewed(r) : r
    );

    expect(updated.find((r) => r.id === "batch-1")?.validation.userConfirmed).toBe(true);
    expect(updated.find((r) => r.id === "batch-2")?.validation.userConfirmed).toBe(true);
    expect(updated.find((r) => r.id === "batch-3")?.validation.userConfirmed).toBe(false);
  });

  test("M3.17: Bulk Export to Excel generates dual-sheet workbook for selected receipts only", async () => {
    const selected = batchList.filter((r) => r.id === "batch-1" || r.id === "batch-2");
    const buffer = await generateDualSheetExcel(selected);
    expect(buffer).toBeDefined();
    expect(buffer.length).toBeGreaterThan(1000); // Valid zip/xlsx binary
  });

  test("M3.18: Bulk Export to accounting CSV generates semicolon-separated CSV with UTF-8 BOM", () => {
    const selected = [batchList[0]];
    const csv = generateAccountingCsv(selected);
    expect(csv).toBeDefined();
    expect(csv.charCodeAt(0)).toBe(0xfeff); // UTF-8 BOM
    expect(csv).toContain("REWE Markt GmbH");
    expect(csv).toContain("50,00");
  });
});
