/**
 * `forge clean` — wipe Forge's on-disk caches.
 *
 * Today: just `forge clean cache` which removes auto-cloned gitlab
 * repos under `<dataDir>/cloned-projects/`. Pipelines that need them
 * re-clone on the next dispatch (one-shot slowdown, no broken state).
 *
 * Add more cache targets here as they appear — keep one obvious entry
 * point so users don't have to learn N subcommands.
 */

import { existsSync, readdirSync, rmSync, statSync } from 'node:fs';
import { join } from 'node:path';
import { homedir } from 'node:os';

function dataDir(): string {
  return process.env.FORGE_DATA_DIR || join(homedir(), '.forge', 'data');
}

function dirSizeBytes(p: string): number {
  let total = 0;
  try {
    for (const ent of readdirSync(p, { withFileTypes: true })) {
      const child = join(p, ent.name);
      try {
        if (ent.isDirectory()) total += dirSizeBytes(child);
        else total += statSync(child).size;
      } catch { /* skip unreadable */ }
    }
  } catch { /* skip unreadable */ }
  return total;
}

function humanBytes(n: number): string {
  if (n < 1024) return `${n} B`;
  if (n < 1024 ** 2) return `${(n / 1024).toFixed(1)} KB`;
  if (n < 1024 ** 3) return `${(n / 1024 ** 2).toFixed(1)} MB`;
  return `${(n / 1024 ** 3).toFixed(1)} GB`;
}

function printHelp(): void {
  console.log(`
forge clean — wipe Forge caches.

Subcommands:
  forge clean cache               List cached items + sizes (dry-run).
  forge clean cache --yes         Actually delete everything.
  forge clean cache <name> --yes  Delete one entry (e.g. fortinet-fortinac-dev).

Targets cleaned:
  • <dataDir>/cloned-projects/    Auto-cloned gitlab repos (gitlab fallback).
`);
}

async function cmdCache(args: string[]): Promise<void> {
  const root = join(dataDir(), 'cloned-projects');
  const yes = args.includes('--yes') || args.includes('-y');
  const target = args.find((a) => !a.startsWith('-'));

  if (!existsSync(root)) {
    console.log(`Nothing to clean. ${root} does not exist.`);
    return;
  }

  const entries: { name: string; path: string; size: number }[] = [];
  for (const ent of readdirSync(root, { withFileTypes: true })) {
    if (!ent.isDirectory()) continue;
    const p = join(root, ent.name);
    entries.push({ name: ent.name, path: p, size: dirSizeBytes(p) });
  }
  entries.sort((a, b) => b.size - a.size);

  if (entries.length === 0) {
    console.log(`Nothing to clean. ${root} is empty.`);
    return;
  }

  const totalBytes = entries.reduce((s, e) => s + e.size, 0);

  // Single-target mode
  if (target) {
    const hit = entries.find((e) => e.name === target);
    if (!hit) {
      console.error(`Not found: ${target}`);
      console.error(`Available: ${entries.map(e => e.name).join(', ')}`);
      process.exit(1);
    }
    console.log(`${hit.name}  ${humanBytes(hit.size)}  ${hit.path}`);
    if (!yes) {
      console.log('\nDry-run. Add --yes to actually delete.');
      return;
    }
    try {
      rmSync(hit.path, { recursive: true, force: true });
      console.log(`✓ Deleted ${hit.name}`);
    } catch (e) {
      console.error(`Failed: ${(e as Error).message}`);
      process.exit(1);
    }
    return;
  }

  // List / wipe-all mode
  console.log(`Cached repos under ${root}:\n`);
  for (const e of entries) {
    console.log(`  ${e.name.padEnd(40)} ${humanBytes(e.size).padStart(10)}`);
  }
  console.log(`\nTotal: ${entries.length} entries, ${humanBytes(totalBytes)}`);

  if (!yes) {
    console.log('\nDry-run. Add --yes to delete everything.');
    return;
  }

  let deleted = 0;
  for (const e of entries) {
    try {
      rmSync(e.path, { recursive: true, force: true });
      deleted++;
    } catch (err) {
      console.error(`Failed ${e.name}: ${(err as Error).message}`);
    }
  }
  console.log(`\n✓ Deleted ${deleted} entries (${humanBytes(totalBytes)} freed).`);
}

export async function cleanCommand(args: string[]): Promise<void> {
  const sub = args[0] || '';
  switch (sub) {
    case 'cache':
      await cmdCache(args.slice(1));
      break;
    case '--help':
    case '-h':
    case '':
    default:
      printHelp();
      break;
  }
}
