import { DatabaseSync } from "node:sqlite";
import { describe, it, expect, beforeEach } from "vitest";
import {
  initSchema,
  insertComplaint,
  markNotified,
  getPendingNotifications,
  getComplaintById,
  countBySeverity,
  getComplaintsByChatId,
  getRecentComplaints,
} from "./store.js";

function makeDb(): DatabaseSync {
  const db = new DatabaseSync(":memory:");
  initSchema(db);
  return db;
}

const baseParams = {
  agentId: "agent-1",
  channelId: "whatsapp",
  chatId: "wa-+15551234567",
  fromSender: "+15551234567",
  content: "Your product is broken!",
  result: {
    is_complaint: true as const,
    severity: "high" as const,
    category: "product_defect" as const,
    summary: "Product broken",
  },
};

describe("store", () => {
  let db: DatabaseSync;

  beforeEach(() => {
    db = makeDb();
  });

  it("creates complaints table on init", () => {
    const tables = db
      .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='complaints'")
      .all();
    expect(tables).toHaveLength(1);
  });

  it("initSchema is idempotent", () => {
    expect(() => initSchema(db)).not.toThrow();
  });

  it("complaints table has chat_id column", () => {
    const cols = db.prepare("PRAGMA table_info(complaints)").all() as Array<{ name: string }>;
    expect(cols.find((c) => c.name === "chat_id")).toBeDefined();
  });

  it("inserts a complaint record and returns id", () => {
    const id = insertComplaint(db, baseParams);
    expect(typeof id).toBe("number");
    expect(id).toBeGreaterThan(0);
  });

  it("stores all fields correctly", () => {
    const id = insertComplaint(db, baseParams);
    const row = getComplaintById(db, id);
    expect(row?.agent_id).toBe("agent-1");
    expect(row?.channel_id).toBe("whatsapp");
    expect(row?.chat_id).toBe("wa-+15551234567");
    expect(row?.from_sender).toBe("+15551234567");
    expect(row?.content).toBe("Your product is broken!");
    expect(row?.is_complaint).toBe(1);
    expect(row?.severity).toBe("high");
    expect(row?.category).toBe("product_defect");
    expect(row?.summary).toBe("Product broken");
  });

  it("insertComplaint persists chat_id", () => {
    const id = insertComplaint(db, { ...baseParams, chatId: "tg-chat-42" });
    const row = getComplaintById(db, id);
    expect(row?.chat_id).toBe("tg-chat-42");
  });

  it("stores non-complaint with is_complaint=0", () => {
    const id = insertComplaint(db, {
      ...baseParams,
      result: { is_complaint: false, severity: "low", category: "other", summary: "" },
    });
    const row = getComplaintById(db, id);
    expect(row?.is_complaint).toBe(0);
  });

  it("notified_at is null initially", () => {
    const id = insertComplaint(db, baseParams);
    const row = getComplaintById(db, id);
    expect(row?.notified_at).toBeNull();
  });

  it("markNotified sets notified_at", () => {
    const id = insertComplaint(db, baseParams);
    markNotified(db, id);
    const row = getComplaintById(db, id);
    expect(row?.notified_at).not.toBeNull();
    expect(typeof row?.notified_at).toBe("number");
  });

  it("getPendingNotifications returns unnotified complaints", () => {
    insertComplaint(db, baseParams);
    insertComplaint(db, baseParams);
    const pending = getPendingNotifications(db);
    expect(pending).toHaveLength(2);
  });

  it("getPendingNotifications excludes notified rows", () => {
    const id = insertComplaint(db, baseParams);
    markNotified(db, id);
    const pending = getPendingNotifications(db);
    expect(pending).toHaveLength(0);
  });

  it("getPendingNotifications excludes non-complaints", () => {
    insertComplaint(db, {
      ...baseParams,
      result: { is_complaint: false, severity: "low", category: "other", summary: "" },
    });
    const pending = getPendingNotifications(db);
    expect(pending).toHaveLength(0);
  });

  it("countBySeverity returns correct count", () => {
    insertComplaint(db, baseParams);
    insertComplaint(db, baseParams);
    insertComplaint(db, {
      ...baseParams,
      result: { is_complaint: true, severity: "low", category: "service", summary: "Slow" },
    });
    expect(countBySeverity(db, "high")).toBe(2);
    expect(countBySeverity(db, "low")).toBe(1);
    expect(countBySeverity(db, "med")).toBe(0);
  });

  it("getComplaintById returns undefined for missing id", () => {
    const row = getComplaintById(db, 99999);
    expect(row).toBeUndefined();
  });

  it("inserts multiple complaints with auto-incrementing ids", () => {
    const id1 = insertComplaint(db, baseParams);
    const id2 = insertComplaint(db, baseParams);
    expect(id2).toBe(id1 + 1);
  });

  it("getComplaintsByChatId filters and orders DESC", () => {
    insertComplaint(db, { ...baseParams, chatId: "tg-a" });
    insertComplaint(db, { ...baseParams, chatId: "tg-b" });
    const id3 = insertComplaint(db, { ...baseParams, chatId: "tg-a" });
    const rows = getComplaintsByChatId(db, "tg-a", 10);
    expect(rows.map((r) => r.chat_id)).toEqual(["tg-a", "tg-a"]);
    expect(rows[0]?.id).toBe(id3);
  });

  it("getRecentComplaints returns only is_complaint=1 ordered DESC", () => {
    const a = insertComplaint(db, baseParams);
    const b = insertComplaint(db, baseParams);
    db.prepare("UPDATE complaints SET created_at = ? WHERE id = ?").run(1000, a);
    db.prepare("UPDATE complaints SET created_at = ? WHERE id = ?").run(2000, b);
    insertComplaint(db, {
      ...baseParams,
      result: { is_complaint: false, severity: "low", category: "other", summary: "" },
    });
    const rows = getRecentComplaints(db, 10);
    expect(rows).toHaveLength(2);
    expect(rows.map((r) => r.id)).toEqual([b, a]);
    expect(rows.every((r) => r.is_complaint === 1)).toBe(true);
  });

  it("getRecentComplaints respects limit", () => {
    insertComplaint(db, baseParams);
    insertComplaint(db, baseParams);
    insertComplaint(db, baseParams);
    const rows = getRecentComplaints(db, 2);
    expect(rows).toHaveLength(2);
  });

  it("summary_state table no longer exists", () => {
    const tables = db
      .prepare("SELECT name FROM sqlite_master WHERE type='table'")
      .all() as Array<{ name: string }>;
    expect(tables.find((t) => t.name === "summary_state")).toBeUndefined();
  });
});
