/**
 * cmd-cost.js — `qa-stlc cost`
 *
 * Read session cost logs from ~/.qa-stlc/cost-*.jsonl and print a report.
 * Also handles --set-model to persist the coding agent model preference.
 *
 * Usage:
 *   qa-stlc cost                          # last session
 *   qa-stlc cost --all                    # all sessions
 *   qa-stlc cost --session <id>           # specific session
 *   qa-stlc cost --json                   # raw JSON output
 *   qa-stlc cost --set-model <model-id>   # save model for Cursor/Windsurf users
 *
 * Model detection order (mirrors cost_tracker.py):
 *   STLC_CODING_AGENT_MODEL env var
 *   → ANTHROPIC_MODEL env var  (Claude Code sets this automatically)
 *   → GITHUB_COPILOT_MODEL env var
 *   → ~/.qa-stlc/agent-model  (saved by --set-model)
 *   → "claude-sonnet-4-6"  (default)
 */

"use strict";

const fs   = require("fs");
const path = require("path");
const os   = require("os");

const LOG_DIR   = path.join(os.homedir(), ".qa-stlc");
const PREF_FILE = path.join(LOG_DIR, "agent-model");

const C = {
  reset:  "\x1b[0m", bold:   "\x1b[1m",
  dim:    "\x1b[2m", cyan:   "\x1b[36m",
  green:  "\x1b[32m", yellow: "\x1b[33m",
};
const b   = (s) => `${C.bold}${s}${C.reset}`;
const dim = (s) => `${C.dim}${s}${C.reset}`;
const grn = (s) => `${C.green}${s}${C.reset}`;
const cyn = (s) => `${C.cyan}${s}${C.reset}`;

function fmtTok(n) { return n >= 1000 ? `${(n/1000).toFixed(1)}K` : String(n); }
function fmtUsd(n) { return `$${n.toFixed(6)}`; }
function fmtMs(n)  { return `${n}ms`; }

// ── Model preference helpers ───────────────────────────────────────────────

const KNOWN_MODELS = {
  // Anthropic
  "claude-sonnet-4-6":         "$3/$15 per MTok",
  "claude-opus-4-6":           "$5/$25 per MTok",
  "claude-opus-4-7":           "$5/$25 per MTok",
  "claude-haiku-4-5-20251001": "$1/$5 per MTok",
  "claude-sonnet-4-20250514":  "$3/$15 per MTok",
  // OpenAI / Copilot
  "gpt-4o":                    "$2.50/$10 per MTok",
  "gpt-4o-mini":               "$0.15/$0.60 per MTok",
};

function saveModelPref(modelId) {
  fs.mkdirSync(LOG_DIR, { recursive: true });
  fs.writeFileSync(PREF_FILE, modelId.trim(), "utf8");
}

function readModelPref() {
  try {
    if (fs.existsSync(PREF_FILE)) return fs.readFileSync(PREF_FILE, "utf8").trim();
  } catch (_) {}
  return null;
}

function printModelHelp() {
  console.log(`\n  ${b("Coding agent model for cost tracking")}\n`);
  console.log(`  ${dim("Model is detected automatically in this order:")}`);
  console.log(`  1. STLC_CODING_AGENT_MODEL  env var  ${dim("(explicit override, always wins)")}`);
  console.log(`  2. ANTHROPIC_MODEL          env var  ${dim("(set automatically by Claude Code ✓ zero config)")}`);
  console.log(`  3. GITHUB_COPILOT_MODEL     env var  ${dim("(set by Copilot if configured)")}`);
  console.log(`  4. ~/.qa-stlc/agent-model   file     ${dim("(saved by qa-stlc cost --set-model)")}`);
  console.log(`  5. claude-sonnet-4-6               ${dim("(default fallback)")}\n`);

  const saved = readModelPref();
  if (saved) {
    console.log(`  ${grn("✓")}  Currently saved: ${b(saved)}  ${dim("(~/.qa-stlc/agent-model)")}\n`);
  }

  console.log(`  ${b("Known models:")}`);
  for (const [id, rate] of Object.entries(KNOWN_MODELS)) {
    console.log(`    ${id.padEnd(36)} ${dim(rate)}`);
  }

  console.log(`\n  ${b("To set for Cursor / Windsurf / any agent:")}`);
  console.log(`  ${cyn("Option A — save once (recommended):")}`);
  console.log(`    qa-stlc cost --set-model claude-opus-4-6\n`);
  console.log(`  ${cyn("Option B — shell profile (applies to all projects):")}`);
  console.log(`    echo 'export STLC_CODING_AGENT_MODEL=claude-opus-4-6' >> ~/.zshrc\n`);
  console.log(`  ${cyn("Option C — .mcp.json env block (project-level):")}`);
  console.log(`    "qa-gherkin-generator": {`);
  console.log(`      "command": "/path/to/qa-gherkin-generator",`);
  console.log(`      "env": { "STLC_CODING_AGENT_MODEL": "claude-opus-4-6" }`);
  console.log(`    }\n`);
  console.log(`  ${cyn("Option D — .env file in project root:")}`);
  console.log(`    STLC_CODING_AGENT_MODEL=claude-opus-4-6\n`);
  console.log(`  ${dim("Claude Code users: no action needed.")}`);
  console.log(`  ${dim("ANTHROPIC_MODEL is passed through automatically via qa-stlc mcp-config.")}\n`);
}

