/**
 * @file Express router for settings-related endpoints, providing system info, database statistics, hook status, and operations to clear data, re-import sessions, reinstall hooks, reset pricing, export data, and perform cleanup of stale sessions. This allows the frontend to manage and maintain the agent monitoring system effectively.
 * @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
 */

const { Router } = require("express");
const fs = require("fs");
const path = require("path");
const os = require("os");
const { db, stmts, DB_PATH, DEFAULT_PRICING, applyIntroPricing } = require("../db");
const { getConnectionCount } = require("../websocket");
const { transcriptCache } = require("./hooks");

const router = Router();

const APP_VERSION = (() => {
  try {
    return require("../../package.json").version || "0.0.0";
  } catch {
    return "0.0.0";
  }
})();

const { getSettingsPath, getClaudeHome, setClaudeHome } = require("../lib/claude-home");
const CLAUDE_SETTINGS_PATH = getSettingsPath();

function getDbSize() {
  try {
    const stat = fs.statSync(DB_PATH);
    return stat.size;
  } catch {
    return 0;
  }
}

function getTableCounts() {
  const tables = ["sessions", "agents", "events", "model_pricing"];
  const counts = {};
  for (const t of tables) {
    counts[t] = db.prepare(`SELECT COUNT(*) as c FROM ${t}`).get().c;
  }
  counts.token_usage = db
    .prepare("SELECT COUNT(DISTINCT session_id) as c FROM token_usage")
    .get().c;
  return counts;
}

function getHookStatus() {
  try {
    if (!fs.existsSync(CLAUDE_SETTINGS_PATH)) {
      return { installed: false, path: CLAUDE_SETTINGS_PATH, hooks: {} };
    }
    const raw = fs.readFileSync(CLAUDE_SETTINGS_PATH, "utf8");
    const settings = JSON.parse(raw);
    const hookTypes = [
      "PreToolUse",
      "PostToolUse",
      "Stop",
      "SubagentStop",
      "Notification",
      "SessionStart",
      "SessionEnd",
    ];
    const hooks = {};
    for (const ht of hookTypes) {
      const entries = settings.hooks?.[ht] || [];
      hooks[ht] = entries.some(
        (e) =>
          (e.command && e.command.includes("hook-handler.js")) ||
          (Array.isArray(e.hooks) &&
            e.hooks.some((h) => h.command && h.command.includes("hook-handler.js")))
      );
    }
    const installed = Object.values(hooks).every(Boolean);
    return { installed, path: CLAUDE_SETTINGS_PATH, hooks };
  } catch {
    return { installed: false, path: CLAUDE_SETTINGS_PATH, hooks: {} };
  }
}

// GET /api/settings/info — system info, db stats, hook status
router.get("/info", (req, res) => {
  const dbSize = getDbSize();
  const counts = getTableCounts();
  const hookStatus = getHookStatus();

  // Advanced SQLite info
  const pragmas = {
    journal_mode: db.pragma("journal_mode", { simple: true }),
    synchronous: db.pragma("synchronous", { simple: true }),
    auto_vacuum: db.pragma("auto_vacuum", { simple: true }),
    encoding: db.pragma("encoding", { simple: true }),
    foreign_keys: db.pragma("foreign_keys", { simple: true }),
    busy_timeout: db.pragma("busy_timeout", { simple: true }),
  };

  // Recent activity load (events in last 5, 15, 60 minutes)
  const getCount = (ms) => {
    const d = new Date(Date.now() - ms).toISOString();
    return db.prepare("SELECT COUNT(*) as c FROM events WHERE created_at > ?").get(d).c;
  };

  const load_stats = {
    m5: getCount(5 * 60 * 1000),
    m15: getCount(15 * 60 * 1000),
    h1: getCount(60 * 60 * 1000),
  };

  res.json({
    db: {
      path: DB_PATH,
      size: dbSize,
      counts,
      pragmas,
      load_stats,
    },
    hooks: hookStatus,
    server: {
      version: APP_VERSION,
      uptime: process.uptime(),
      node_version: process.version,
      platform: process.platform,
      ws_connections: getConnectionCount(),
      memory: process.memoryUsage(),
      cpu_load: os.loadavg(),
      arch: os.arch(),
      total_mem: os.totalmem(),
      free_mem: os.freemem(),
      cpus: os.cpus().length,
    },
    transcript_cache: transcriptCache.stats(),
  });
});

// POST /api/settings/clear-data — delete all sessions, agents, events, tokens
router.post("/clear-data", (_req, res) => {
  const counts = getTableCounts();
  db.pragma("foreign_keys = OFF");
  db.prepare("DELETE FROM token_usage").run();
  db.prepare("DELETE FROM events").run();
  db.prepare("DELETE FROM agents").run();
  db.prepare("DELETE FROM sessions").run();
  // Fired alerts reference the cleared sessions — wipe the feed too. Alert
  // *rules* survive: they're user configuration, like model_pricing.
  db.prepare("DELETE FROM alert_events").run();
  // Webhook delivery log is an audit trail of those fired alerts — wipe it too.
  // Webhook *targets* survive, like alert rules and pricing.
  db.prepare("DELETE FROM webhook_deliveries").run();
  db.pragma("foreign_keys = ON");
  res.json({ ok: true, cleared: counts });
});

// POST /api/settings/reimport — re-import legacy sessions from ~/.claude/
router.post("/reimport", async (_req, res) => {
  try {
    const { importAllSessions } = require("../../scripts/import-history");
    const dbModule = require("../db");
    const result = await importAllSessions(dbModule);
    res.json({ ok: true, ...result });
  } catch (err) {
    res.status(500).json({
      error: { code: "IMPORT_FAILED", message: err.message },
    });
  }
});

