#!/usr/bin/env node
/**
 * ECC CodeBuddy Uninstaller (Cross-platform Node.js version)
 * Uninstalls Everything Claude Code workflows from a CodeBuddy project.
 *
 * Usage:
 *   node uninstall.js              # Uninstall from current directory
 *   node uninstall.js ~            # Uninstall globally from ~/.codebuddy/
 */

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

/**
 * Get home directory cross-platform
 */
function getHomeDir() {
  return process.env.USERPROFILE || process.env.HOME || os.homedir();
}

/**
 * Resolve a path to its canonical form
 */
function resolvePath(filePath) {
  try {
    return fs.realpathSync(filePath);
  } catch {
    // If realpath fails, return the path as-is
    return path.resolve(filePath);
  }
}

/**
 * Check if a manifest entry is valid (security check)
 */
function isValidManifestEntry(entry) {
  // Reject empty, absolute paths, parent directory references
  if (!entry || entry.length === 0) return false;
  if (entry.startsWith('/')) return false;
  if (entry.startsWith('~')) return false;
  if (entry.includes('/../') || entry.includes('/..')) return false;
  if (entry.startsWith('../') || entry.startsWith('..\\')) return false;
  if (entry === '..' || entry === '...' || entry.includes('\\..\\')||entry.includes('/..')) return false;

  return true;
}

/**
 * Read lines from manifest file
 */
function readManifest(manifestPath) {
  try {
    if (!fs.existsSync(manifestPath)) {
      return [];
    }
    const content = fs.readFileSync(manifestPath, 'utf8');
    return content.split('\n').filter(line => line.length > 0);
  } catch {
    return [];
  }
}

/**
 * Recursively find empty directories
 */
function findEmptyDirs(dirPath) {
  const emptyDirs = [];

  function walkDirs(currentPath) {
    try {
      const entries = fs.readdirSync(currentPath, { withFileTypes: true });
      const subdirs = entries.filter(e => e.isDirectory());

      for (const subdir of subdirs) {
        const subdirPath = path.join(currentPath, subdir.name);
        walkDirs(subdirPath);
      }

      // Check if directory is now empty
      try {
        const remaining = fs.readdirSync(currentPath);
        if (remaining.length === 0 && currentPath !== dirPath) {
          emptyDirs.push(currentPath);
        }
      } catch {
        // Directory might have been deleted
      }
    } catch {
      // Ignore errors
    }
  }

  walkDirs(dirPath);
  return emptyDirs.sort().reverse(); // Sort in reverse for removal
}

/**
 * Prompt user for confirmation
 */
async function promptConfirm(question) {
  return new Promise((resolve) => {
    const rl = readline.createInterface({
      input: process.stdin,
      output: process.stdout,
    });

    rl.question(question, (answer) => {
      rl.close();
      resolve(/^[yY]$/.test(answer));
    });
  });
}

/**
 * Main uninstall function
 */