// ── Log reading ────────────────────────────────────────────────────────────

function readLogs() {
  if (!fs.existsSync(LOG_DIR)) return [];
  return fs.readdirSync(LOG_DIR)
    .filter((f) => f.startsWith("cost-") && f.endsWith(".jsonl"))
    .sort()
    .map((f) => {
      const file = path.join(LOG_DIR, f);
      const records = fs.readFileSync(file, "utf8")
        .split("\n").filter(Boolean).map((l) => JSON.parse(l));
      return {
        file,
        sessionId: f.replace(/^cost-/, "").replace(/\.jsonl$/, ""),
        records,
      };
    });
}

// ── Printing ───────────────────────────────────────────────────────────────

function fmtTokens(r) {
  const n      = r.estimated_tokens || 0;
  const exact  = r.token_method === "exact";
  const prefix = exact ? "" : "~";
  return prefix + fmtTok(n);
}

function printSession(sess) {
  const { sessionId, records } = sess;
  if (!records.length) return;

  const byServer = {};
  let totalCost = 0, totalExactTokens = 0, totalEstTokens = 0;
  let hasExact = false, hasEst = false;

  for (const r of records) {
    const k = r.server || "unknown";
    if (!byServer[k]) byServer[k] = { calls: 0, tokens: 0, cost: 0, exact: false };
    byServer[k].calls++;
    byServer[k].tokens += r.estimated_tokens || 0;
    byServer[k].cost   += r.cost_usd || 0;
    if (r.token_method === "exact") { byServer[k].exact = true; hasExact = true; }
    else                             { hasEst = true; }
    totalCost += r.cost_usd || 0;
    if (r.token_method === "exact") totalExactTokens += r.estimated_tokens || 0;
    else                             totalEstTokens  += r.estimated_tokens || 0;
  }

  const methodLabel = hasExact && hasEst ? "mixed (exact + estimated)"
                    : hasExact           ? "exact (from API response)"
                    :                      "estimated (payload chars÷4)";

  const models  = [...new Set(records.map((r) => r.model).filter(Boolean))].join(", ");
  const sources = [...new Set(records.map((r) => r.model_source).filter(Boolean))].join(", ");
  const ts    = records[0]?.timestamp?.slice(0, 19).replace("T", " ") || "";
  const tsEnd = records[records.length - 1]?.timestamp?.slice(0, 19).replace("T", " ") || "";

  console.log(`\n${"═".repeat(72)}`);
  console.log(b(`  stlc-agents · Cost Report  ·  ${sessionId}`));
  console.log(`${"═".repeat(72)}`);
  console.log(dim(`  ${ts} → ${tsEnd}`));
  console.log(dim(`  Model(s): ${models}  (via: ${sources})`));
  console.log(dim(`  Token method: ${methodLabel}`));

  // Per-server
  console.log(`\n  ${"Server".padEnd(30)} ${"Calls".padStart(6)} ${"Tokens".padStart(10)} ${"Cost (USD)".padStart(14)}  Method`);
  console.log(`  ${"─".repeat(70)}`);
  for (const [svr, d] of Object.entries(byServer)) {
    const meth = d.exact ? "" : dim(" [est]");
    const prefix = d.exact ? "" : "~";
    console.log(
      `  ${cyn(svr.padEnd(30))} ${String(d.calls).padStart(6)} ` +
      `${(prefix + fmtTok(d.tokens)).padStart(10)} ${grn(fmtUsd(d.cost).padStart(14))}${meth}`
    );
  }

  // Per-step
  console.log(`\n  ${"Step detail"}`);
  console.log(`  ${"─".repeat(72)}`);
  for (const r of records) {
    const exact   = r.token_method === "exact";
    const methTag = exact ? "" : dim(" [est]");
    const cacheNote = (r.cache_write_tokens || r.cache_read_tokens)
      ? dim(` cw=${r.cache_write_tokens||0} cr=${r.cache_read_tokens||0}`) : "";
    const iters = r.iterations > 1 ? dim(` ×${r.iterations}`) : "";
    console.log(
      `  ${(r.server || "?").padEnd(26)} ${(r.tool || "?").padEnd(34)} ` +
      `${fmtTokens(r).padStart(8)}  ` +
      `${grn(fmtUsd(r.cost_usd || 0))}  ${fmtMs(r.latency_ms || 0)}${methTag}${cacheNote}${iters}`
    );
  }

  // Totals
  const totalTokens = totalExactTokens + totalEstTokens;
  const tokenNote   = hasExact && hasEst
    ? ` (${fmtTok(totalExactTokens)} exact + ~${fmtTok(totalEstTokens)} est)`
    : hasExact ? " (exact)" : " (estimated)";
  console.log(`\n  ${"─".repeat(72)}`);
  console.log(`  ${"Total tokens".padEnd(40)} ${fmtTok(totalTokens).padStart(10)}${dim(tokenNote)}`);
  console.log(`  ${b("Total cost".padEnd(40))} ${grn(fmtUsd(totalCost))}`);
  console.log(dim(`  Log: ${sess.file}`));
  console.log(`${"═".repeat(72)}\n`);
}

