'use strict';

// Recursive enable: walk a directory tree, find every git repository, and run
// the sidecar's `cielara-code enable` (index + hooks + initial build) in each,
// with a progress bar. Used by `cielara enable --recursive`.
//
// Walk rule (per spec): starting at the root, a directory that CONTAINS a `.git`
// entry IS a repo root — record it and stop descending into it. A directory
// without `.git` is recursed into. This indexes sibling/child repos but never
// descends past a repo boundary into nested submodule checkouts.

const fs = require('fs');
const path = require('path');
const { spawnSync } = require('child_process');

const { getBinary } = require('./cielara');

// Directories we never descend into — heavy / irrelevant to code graphs.
const SKIP_DIRS = new Set(['node_modules', 'dist', '.cache', '__pycache__', 'venv', '.venv']);

function isGitRepo(dir) {
  // `.git` is a directory for a normal clone, a file for a worktree/submodule.
  return fs.existsSync(path.join(dir, '.git'));
}

// findGitRepos: iterative DFS from `root`. Returns absolute repo-root paths.
// Stops descending at each repo boundary and at SKIP_DIRS / dot-directories.
function findGitRepos(root) {
  const start = path.resolve(root);
  const repos = [];
  const stack = [start];
  const seen = new Set();

  while (stack.length) {
    const dir = stack.pop();
    if (seen.has(dir)) continue;
    seen.add(dir);

    if (isGitRepo(dir)) {
      repos.push(dir);
      continue; // repo boundary — do not descend further
    }

    let entries;
    try {
      entries = fs.readdirSync(dir, { withFileTypes: true });
    } catch {
      continue; // unreadable dir — skip
    }
    for (const entry of entries) {
      if (!entry.isDirectory()) continue;
      const name = entry.name;
      // Skip dot-dirs (except the root itself, already handled) and heavy dirs.
      if (name.startsWith('.') || SKIP_DIRS.has(name)) continue;
      stack.push(path.join(dir, name));
    }
  }

  repos.sort();
  return repos;
}

// renderProgress: single-line stderr bar, e.g. "[#####-----] 5/10 /path/to/repo".
// Redraws in place with a carriage return; caller prints a trailing newline.
function renderProgress(done, total, label, width = 20) {
  const ratio = total === 0 ? 1 : done / total;
  const filled = Math.round(ratio * width);
  const bar = '#'.repeat(filled) + '-'.repeat(Math.max(0, width - filled));
  const line = `\r[${bar}] ${done}/${total} ${label || ''}`;
  // Pad to clear any longer previous label, then trim to a sane width.
  process.stderr.write(line.slice(0, 200).padEnd(80));
}

// runRecursiveEnable: enable every repo under `root`. `accessToken` is forwarded
// to the sidecar as CIELARA_ACCESS_TOKEN (enable is login-gated). Extra sidecar
// args (e.g. scope overrides) may be appended via `extraArgs`.
function runRecursiveEnable(root, { accessToken, extraArgs = [], cliBin = 'cielara-code' } = {}) {
  const graphBin = getBinary('cielara-code');
  if (!graphBin) {
    process.stderr.write(
      'cielara: pre-compiled cielara-code binary not found for this platform.\n' +
      '  Reinstall: npm install -g cielara-enterprise@latest\n',
    );
    return { total: 0, succeeded: 0, failed: 0, repos: [] };
  }

  const repos = findGitRepos(root);
  if (repos.length === 0) {
    process.stderr.write(`cielara: no git repositories found under ${path.resolve(root)}\n`);
    return { total: 0, succeeded: 0, failed: 0, repos: [] };
  }

  process.stderr.write(`cielara: enabling ${repos.length} repositor${repos.length === 1 ? 'y' : 'ies'}…\n`);

  const env = { ...process.env };
  if (accessToken) env.CIELARA_ACCESS_TOKEN = accessToken;

  let succeeded = 0;
  const failures = [];
  for (let i = 0; i < repos.length; i += 1) {
    const repo = repos[i];
    renderProgress(i, repos.length, repo);
    const args = ['enable', '--cwd', repo, '--cli-bin', cliBin, ...extraArgs];
    // Inherit stderr so the sidecar's own progress/errors are visible; suppress
    // stdout so it doesn't fight the progress bar.
    const res = spawnSync(graphBin, args, { env, stdio: ['ignore', 'ignore', 'inherit'] });
    if (res.status === 0) {
      succeeded += 1;
    } else {
      failures.push({ repo, code: res.status, error: res.error?.message });
    }
    renderProgress(i + 1, repos.length, repo);
  }
  process.stderr.write('\n');

  if (failures.length) {
    process.stderr.write(`cielara: ${failures.length} repositor${failures.length === 1 ? 'y' : 'ies'} failed to enable:\n`);
    for (const f of failures) {
      process.stderr.write(`  ${f.repo} (exit ${f.code ?? 'error'}${f.error ? `: ${f.error}` : ''})\n`);
    }
  }
  process.stderr.write(`cielara: enabled ${succeeded}/${repos.length} repositories.\n`);

  return { total: repos.length, succeeded, failed: failures.length, repos };
}

module.exports = { findGitRepos, renderProgress, runRecursiveEnable };