async function doUninstall() {
  const codebuddyDirName = '.codebuddy';

  // Parse arguments
  let targetDir = process.cwd();
  if (process.argv.length > 2) {
    const arg = process.argv[2];
    if (arg === '~' || arg === getHomeDir()) {
      targetDir = getHomeDir();
    } else {
      targetDir = path.resolve(arg);
    }
  }

  // Determine codebuddy full path
  let codebuddyFullPath;
  const baseName = path.basename(targetDir);

  if (baseName === codebuddyDirName) {
    codebuddyFullPath = targetDir;
  } else {
    codebuddyFullPath = path.join(targetDir, codebuddyDirName);
  }

  console.log('ECC CodeBuddy Uninstaller');
  console.log('==========================');
  console.log('');
  console.log(`Target:  ${codebuddyFullPath}/`);
  console.log('');

  // Check if codebuddy directory exists
  if (!fs.existsSync(codebuddyFullPath)) {
    console.error(`Error: ${codebuddyDirName} directory not found at ${targetDir}`);
    process.exit(1);
  }

  const codebuddyRootResolved = resolvePath(codebuddyFullPath);
  const manifest = path.join(codebuddyFullPath, '.ecc-manifest');

  // Handle missing manifest
  if (!fs.existsSync(manifest)) {
    console.log('Warning: No manifest file found (.ecc-manifest)');
    console.log('');
    console.log('This could mean:');
    console.log('  1. ECC was installed with an older version without manifest support');
    console.log('  2. The manifest file was manually deleted');
    console.log('');

    const confirmed = await promptConfirm(`Do you want to remove the entire ${codebuddyDirName} directory? (y/N) `);
    if (!confirmed) {
      console.log('Uninstall cancelled.');
      process.exit(0);
    }

    try {
      fs.rmSync(codebuddyFullPath, { recursive: true, force: true });
      console.log('Uninstall complete!');
      console.log('');
      console.log(`Removed: ${codebuddyFullPath}/`);
    } catch (err) {
      console.error(`Error removing directory: ${err.message}`);
      process.exit(1);
    }
    return;
  }

  console.log('Found manifest file - will only remove files installed by ECC');
  console.log('');

  const confirmed = await promptConfirm(`Are you sure you want to uninstall ECC from ${codebuddyDirName}? (y/N) `);
  if (!confirmed) {
    console.log('Uninstall cancelled.');
    process.exit(0);
  }

  // Read manifest and remove files
  const manifestLines = readManifest(manifest);
  let removed = 0;
  let skipped = 0;

  for (const filePath of manifestLines) {
    if (!filePath || filePath.length === 0) continue;

    if (!isValidManifestEntry(filePath)) {
      console.log(`Skipped: ${filePath} (invalid manifest entry)`);
      skipped += 1;
      continue;
    }

    const fullPath = path.join(codebuddyFullPath, filePath);

    // Security check: use path.relative() to ensure the manifest entry
    // resolves inside the codebuddy directory. This is stricter than
    // startsWith and correctly handles edge-cases with symlinks.
    const relative = path.relative(codebuddyRootResolved, path.resolve(fullPath));
    if (relative.startsWith('..') || path.isAbsolute(relative)) {
      console.log(`Skipped: ${filePath} (outside target directory)`);
      skipped += 1;
      continue;
    }

    try {
      const stats = fs.lstatSync(fullPath);

      if (stats.isFile() || stats.isSymbolicLink()) {
        fs.unlinkSync(fullPath);
        console.log(`Removed: ${filePath}`);
        removed += 1;
      } else if (stats.isDirectory()) {
        try {
          const files = fs.readdirSync(fullPath);
          if (files.length === 0) {
            fs.rmdirSync(fullPath);
            console.log(`Removed: ${filePath}/`);
            removed += 1;
          } else {
            console.log(`Skipped: ${filePath}/ (not empty - contains user files)`);
            skipped += 1;
          }
        } catch {
          console.log(`Skipped: ${filePath}/ (not empty - contains user files)`);
          skipped += 1;
        }
      }
    } catch {
      skipped += 1;
    }
  }

  // Remove empty directories
  const emptyDirs = findEmptyDirs(codebuddyFullPath);
  for (const emptyDir of emptyDirs) {
    try {
      fs.rmdirSync(emptyDir);
      const relativePath = path.relative(codebuddyFullPath, emptyDir);
      console.log(`Removed: ${relativePath}/`);
      removed += 1;
    } catch {
      // Directory might not be empty anymore
    }
  }

  // Try to remove main codebuddy directory if empty
  try {
    const files = fs.readdirSync(codebuddyFullPath);
    if (files.length === 0) {
      fs.rmdirSync(codebuddyFullPath);
      console.log(`Removed: ${codebuddyDirName}/`);
      removed += 1;
    }
  } catch {
    // Directory not empty
  }

  // Print summary
  console.log('');
  console.log('Uninstall complete!');
  console.log('');
  console.log('Summary:');
  console.log(`  Removed: ${removed} items`);
  console.log(`  Skipped: ${skipped} items (not found or user-modified)`);
  console.log('');

  if (fs.existsSync(codebuddyFullPath)) {
    console.log(`Note: ${codebuddyDirName} directory still exists (contains user-added files)`);
  }
}

// Run uninstaller
doUninstall().catch((error) => {
  console.error(`Error: ${error.message}`);
  process.exit(1);
});
