#!/usr/bin/env tsx
/**
 * search — OSINT search CLI (PostgreSQL + pgvector backend).
 *
 * Usage:
 *   search "Palestinian phone" --type fts --limit 20
 *   search "who is behind the page" --type text
 *   search --entity 61562211934692
 *   search --sql "SELECT * FROM v_network_edges"
 *   search --stats
 */

import { Command } from "commander";
import { query, closePool } from "./db.js";
import { ftsSearch, semanticSearch, entityDump } from "./lib/search/index.js";
import type { SearchType } from "./lib/search/index.js";

// ─── Database stats ──────────────────────────────────────────────────────────

const STAT_TABLES = [
  "investigations",
  "entities",
  "content",
  "media",
  "http_log",
  "relationships",
  "identifiers",
  "annotations",
  "text_embeddings",
  "image_embeddings",
  "claims",
  "llm_runs",
  "timeline_events",
] as const;

async function printStats(): Promise<void> {
  console.log("\n=== DATABASE STATS ===");
  for (const table of STAT_TABLES) {
    const { rows } = await query<{ n: string }>(
      `SELECT COUNT(*) AS n FROM ${table}`,
    );
    const count = Number(rows[0].n);
    console.log(`  ${table.padEnd(25)}: ${count.toLocaleString()}`);
  }
}

// ─── Raw SQL ─────────────────────────────────────────────────────────────────

async function execSql(sql: string): Promise<void> {
  const { rows } = await query(sql);
  for (const row of rows) {
    console.log(JSON.stringify(row));
  }
}

// ─── CLI ─────────────────────────────────────────────────────────────────────

const program = new Command()
  .name("search")
  .description("OSINT search — PostgreSQL + pgvector")
  .argument("[query]", "Natural language query")
  .option("--type <type>", "Search type: text | image | fts", "fts")
  .option("--limit <n>", "Max results", "10")
  .option("--entity <id>", "Dump all data for an entity ID")
  .option("--sql <query>", "Run raw SQL against cultguard")
  .option("--stats", "Show database table counts");

program.action(async (searchQuery: string | undefined, opts: {
  type: string;
  limit: string;
  entity?: string;
  sql?: string;
  stats?: boolean;
}) => {
  try {
    // ── Stats mode ────────────────────────────────────────────
    if (opts.stats) {
      await printStats();
      return;
    }

    // ── Entity dump mode ──────────────────────────────────────
    if (opts.entity) {
      await entityDump(opts.entity);
      return;
    }

    // ── Raw SQL mode ──────────────────────────────────────────
    if (opts.sql) {
      await execSql(opts.sql);
      return;
    }

    // ── Search modes ──────────────────────────────────────────
    if (!searchQuery) {
      program.help();
      return;
    }

    const limit = parseInt(opts.limit, 10) || 10;
    const searchType = opts.type as SearchType | "fts";

    if (searchType === "fts") {
      const results = await ftsSearch(searchQuery, limit);
      console.log(`Full-text results for: '${searchQuery}'\n`);
      results.forEach((r, i) => {
        const idx = String(i + 1).padStart(2);
        console.log(`#${idx}  [${r.source}:${r.ref_id}]  rank=${r.rank.toFixed(4)}`);
        console.log(`      ${r.text ?? ""}`);
      });
    } else {
      console.log(
        `Semantic search: '${searchQuery}'  (type=${searchType}, limit=${limit})`,
      );
      await semanticSearch(searchQuery, limit, searchType);
    }
  } catch (err) {
    console.error("Error:", err instanceof Error ? err.message : err);
    process.exit(1);
  } finally {
    await closePool();
  }
});

program.parseAsync(process.argv);
