/**
 * wip-repos claude ... generate cross-repo CLAUDE.md ecosystem sections
 *
 * Reads all repos on disk via the manifest, extracts metadata from each,
 * and writes an ## Ecosystem section into each repo's CLAUDE.md.
 *
 * Usage:
 *   wip-repos claude              # regenerate all repos
 *   wip-repos claude <repo>       # regenerate one repo
 *   wip-repos claude --init       # create CLAUDE.md for repos missing one
 *   wip-repos claude --dry-run    # preview changes
 */

import { existsSync, readFileSync, writeFileSync, readdirSync, mkdirSync } from 'node:fs';
import { join, basename, resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const TEMPLATE_PATH = join(__dirname, '..', '..', 'templates', 'repo-claude-md.template');
const START_MARKER = '<!-- wip-repos:start -->';
const END_MARKER = '<!-- wip-repos:end -->';

function readJSON(path) {
  try { return JSON.parse(readFileSync(path, 'utf8')); } catch { return null; }
}

/**
 * Extract metadata from a single repo on disk.
 */
function extractRepoMeta(repoPath) {
  const pkg = readJSON(join(repoPath, 'package.json'));
  const name = pkg?.name || basename(repoPath);
  const description = pkg?.description || '';
  const version = pkg?.version || '';
  const exports = pkg?.exports ? Object.keys(pkg.exports) : [];
  const binCommands = pkg?.bin ? Object.keys(pkg.bin) : [];
  const scripts = pkg?.scripts || {};

  // Detect interfaces
  const interfaces = [];
  if (binCommands.length > 0) interfaces.push('CLI');
  if (pkg?.main || pkg?.exports) interfaces.push('Module');
  if (existsSync(join(repoPath, 'mcp-server.mjs')) || existsSync(join(repoPath, 'dist', 'mcp-server.js'))) interfaces.push('MCP');
  if (existsSync(join(repoPath, 'openclaw.plugin.json'))) interfaces.push('OpenClaw Plugin');
  if (existsSync(join(repoPath, 'SKILL.md'))) interfaces.push('Skill');

  // Detect key dirs
  const dirs = [];
  for (const d of ['src', 'lib', 'tools', 'bin', 'dist', 'test', 'scripts', 'ai']) {
    if (existsSync(join(repoPath, d))) dirs.push(d + '/');
  }

  return {
    name,
    description,
    version,
    interfaces,
    binCommands,
    dirs,
    scripts,
    path: repoPath,
  };
}

/**
 * Determine which repos are relevant to a given repo.
 * Returns only repos in the same category or that share dependencies.
 */
function filterRelevant(targetMeta, allMetas, manifest) {
  // Find target's category from manifest
  const targetBase = basename(targetMeta.path);
  let targetCategory = null;

  for (const [category, repos] of Object.entries(manifest.repos || {})) {
    if (repos.some(r => r.local?.includes(targetBase) || r.name === targetBase)) {
      targetCategory = category;
      break;
    }
  }

  // Core repos every repo should know about
  const coreNames = ['wip-ldm-os', 'wip-ai-devops-toolbox', 'memory-crystal'];

  return allMetas.filter(m => {
    if (m.path === targetMeta.path) return false; // skip self
    const mBase = basename(m.path);
    // Same category
    if (targetCategory) {
      for (const repos of Object.values(manifest.repos || {})) {
        if (repos.some(r => r.local?.includes(mBase))) return true;
      }
    }
    // Core repo
    if (coreNames.some(c => mBase.includes(c))) return true;
    return false;
  }).slice(0, 15); // cap at 15 to keep CLAUDE.md focused
}

/**
 * Generate the ecosystem section for a repo.
 */
function generateEcosystem(targetMeta, relevantMetas) {
  const lines = [];
  for (const m of relevantMetas) {
    const relPath = basename(m.path);
    lines.push(`### ${m.name}`);
    lines.push(`**Path:** \`${relPath}\``);
    if (m.description) lines.push(m.description);
    if (m.interfaces.length > 0) lines.push(`**Interfaces:** ${m.interfaces.join(', ')}`);
    if (m.binCommands.length > 0) lines.push(`**CLI:** ${m.binCommands.join(', ')}`);
    if (m.version) lines.push(`**Version:** ${m.version}`);
    lines.push('');
  }
  return lines.join('\n').trim();
}

/**
 * Update the ecosystem section in a CLAUDE.md file.
 * Only replaces content between the delimiter comments.
 */
function updateEcosystemSection(claudeMdPath, ecosystemContent) {
  const content = readFileSync(claudeMdPath, 'utf8');
  const startIdx = content.indexOf(START_MARKER);
  const endIdx = content.indexOf(END_MARKER);

  if (startIdx === -1 || endIdx === -1) {
    // No delimiters found. Append section.
    const section = `\n## Ecosystem (auto-generated by wip-repos claude)\n${START_MARKER}\n\n${ecosystemContent}\n\n${END_MARKER}\n`;
    return content + section;
  }

  // Replace between delimiters
  const before = content.substring(0, startIdx + START_MARKER.length);
  const after = content.substring(endIdx);
  return `${before}\n\n${ecosystemContent}\n\n${after}`;
}

/**
 * Create a starter CLAUDE.md from the template.
 */
function createFromTemplate(repoPath, meta) {
  const template = existsSync(TEMPLATE_PATH)
    ? readFileSync(TEMPLATE_PATH, 'utf8')
    : '# CLAUDE.md\n\n## Ecosystem\n<!-- wip-repos:start -->\n<!-- wip-repos:end -->\n';

  let content = template
    .replace(/\{\{repo-name\}\}/g, meta.name)
    .replace(/\{\{description\}\}/g, meta.description || 'No description')
    .replace(/\{\{language\}\}/g, meta.scripts?.build?.includes('tsc') ? 'TypeScript' : 'JavaScript')
    .replace(/\{\{scripts\.test\}\}/g, meta.scripts?.test || 'npm test')
    .replace(/\{\{scripts\.build\}\}/g, meta.scripts?.build || 'npm run build')
    .replace(/\{\{scripts\.lint\}\}/g, meta.scripts?.lint || 'npm run lint')
    .replace(/\{\{detected-dirs\}\}/g, meta.dirs.length > 0 ? meta.dirs.map(d => `- \`${d}\``).join('\n') : '(no standard dirs detected)')
    .replace(/\{\{guardrails\}\}/g, existsSync(join(repoPath, '.license-guard.json')) ? '- Dual-license (MIT + AGPL). See LICENSE.' : '');

  return content;
}

/**
 * Main entry point.
 */
export function runClaude(manifestPath, args = []) {
  const dryRun = args.includes('--dry-run');
  const init = args.includes('--init');
  const targetRepo = args.find(a => !a.startsWith('--'));

  const manifest = readJSON(manifestPath);
  if (!manifest) {
    console.error('  Could not read manifest at ' + manifestPath);
    process.exit(1);
  }

  // Find all repo paths from manifest
  const manifestDir = dirname(manifestPath);
  const repoPaths = [];
  for (const [category, repos] of Object.entries(manifest.repos || {})) {
    for (const repo of repos) {
      const localPath = repo.local ? resolve(manifestDir, repo.local) : null;
      if (localPath && existsSync(localPath)) {
        repoPaths.push(localPath);
      }
    }
  }

  if (repoPaths.length === 0) {
    console.log('  No repos found in manifest.');
    return;
  }

  // Extract metadata from all repos
  const allMetas = repoPaths.map(p => extractRepoMeta(p));

  // Filter to target if specified
  const targets = targetRepo
    ? allMetas.filter(m => basename(m.path).includes(targetRepo))
    : allMetas;

  if (targets.length === 0) {
    console.error(`  No repo matching "${targetRepo}" found.`);
    process.exit(1);
  }

  console.log(`  Processing ${targets.length} repo(s)...`);
  let created = 0, updated = 0, skipped = 0;

  for (const meta of targets) {
    const claudeMdPath = join(meta.path, 'CLAUDE.md');
    const repoName = basename(meta.path);

    if (!existsSync(claudeMdPath)) {
      if (init) {
        const content = createFromTemplate(meta.path, meta);
        if (dryRun) {
          console.log(`  [create] ${repoName}/CLAUDE.md`);
        } else {
          writeFileSync(claudeMdPath, content);
          console.log(`  + Created ${repoName}/CLAUDE.md`);
        }
        created++;
      } else {
        skipped++;
      }
      continue;
    }

    // Generate ecosystem section
    const relevant = filterRelevant(meta, allMetas, manifest);
    const ecosystem = generateEcosystem(meta, relevant);
    const newContent = updateEcosystemSection(claudeMdPath, ecosystem);
    const oldContent = readFileSync(claudeMdPath, 'utf8');

    if (newContent === oldContent) {
      skipped++;
      continue;
    }

    if (dryRun) {
      console.log(`  [update] ${repoName}/CLAUDE.md (${relevant.length} related repos)`);
    } else {
      writeFileSync(claudeMdPath, newContent);
      console.log(`  + Updated ${repoName}/CLAUDE.md (${relevant.length} related repos)`);
    }
    updated++;
  }

  console.log('');
  console.log(`  ${dryRun ? 'Dry run' : 'Done'}. ${created} created, ${updated} updated, ${skipped} skipped.`);
}
