#!/usr/bin/env node

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

const TEMPLATE_DIR =   __dirname;
const YES = process.argv.includes('--yes') || process.argv.includes('-y');
const IS_UPDATE = process.argv.includes('--update') || process.argv.includes('-u');
const IS_SELF = process.argv.includes('--self') || process.argv.includes('-s');
const IS_FORCE = process.argv.includes('--force') || process.argv.includes('-f');

// Files that belong to the user — NEVER overwritten
const USER_FILES = new Set([
  '.qa-workflow.json',
  'opencode.json',
  'docs/application-context.md',
  'package.json',
]);

// Directories that are pure user data — NEVER touched
const USER_DIRS = new Set([
  'user-story',
  'specs',
  'tests',
  'test-results',
  '.qa-context',
  '.auth',
  '.env',
]);

// Template files to copy/update
const QA_ITEMS = [
  { src: 'ai-qa-workflow.js', dest: 'ai-qa-workflow.js' },
  { src: 'scripts', dest: 'scripts', dir: true },
  { src: '.qa-workflow.json', dest: '.qa-workflow.json' },
  { src: 'prompts', dest: 'prompts', dir: true },
  { src: '.github/agents', dest: '.github/agents', dir: true },
  { src: '.opencode', dest: '.opencode', dir: true },
   { src: 'README.md', dest: 'README.md' },
  { src: 'PROJECT_GUIDE.md', dest: 'PROJECT_GUIDE.md' },
    { src: 'prompting_template.md', dest: 'prompting_template.md' },
    { src: '.cursorrules', dest: '.cursorrules' },
     { src: '.geminirules', dest: '.geminirules' },
     { src: '.github/copilot-instructions.md', dest: '.github/copilot-instructions.md' },
      { src: 'router.md', dest: 'router.md' },
      { src: 'opencode.json', dest: 'opencode.json' },
    { src: 'playwright.config.ts', dest: 'playwright.config.ts' },
    { src: 'cli.js', dest: 'cli.js' },
    { src: 'package.json', dest: 'package.json' },
    { src: 'install.js', dest: 'install.js' },
    { src: 'tsconfig.json', dest: 'tsconfig.json' },
    { src: 'bin', dest: 'bin', dir: true },


    ];
// Files to copy even during update (excludes user config files unless --force)
const UPDATE_ITEMS = IS_FORCE ? QA_ITEMS : QA_ITEMS.filter(item => {
  if (item.dir) return !USER_DIRS.has(item.dest);
  return !USER_FILES.has(item.dest);
});

const DIRS_TO_CREATE = ['user-story', 'specs', 'tests', 'test-results', '.qa-context', '.auth', 'templates', 'bin'];

const BANNER = `
  ╔══════════════════════════════════════════╗
  ║    AI QA Pipeline Installer v2.0         ║
  ╚══════════════════════════════════════════╝
`;

function copyRecursive(src, dest, skipDirs) {
  skipDirs = skipDirs || new Set();
  const stat = fs.statSync(src);
  if (stat.isDirectory()) {
    const base = path.basename(src);
    if (skipDirs.has(base)) return;
    if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
    fs.readdirSync(src).forEach(item => copyRecursive(path.join(src, item), path.join(dest, item), skipDirs));
  } else {
    if (!fs.existsSync(dest) || !fs.readFileSync(src).equals(fs.readFileSync(dest))) {
      fs.copyFileSync(src, dest);
      return true;
    }
  }
  return false;
}

function countFiles(dir) {
  let count = 0;
  if (!fs.existsSync(dir)) return 0;
  fs.readdirSync(dir).forEach(item => {
    try {
      const full = path.join(dir, item);
      if (fs.statSync(full).isDirectory()) count += countFiles(full);
      else count++;
    } catch (e) {}
  });
  return count;
}

function ask(query) {
  const rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
  return new Promise(resolve => rl.question(query, a => { rl.close(); resolve(a.toLowerCase()); }));
}