// POST /api/settings/reinstall-hooks — reinstall Claude Code hooks
router.post("/reinstall-hooks", (_req, res) => {
  try {
    const { installHooks } = require("../../scripts/install-hooks");
    const success = installHooks(true);
    const hookStatus = getHookStatus();
    res.json({ ok: success, hooks: hookStatus });
  } catch (err) {
    res.status(500).json({
      error: { code: "HOOK_INSTALL_FAILED", message: err.message },
    });
  }
});

// POST /api/settings/reset-pricing — reset pricing to defaults
router.post("/reset-pricing", (_req, res) => {
  db.prepare("DELETE FROM model_pricing").run();

  const seedPricing = db.prepare(
    "INSERT OR IGNORE INTO model_pricing (model_pattern, display_name, input_per_mtok, output_per_mtok, cache_read_per_mtok, cache_write_per_mtok, cache_write_1h_per_mtok, fast_input_per_mtok, fast_output_per_mtok) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
  );
  for (const [pattern, name, inp, out, cr, cw, cw1h, fin, fout] of DEFAULT_PRICING) {
    seedPricing.run(pattern, name, inp, out, cr, cw, cw1h, fin, fout);
  }
  // Re-apply time-limited intro rates (e.g. Sonnet 5) — the seed above only
  // carries standard rates, so without this a reset silently drops the promo.
  applyIntroPricing(db);

  const pricing = stmts.listPricing.all();
  res.json({ ok: true, pricing });
});

// GET /api/settings/export — export all data as JSON
router.get("/export", (_req, res) => {
  const sessions = db.prepare("SELECT * FROM sessions ORDER BY started_at DESC").all();
  const agents = db.prepare("SELECT * FROM agents ORDER BY started_at DESC").all();
  const events = db.prepare("SELECT * FROM events ORDER BY created_at DESC").all();
  const tokenUsage = db.prepare("SELECT * FROM token_usage").all();
  const pricing = stmts.listPricing.all();

  res.setHeader("Content-Type", "application/json");
  res.setHeader(
    "Content-Disposition",
    `attachment; filename="agent-monitor-export-${new Date().toISOString().slice(0, 10)}.json"`
  );
  res.json({
    exported_at: new Date().toISOString(),
    sessions,
    agents,
    events,
    token_usage: tokenUsage,
    model_pricing: pricing,
  });
});

// GET /api/settings/claude-home — get current CLAUDE_HOME path
router.get("/claude-home", (_req, res) => {
  res.json({ claude_home: getClaudeHome() });
});

// PUT /api/settings/claude-home — update CLAUDE_HOME path
router.put("/claude-home", (req, res) => {
  const { path: newPath } = req.body;
  if (!newPath || typeof newPath !== "string") {
    return res.status(400).json({
      error: { code: "INVALID_PATH", message: "path is required and must be a string" },
    });
  }
  try {
    const resolved = setClaudeHome(newPath);
    res.json({ ok: true, claude_home: resolved });
  } catch (err) {
    res.status(400).json({
      error: { code: "INVALID_PATH", message: err.message },
    });
  }
});

// POST /api/settings/cleanup — abandon stale sessions, purge old data
router.post("/cleanup", (req, res) => {
  const { abandon_hours, purge_days } = req.body;
  const result = { abandoned: 0, purged_sessions: 0, purged_events: 0, purged_agents: 0 };

  if (abandon_hours && typeof abandon_hours === "number" && abandon_hours > 0) {
    // Mark active sessions with no recent events as abandoned
    const cutoff = new Date(Date.now() - abandon_hours * 3600 * 1000).toISOString();
    const stale = db
      .prepare(
        `SELECT s.id FROM sessions s
         WHERE s.status = 'active'
           AND s.started_at < ?
           AND NOT EXISTS (
             SELECT 1 FROM events e WHERE e.session_id = s.id AND e.created_at > ?
           )`
      )
      .all(cutoff, cutoff);

    for (const row of stale) {
      db.prepare(
        "UPDATE sessions SET status = 'abandoned', ended_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE id = ?"
      ).run(row.id);
      // Also complete any lingering agents
      db.prepare(
        "UPDATE agents SET status = 'completed', ended_at = strftime('%Y-%m-%dT%H:%M:%fZ', 'now') WHERE session_id = ? AND status IN ('waiting','working')"
      ).run(row.id);
    }
    result.abandoned = stale.length;
  }

  if (purge_days && typeof purge_days === "number" && purge_days > 0) {
    const cutoff = new Date(Date.now() - purge_days * 86400 * 1000).toISOString();
    // Only purge completed/error/abandoned sessions, never active
    const toDelete = db
      .prepare(
        "SELECT id FROM sessions WHERE status IN ('completed','error','abandoned') AND started_at < ?"
      )
      .all(cutoff);

    if (toDelete.length > 0) {
      const ids = toDelete.map((r) => r.id);
      const placeholders = ids.map(() => "?").join(",");
      // Cascading deletes handle agents/events, but token_usage FK might not cascade on all setups
      result.purged_events = db
        .prepare(`DELETE FROM events WHERE session_id IN (${placeholders})`)
        .run(...ids).changes;
      result.purged_agents = db
        .prepare(`DELETE FROM agents WHERE session_id IN (${placeholders})`)
        .run(...ids).changes;
      db.prepare(`DELETE FROM token_usage WHERE session_id IN (${placeholders})`).run(...ids);
      db.prepare(`DELETE FROM sessions WHERE id IN (${placeholders})`).run(...ids);
      result.purged_sessions = toDelete.length;
    }
  }

  res.json({ ok: true, ...result });
});

module.exports = router;
