#!/usr/bin/env node
'use strict';

/**
 * gen-project-map.js — SigMap v0.4
 * Generates PROJECT_MAP.md with import graph, class hierarchy, and route table.
 *
 * Usage:
 *   node gen-project-map.js           Generate PROJECT_MAP.md
 *   node gen-project-map.js --version Print version and exit
 *   node gen-project-map.js --help    Print usage and exit
 *
 * Zero npm dependencies — runs on Node.js 18+ with nothing installed.
 */

const fs   = require('fs');
const path = require('path');

const VERSION     = '0.4.0';
const OUTPUT_FILE = 'PROJECT_MAP.md';

// ---------------------------------------------------------------------------
// CLI
// ---------------------------------------------------------------------------
const args = process.argv.slice(2);

function detectInvokedAs() {
  const argv1 = process.argv[1] || '';
  const base = path.basename(argv1);
  const baseNoExt = base.endsWith('.js') ? base.slice(0, -3) : base;
  if (argv1.includes('/_npx/') || argv1.includes('\\_npx\\') ||
      argv1.includes('/.npm/_') || argv1.includes('\\.npm\\_')) {
    return 'npx gen-project-map';
  }
  if (baseNoExt === 'gen-project-map') return 'gen-project-map';
  return 'node gen-project-map.js';
}

if (args.includes('--version')) {
  console.log(`gen-project-map.js v${VERSION}`);
  process.exit(0);
}

if (args.includes('--help')) {
  const cmd = detectInvokedAs();
  console.log([
    `SigMap project map generator v${VERSION}  (${cmd})`,
    '',
    'Usage:',
    `  ${cmd}           Generate PROJECT_MAP.md`,
    `  ${cmd} --version Print version and exit`,
    `  ${cmd} --help    Print this message`,
    '',
    'Configuration: gen-context.config.json (same file as gen-context.js)',
    '  srcDirs  — directories to scan  (default: ["src","app","lib",...])',
    '  exclude  — patterns to skip     (default: ["node_modules",".git",...])',
    '  maxDepth — recursion limit      (default: 6)',
    '',
    'Output: PROJECT_MAP.md (project root)',
  ].join('\n'));
  process.exit(0);
}

// ---------------------------------------------------------------------------
// Config — reuse src/config/loader.js
// ---------------------------------------------------------------------------
const { loadConfig } = require('./src/config/loader');

// ---------------------------------------------------------------------------
// Compact inline file walker
// ---------------------------------------------------------------------------
const DEFAULT_EXCLUDE = new Set([
  'node_modules', '.git', 'dist', 'build', 'out',
  '__pycache__', '.next', 'coverage', 'target', 'vendor', '.context',
]);

function walkDir(dir, excludeSet, maxDepth, depth, results) {
  if (depth > maxDepth) return;
  let entries;
  try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch (_) { return; }
  for (const entry of entries) {
    if (excludeSet.has(entry.name)) continue;
    const full = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      walkDir(full, excludeSet, maxDepth, depth + 1, results);
    } else if (entry.isFile()) {
      results.push(full);
    }
  }
}

function buildFileList(cwd, srcDirs, exclude, maxDepth) {
  const excludeSet = new Set([...DEFAULT_EXCLUDE, ...exclude]);
  const files = [];
  for (const dir of srcDirs) {
    const abs = path.isAbsolute(dir) ? dir : path.join(cwd, dir);
    if (!fs.existsSync(abs)) continue;
    walkDir(abs, excludeSet, maxDepth, 0, files);
  }
  // Also check for files directly in root (routes, config files, etc.)
  // but only when srcDirs are explicit; skip to avoid noise
  return files;
}

// ---------------------------------------------------------------------------
// Analyzers (lazy-required so errors don't abort the run)
// ---------------------------------------------------------------------------
function runAnalyzer(name, files, cwd) {
  try {
    const mod = require(`./src/map/${name}`);
    return mod.analyze(files, cwd) || '';
  } catch (err) {
    console.warn(`[sigmap] ${name} analyzer failed: ${err.message}`);
    return '';
  }
}

// ---------------------------------------------------------------------------
// Format PROJECT_MAP.md
// ---------------------------------------------------------------------------
function formatOutput(sections) {
  const now = new Date().toISOString().slice(0, 10);
  const lines = [
    '# Project Map',
    `<!-- Generated by gen-project-map.js v${VERSION} on ${now} -->`,
    '',
  ];

  const parts = [
    { key: 'imports',   header: '### Import graph',      content: sections.imports },
    { key: 'classes',   header: '### Class hierarchy',   content: sections.classes },
    { key: 'routes',    header: '### Route table',       content: sections.routes  },
  ];

  for (const { header, content } of parts) {
    lines.push(header);
    lines.push('');
    if (content) {
      lines.push(content);
    } else {
      lines.push('_No entries found._');
    }
    lines.push('');
  }

  return lines.join('\n');
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
function main() {
  const cwd = process.cwd();
  const config = loadConfig(cwd);
  const { srcDirs, exclude, maxDepth } = config;

  console.log(`[sigmap] scanning ${srcDirs.join(', ')} (maxDepth=${maxDepth}) …`);

  const files = buildFileList(cwd, srcDirs, exclude, maxDepth);

  if (files.length === 0) {
    console.warn(`[sigmap] no source files found — check srcDirs in gen-context.config.json`);
  } else {
    console.log(`[sigmap] found ${files.length} source files`);
  }

  const sections = {
    imports: runAnalyzer('import-graph',     files, cwd),
    classes: runAnalyzer('class-hierarchy',  files, cwd),
    routes:  runAnalyzer('route-table',      files, cwd),
  };

  const output = formatOutput(sections);
  const outPath = path.join(cwd, OUTPUT_FILE);

  try {
    fs.writeFileSync(outPath, output, 'utf8');
    console.log(`[sigmap] wrote ${OUTPUT_FILE} (${output.length} bytes)`);
  } catch (err) {
    console.error(`[sigmap] failed to write ${OUTPUT_FILE}: ${err.message}`);
    process.exit(1);
  }
}

main();