async function install(targetPath, mode) {
  const isUpdate = mode === 'update';

  console.log(BANNER);
  console.log(`  Mode: ${isUpdate ? 'UPDATE (preserves config)' : 'FRESH INSTALL'}`);
  console.log(`  Target: ${targetPath}\n`);

  // 1. Copy template files
  const items = isUpdate ? UPDATE_ITEMS : QA_ITEMS;
  console.log(`  ── Step 1: QA Pipeline Files ──`);
  for (const item of items) {
    const srcPath = path.join(TEMPLATE_DIR, item.src);
    const destPath = path.join(targetPath, item.dest);
    if (!fs.existsSync(srcPath)) { console.log(`  ⚠  ${item.src} not found, skipping`); continue; }
    if (item.dir) {
      if (!fs.existsSync(destPath)) fs.mkdirSync(destPath, { recursive: true });
      const skipDirs = isUpdate ? new Set(['node_modules', 'data']) : new Set();
      copyRecursive(srcPath, destPath, skipDirs);
      const count = countFiles(srcPath);
      console.log(`  ✓ ${item.dest}/ (${count} files)`);
    } else {
      fs.copyFileSync(srcPath, destPath);
      console.log(`  ✓ ${item.dest}`);
    }
  }

  // Warn about protected user files not updated during update
  if (isUpdate && !IS_FORCE) {
    const skipped = QA_ITEMS.filter(item => {
      if (item.dir) return USER_DIRS.has(item.dest);
      return USER_FILES.has(item.dest);
    });
    if (skipped.length) {
      console.log(`\n  ── Protected files (use --force to overwrite) ──`);
      for (const item of skipped) {
        const srcStat = fs.existsSync(path.join(TEMPLATE_DIR, item.src));
        const destStat = fs.existsSync(path.join(targetPath, item.dest));
        if (srcStat && destStat) {
          const changed = !fs.readFileSync(path.join(TEMPLATE_DIR, item.src))
            .equals(fs.readFileSync(path.join(targetPath, item.dest)));
          if (changed) {
            console.log(`  ⚠  ${item.dest} (modified locally, skipped)`);
          }
        }
      }
    }
  }

  // 2. Create directories (fresh install only)
  if (!isUpdate) {
    console.log(`\n  ── Step 2: Project Directories ──`);
    for (const dir of DIRS_TO_CREATE) {
      const dirPath = path.join(targetPath, dir);
      if (!fs.existsSync(dirPath)) { fs.mkdirSync(dirPath, { recursive: true }); console.log(`  ✓ ${dir}/`); }
      else console.log(`  • ${dir}/ (exists)`);
    }
  }

  // 3. Create .env template (fresh install only)
  console.log(`\n  ── Step 3: Environment ──`);
  if (!isUpdate) {
    const envPath = path.join(targetPath, '.env');
    if (!fs.existsSync(envPath)) {
      fs.writeFileSync(envPath, 'APPLITOOLS_API_KEY=""\nGITHUB_TOKEN=""\n');
      console.log(`  ✓ .env (template created)`);
    } else {
      console.log(`  • .env (exists, kept as-is)`);
    }
  }

  // 4. Dashboard
  const dashboardSrc = path.join(TEMPLATE_DIR, 'qa-dashboard');
  const dashboardDest = path.join(targetPath, 'qa-dashboard');
  if (fs.existsSync(dashboardSrc)) {
    console.log(`\n  ── Step 4: QA Dashboard ──`);
    if (!fs.existsSync(dashboardDest)) {
      fs.mkdirSync(dashboardDest, { recursive: true });
      copyRecursive(dashboardSrc, dashboardDest);
      console.log(`  ✓ Dashboard copied to qa-dashboard/`);
      console.log(`  → Installing dashboard dependencies...`);
      try { execSync('npm install', { cwd: dashboardDest, stdio: 'pipe', timeout: 120000 }); console.log(`  ✓ Dashboard deps installed`); } catch (e) { console.log(`  ⚠  cd qa-dashboard && npm install`); }
    } else {
      const skipDirs = new Set(['node_modules', 'data']);

      // Detect if dashboard package.json changed
      const dashPkgSrc = path.join(dashboardSrc, 'package.json');
      const dashPkgDest = path.join(dashboardDest, 'package.json');
      const pkgChanged = fs.existsSync(dashPkgSrc) && fs.existsSync(dashPkgDest) &&
        !fs.readFileSync(dashPkgSrc).equals(fs.readFileSync(dashPkgDest));

      copyRecursive(dashboardSrc, dashboardDest, skipDirs);
      console.log(`  ✓ Dashboard updated (preserved data/, node_modules/)`);

      const needsInstall = !isUpdate || pkgChanged;
      if (needsInstall) {
        console.log(`  → ${pkgChanged ? 'package.json changed, updating' : 'Installing'} dashboard dependencies...`);
        try { execSync('npm install', { cwd: dashboardDest, stdio: 'pipe', timeout: 120000 }); console.log(`  ✓ Dashboard deps ${pkgChanged ? 'updated' : 'installed'}`); } catch (e) { console.log(`  ⚠  cd qa-dashboard && npm install`); }
      }
    }
  }

  // 5. Register project in dashboard (fresh install only)
  if (!isUpdate && fs.existsSync(dashboardDest)) {
    console.log(`\n  ── Step 5: Link Dashboard → Project ──`);
    const dashDataDir = path.join(dashboardDest, 'data');
    const dashProjectsFile = path.join(dashDataDir, 'projects.json');
    if (fs.existsSync(dashDataDir)) {
      let projects = [];
      if (fs.existsSync(dashProjectsFile)) {
        try { projects = JSON.parse(fs.readFileSync(dashProjectsFile, 'utf-8')); } catch(e) {}
      }
      const projectName = path.basename(targetPath);
      const projectId = projectName.toLowerCase().replace(/[^a-z0-9]+/g, '-');
      if (!projects.find(p => p.id === projectId)) {
        projects.push({ id: projectId, name: projectName, path: targetPath, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString() });
        fs.writeFileSync(dashProjectsFile, JSON.stringify(projects, null, 2));
        console.log(`  ✓ Project "${projectName}" registered in dashboard`);
      }
    }
  }

  // 6. Install all dependencies (fresh only)
  if (!isUpdate) {
    console.log(`\n  ── Step 6: Dependencies ──`);
    if (!fs.existsSync(path.join(targetPath, 'node_modules', '@playwright'))) {
      console.log(`  → Installing all dependencies (Playwright, Allure, Applitools)...`);
      try { execSync('npm install', { cwd: targetPath, stdio: 'pipe', timeout: 180000 }); console.log(`  ✓ All dependencies installed`); } catch (e) { console.log(`  ⚠  npm install failed: npm install`); }
    } else console.log(`  • Dependencies already installed`);
  }

  // Summary
  const totalFiles = countFiles(path.join(targetPath, 'scripts'));
  const dashboardCount = countFiles(path.join(targetPath, 'qa-dashboard'));

  console.log(`\n  ╔══════════════════════════════════════════╗`);
  console.log(`  ║   ✓ ${isUpdate ? 'UPDATE COMPLETE' : 'INSTALLATION COMPLETE'}${' '.repeat(isUpdate ? 13 : 7)}║`);
  console.log(`  ╚══════════════════════════════════════════╝\n`);

  if (isUpdate) {
    console.log(`  Pipeline files updated. Your config and data are preserved.`);
    if (!IS_FORCE) console.log(`  Use --force to overwrite protected files (package.json, .qa-workflow.json, etc.)`);
    console.log(`  Restart the dashboard if it was running.\n`);
  } else {
    console.log(`  Files: ~${totalFiles} scripts + ${dashboardCount} dashboard files`);
    console.log(`  Dependencies: @playwright/test, allure-playwright, @applitools/eyes-playwright, @applitools/mcp`);
    console.log(`  Next:\n`);
    console.log(`  npm install          Install all dependencies`);
    console.log(`  npm run qa:init      Initialize pipeline (config + dirs + auth)`);
    console.log(`  npm run qa:execute   Run Playwright tests (auto-generates Allure report)`);
    console.log(`  npm run qa:status    Check pipeline state`);
    console.log(`  npm run qa:update    Update pipeline files from template`);
    console.log(`  npm run dashboard    Start dashboard (port 4000)\n`);
  }
}

