/**
 * Unit tests for SearchDb — in-memory FTS5 SQLite.
 * SearchDb accepts a custom dbPath, so we use ":memory:" directly.
 */
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
import { SearchDb } from "../search-db.ts";

describe("SearchDb", () => {
  let search: SearchDb;

  beforeEach(() => {
    search = new SearchDb(":memory:");
  });

  afterEach(() => {
    search.close();
  });

  // ── indexMessage ──────────────────────────────────────────────────────

  describe("indexMessage", () => {
    it("indexes a message searchable by content", () => {
      search.indexMessage("sess-1", "assistant", "implement the authentication module");
      const results = search.searchAll("authentication");
      expect(results.length).toBeGreaterThanOrEqual(1);
      expect(results[0].resultType).toBe("message");
      expect(results[0].id).toBe("sess-1");
    });
  });

  // ── indexTask ─────────────────────────────────────────────────────────

  describe("indexTask", () => {
    it("indexes a task searchable by title", () => {
      search.indexTask("task-1", "Fix login bug", "Users cannot login", "todo", "agent-1");
      const results = search.searchAll("login");
      expect(results.length).toBeGreaterThanOrEqual(1);
      expect(results[0].resultType).toBe("task");
      expect(results[0].title).toBe("Fix login bug");
    });

    it("indexes a task searchable by description", () => {
      search.indexTask("task-2", "Refactor", "Extract database connection pooling", "progress", "agent-1");
      const results = search.searchAll("pooling");
      expect(results.length).toBeGreaterThanOrEqual(1);
      expect(results[0].resultType).toBe("task");
    });
  });

  // ── indexBtmsg ────────────────────────────────────────────────────────

  describe("indexBtmsg", () => {
    it("indexes a btmsg searchable by content", () => {
      search.indexBtmsg("msg-1", "manager", "architect", "review the PR for auth changes", "general");
      const results = search.searchAll("auth");
      expect(results.length).toBeGreaterThanOrEqual(1);
      expect(results[0].resultType).toBe("btmsg");
    });
  });

  // ── searchAll ─────────────────────────────────────────────────────────

  describe("searchAll", () => {
    it("returns results across all tables ranked by relevance", () => {
      search.indexMessage("s1", "user", "deploy the kubernetes cluster");
      search.indexTask("t1", "Deploy staging", "kubernetes deployment", "todo", "a1");
      search.indexBtmsg("b1", "ops", "dev", "kubernetes rollout status", "ops-channel");

      const results = search.searchAll("kubernetes");
      expect(results).toHaveLength(3);
      // All three types should be present
      const types = results.map((r) => r.resultType).sort();
      expect(types).toEqual(["btmsg", "message", "task"]);
    });

    it("returns empty array for empty query", () => {
      search.indexMessage("s1", "user", "some content");
      expect(search.searchAll("")).toEqual([]);
      expect(search.searchAll("   ")).toEqual([]);
    });

    it("handles bad FTS5 query gracefully (no crash)", () => {
      search.indexMessage("s1", "user", "test content");
      // Unbalanced quotes are invalid FTS5 syntax — should not throw
      const results = search.searchAll('"unbalanced');
      // May return empty or partial results, but must not crash
      expect(Array.isArray(results)).toBe(true);
    });

    it("respects limit parameter", () => {
      for (let i = 0; i < 10; i++) {
        search.indexMessage(`s-${i}`, "user", `message about testing number ${i}`);
      }
      const results = search.searchAll("testing", 3);
      expect(results.length).toBeLessThanOrEqual(3);
    });
  });

  // ── rebuildIndex ──────────────────────────────────────────────────────

  describe("rebuildIndex", () => {
    it("clears all indexed data and recreates tables", () => {
      search.indexMessage("s1", "user", "important data");
      search.indexTask("t1", "Task", "desc", "todo", "a");
      search.indexBtmsg("b1", "from", "to", "msg", "ch");

      search.rebuildIndex();

      const results = search.searchAll("important");
      expect(results).toHaveLength(0);

      // Can still index after rebuild
      search.indexMessage("s2", "user", "new data");
      const after = search.searchAll("new");
      expect(after).toHaveLength(1);
    });
  });
});
