#!/usr/bin/env node

/**
 * token-hotspots CLI — emit ranked token hotspots and Opus 4.7 pattern findings
 * for a target repo path.
 *
 * Usage:
 *   node token-hotspots-cli.mjs [path] [--json] [--output-file <path>] [--global]
 *                               [--with-telemetry-recipe] [--accurate-tokens]
 *
 * Exit codes: 0=ok, 3=unrecoverable error.
 * Zero external dependencies.
 */

import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { writeFile, readFile, stat } from 'node:fs/promises';
import { discoverConfigFiles } from './lib/file-discovery.mjs';
import { resetCounter } from './lib/output.mjs';
import { scan } from './token-hotspots.mjs';
import * as tokenizerApi from './lib/tokenizer-api.mjs';
import { humanizeFindings } from './lib/humanizer.mjs';

const __dirname = dirname(fileURLToPath(import.meta.url));
const TELEMETRY_RECIPE_PATH = resolve(__dirname, '..', 'knowledge', 'cache-telemetry-recipe.md');

const ACCURATE_TOKENS_SAMPLE_SIZE = 3;

async function calibrateAgainstApi(hotspots, apiKey) {
  const sampled = hotspots.slice(0, ACCURATE_TOKENS_SAMPLE_SIZE);
  let actualTokens = 0;
  for (const hotspot of sampled) {
    if (!hotspot?.path) continue;
    let content;
    try {
      content = await readFile(hotspot.path, 'utf-8');
    } catch {
      continue;
    }
    const result = await tokenizerApi.callCountTokensApi(content, apiKey);
    actualTokens += result.input_tokens;
  }
  return {
    actual_tokens: actualTokens,
    source: 'count_tokens_api',
    sampled_hotspots: sampled.length,
  };
}

async function main() {
  const args = process.argv.slice(2);
  let targetPath = '.';
  let outputFile = null;
  let jsonMode = false;
  let rawMode = false;
  let includeGlobal = false;
  let withTelemetryRecipe = false;
  let accurateTokens = false;

  for (let i = 0; i < args.length; i++) {
    if (args[i] === '--json') jsonMode = true;
    else if (args[i] === '--raw') rawMode = true;
    else if (args[i] === '--global') includeGlobal = true;
    else if (args[i] === '--with-telemetry-recipe') withTelemetryRecipe = true;
    else if (args[i] === '--accurate-tokens') accurateTokens = true;
    else if (args[i] === '--output-file' && args[i + 1]) outputFile = args[++i];
    else if (!args[i].startsWith('-')) targetPath = args[i];
  }

  const absPath = resolve(targetPath);
  try {
    const s = await stat(absPath);
    if (!s.isDirectory()) {
      process.stderr.write(`Error: ${absPath} is not a directory\n`);
      process.exit(3);
    }
  } catch {
    process.stderr.write(`Error: path does not exist: ${absPath}\n`);
    process.exit(3);
  }

  resetCounter();
  const discovery = await discoverConfigFiles(absPath, { includeGlobal });
  const result = await scan(absPath, discovery);

  const payload = {
    scanner: result.scanner,
    status: result.status,
    files_scanned: result.files_scanned,
    duration_ms: result.duration_ms,
    total_estimated_tokens: result.total_estimated_tokens,
    hotspots: result.hotspots,
    findings: result.findings,
    counts: result.counts,
  };

  if (withTelemetryRecipe) {
    payload.telemetry_recipe_path = TELEMETRY_RECIPE_PATH;
  }

  if (accurateTokens) {
    const apiKey = process.env.ANTHROPIC_API_KEY;
    if (!apiKey || apiKey.length === 0) {
      process.stderr.write('ANTHROPIC_API_KEY not set — skipping API calibration\n');
      payload.calibration = { skipped: 'no-api-key' };
    } else {
      try {
        payload.calibration = await calibrateAgainstApi(result.hotspots || [], apiKey);
      } catch (err) {
        // Error message is already key-masked by tokenizer-api.mjs.
        process.stderr.write(`Calibration error: ${err.message}\n`);
        payload.calibration = { skipped: 'api-error', error: err.message };
      }
    }
  }

  // Default mode humanizes payload.findings (NOT result.findings).
  // --json and --raw bypass for v5.0.0 byte-equal output.
  if (!jsonMode && !rawMode) {
    payload.findings = humanizeFindings(payload.findings);
  }

  const json = JSON.stringify(payload, null, 2);

  if (outputFile) {
    await writeFile(outputFile, json, 'utf-8');
  }

  if (jsonMode || rawMode || !outputFile) {
    process.stdout.write(json + '\n');
  }
}

const isDirectRun = process.argv[1] && resolve(process.argv[1]) === resolve(new URL(import.meta.url).pathname);
if (isDirectRun) {
  main().catch(err => {
    process.stderr.write(`Fatal: ${err.message}\n`);
    process.exit(3);
  });
}