// ---- CLI entry ----
const args = process.argv.slice(2);
const isHelp = !args.length || args.includes('--help') || args.includes('-h');

if (isHelp) {
  console.log(`
AI QA Pipeline Installer

  USAGE:

    npx ai-qa-workflow init [--yes]     Install into current directory
    npx ai-qa-workflow update [--yes]   Update pipeline files (preserves config)

  Or from the template directory:

    node install.js <target> [--yes]        Fresh install into target project
    node install.js <target> --update       Update existing installation
    node install.js . --yes                 Install into current directory

  FLAGS:
    --yes, -y    Skip all prompts
    --update,-u  Update mode (preserves config, stories, results)
    --force,-f   Overwrite protected user files (package.json, .qa-workflow.json, ...)

  EXAMPLES:
    npx ai-qa-workflow init --yes
    npx ai-qa-workflow update           Update pipeline in current project
    node install.js ../my-project --yes
    node install.js ../my-project --update
    node install.js ../my-project --update --force
`);
  process.exit(0);
}

async function main() {
  let targetPath;
  let mode = 'install';

  // Parse flags and target
  const nonFlagArgs = args.filter(a => !a.startsWith('-'));

  if (args.includes('update')) {
      targetPath = process.cwd();
      mode = 'update';
  } else if (IS_SELF || args.includes('init')) {
      targetPath = process.cwd();
      mode = 'install';
  } else if (IS_UPDATE) {
    targetPath = path.resolve(nonFlagArgs[0] || process.cwd());
    mode = 'update';
  } else if (nonFlagArgs.length > 0) {
    targetPath = path.resolve(nonFlagArgs[0]);
    mode = 'install';
  } else {
    targetPath = process.cwd();
    mode = 'install';
  }

  if (!fs.existsSync(targetPath) || !fs.statSync(targetPath).isDirectory()) {
    console.error(`  ✗ Target not found: ${targetPath}`);
    process.exit(1);
  }

  // Confirm unless --yes
  if (!YES) {
    const modeLabel = mode === 'update' ? 'UPDATE pipeline files' : 'INSTALL pipeline';
    const answer = await ask(`  ${modeLabel} in "${path.basename(targetPath)}"? (Y/n) `);
    if (answer.startsWith('n')) { console.log('  Aborted.'); process.exit(0); }
  }

  await install(targetPath, mode);
}

main().catch(e => { console.error(e); process.exit(1); });