// ── Main ───────────────────────────────────────────────────────────────────

module.exports = async function cost(opts) {

  // --set-model: save model preference for Cursor/Windsurf users
  if (opts.setModel) {
    const modelId = opts.setModel.trim();
    saveModelPref(modelId);
    const rate = KNOWN_MODELS[modelId];
    console.log(`\n  ${grn("✓")}  Model saved: ${b(modelId)}`);
    if (rate) console.log(`     Pricing: ${dim(rate)}`);
    else      console.log(`     ${dim("(unknown model — pricing may show $0.000000, add to pricing.py)")}`);
    console.log(`     Written to: ${dim(PREF_FILE)}`);
    console.log(`\n  This preference is used by all stlc-agents servers in all projects.`);
    console.log(`  Override per-project via STLC_CODING_AGENT_MODEL in .env or .mcp.json.\n`);
    return;
  }

  // --model-help: explain how model detection works
  if (opts.modelHelp) {
    printModelHelp();
    return;
  }

  const sessions = readLogs();

  if (!sessions.length) {
    console.log(`\n  No cost logs found in ${LOG_DIR}`);
    console.log(`  Run any qa-stlc MCP tool call to start tracking.\n`);
    return;
  }

  if (opts.json) {
    const all = opts.all ? sessions : [sessions[sessions.length - 1]];
    console.log(JSON.stringify(all, null, 2));
    return;
  }

  if (opts.session) {
    const found = sessions.find((s) => s.sessionId === opts.session);
    if (!found) {
      console.log(`\n  Session not found: ${opts.session}`);
      console.log(`  Available: ${sessions.map((s) => s.sessionId).join(", ")}\n`);
      return;
    }
    printSession(found);
    return;
  }

  if (opts.all) {
    for (const s of sessions) printSession(s);
    const allRecords      = sessions.flatMap((s) => s.records);
    const grandTotal      = allRecords.reduce((a, r) => a + (r.cost_usd || 0), 0);
    const grandExact      = allRecords.filter((r) => r.token_method === "exact")
                                      .reduce((a, r) => a + (r.estimated_tokens || 0), 0);
    const grandEst        = allRecords.filter((r) => r.token_method !== "exact")
                                      .reduce((a, r) => a + (r.estimated_tokens || 0), 0);
    const grandTokens     = grandExact + grandEst;
    const tokenBreakdown  = grandExact && grandEst
      ? ` (${fmtTok(grandExact)} exact + ~${fmtTok(grandEst)} est)`
      : grandExact ? " (exact)" : " (estimated)";
    console.log(`${"═".repeat(72)}`);
    console.log(b(`  All sessions — grand total`));
    console.log(`${"═".repeat(72)}`);
    console.log(`  Sessions    : ${sessions.length}`);
    console.log(`  Total tokens: ${fmtTok(grandTokens)}${dim(tokenBreakdown)}`);
    console.log(b(`  TOTAL COST  : ${grn(fmtUsd(grandTotal))}`));
    console.log(`${"═".repeat(72)}\n`);
    return;
  }

  // Default: last session
  printSession(sessions[sessions.length - 1]);
};