#!/usr/bin/env node

/**
 * HarborForge Monitor Plugin Installer v0.1.0
 */

import { execSync } from 'child_process';
import {
  existsSync,
  mkdirSync,
  copyFileSync,
  readdirSync,
  rmSync,
  readFileSync,
  writeFileSync,
} from 'fs';
import { dirname, join, resolve } from 'path';
import { fileURLToPath } from 'url';
import { homedir, platform } from 'os';

const __filename = fileURLToPath(import.meta.url);
const __dirname = resolve(dirname(__filename), '..');

const PLUGIN_NAME = 'harborforge-monitor';
const PLUGIN_SRC_DIR = join(__dirname, 'plugin');
const SERVER_SRC_DIR = join(__dirname, 'server');
const SKILLS_SRC_DIR = join(__dirname, 'skills');

const args = process.argv.slice(2);
const options = {
  openclawProfilePath: null,
  buildOnly: args.includes('--build-only'),
  skipCheck: args.includes('--skip-check'),
  verbose: args.includes('--verbose') || args.includes('-v'),
  uninstall: args.includes('--uninstall'),
  installOnly: args.includes('--install'),
};

const profileIdx = args.indexOf('--openclaw-profile-path');
if (profileIdx !== -1 && args[profileIdx + 1]) {
  options.openclawProfilePath = resolve(args[profileIdx + 1]);
}

function resolveOpenclawPath() {
  if (options.openclawProfilePath) return options.openclawProfilePath;
  if (process.env.OPENCLAW_PATH) return resolve(process.env.OPENCLAW_PATH);
  return join(homedir(), '.openclaw');
}

const c = {
  reset: '\x1b[0m', red: '\x1b[31m', green: '\x1b[32m',
  yellow: '\x1b[33m', blue: '\x1b[34m', cyan: '\x1b[36m',
};

function log(msg, color = 'reset') { console.log(`${c[color]}${msg}${c.reset}`); }
function logStep(n, total, msg) { log(`[${n}/${total}] ${msg}`, 'cyan'); }
function logOk(msg) { log(` ✓ ${msg}`, 'green'); }
function logWarn(msg) { log(` ⚠ ${msg}`, 'yellow'); }
function logErr(msg) { log(` ✗ ${msg}`, 'red'); }

function exec(command, opts = {}) {
  return execSync(command, {
    cwd: __dirname,
    stdio: opts.silent ? 'pipe' : 'inherit',
    encoding: 'utf8',
    ...opts,
  });
}

function getOpenclawConfig(key, def = undefined) {
  try {
    const out = exec(`openclaw config get ${key} --json 2>/dev/null || echo "undefined"`, { silent: true }).trim();
    if (out === 'undefined' || out === '') return def;
    return JSON.parse(out);
  } catch { return def; }
}

function setOpenclawConfig(key, value) {
  exec(`openclaw config set ${key} '${JSON.stringify(value)}' --json`, { silent: true });
}

function unsetOpenclawConfig(key) {
  try { exec(`openclaw config unset ${key}`, { silent: true }); } catch {}
}

function copyDir(src, dest) {
  mkdirSync(dest, { recursive: true });
  for (const entry of readdirSync(src, { withFileTypes: true })) {
    const s = join(src, entry.name);
    const d = join(dest, entry.name);
    if (entry.name === 'node_modules') continue;
    entry.isDirectory() ? copyDir(s, d) : copyFileSync(s, d);
  }
}

function detectEnvironment() {
  logStep(1, 5, 'Detecting environment...');
  const env = { platform: platform(), nodeVersion: null };
  
  try { 
    env.nodeVersion = exec('node --version', { silent: true }).trim(); 
    logOk(`Node.js ${env.nodeVersion}`); 
  } catch { 
    logErr('Node.js not found'); 
  }
  
  try { 
    logOk(`openclaw at ${exec('which openclaw', { silent: true }).trim()}`); 
  } catch { 
    logWarn('openclaw CLI not in PATH'); 
  }
  
  return env;
}

function checkDeps(env) {
  if (options.skipCheck) { logStep(2, 5, 'Skipping dep checks'); return; }
  logStep(2, 5, 'Checking dependencies...');
  
  let fail = false;
  if (!env.nodeVersion || parseInt(env.nodeVersion.slice(1)) < 18) { 
    logErr('Node.js 18+ required'); 
    fail = true; 
  }
  
  if (fail) { 
    log('\nInstall missing deps and retry.', 'red'); 
    process.exit(1); 
  }
  
  logOk('All deps OK');
}

async function build() {
  logStep(3, 5, 'Building plugin...');
  
  log(' Building TypeScript plugin...', 'blue');
  exec('npm install', { cwd: PLUGIN_SRC_DIR, silent: !options.verbose });
  exec('npm run build', { cwd: PLUGIN_SRC_DIR, silent: !options.verbose });
  logOk('plugin compiled');
}

function clearInstallTargets(openclawPath) {
  const destDir = join(openclawPath, 'plugins', PLUGIN_NAME);
  if (existsSync(destDir)) { 
    rmSync(destDir, { recursive: true, force: true }); 
    logOk(`Removed ${destDir}`); 
  }
}

function cleanupConfig(openclawPath) {
  const destDir = join(openclawPath, 'plugins', PLUGIN_NAME);
  
  try {
    const allow = getOpenclawConfig('plugins.allow', []);
    const idx = allow.indexOf(PLUGIN_NAME);
    if (idx !== -1) {
      allow.splice(idx, 1);
      setOpenclawConfig('plugins.allow', allow);
      logOk('Removed from allow list');
    }
    
    unsetOpenclawConfig(`plugins.entries.${PLUGIN_NAME}`);
    logOk('Removed plugin entry');
    
    const paths = getOpenclawConfig('plugins.load.paths', []);
    const pidx = paths.indexOf(destDir);
    if (pidx !== -1) {
      paths.splice(pidx, 1);
      setOpenclawConfig('plugins.load.paths', paths);
      logOk('Removed from load paths');
    }
  } catch (err) {
    logWarn(`Config cleanup: ${err.message}`);
  }
}

async function install() {
  if (options.buildOnly) { logStep(4, 5, 'Skipping install (--build-only)'); return null; }
  logStep(4, 5, 'Installing...');
  
  const openclawPath = resolveOpenclawPath();
  const pluginsDir = join(openclawPath, 'plugins');
  const destDir = join(pluginsDir, PLUGIN_NAME);
  
  log(` OpenClaw path: ${openclawPath}`, 'blue');
  
  if (existsSync(destDir)) {
    logWarn('Existing install detected, uninstalling before install...');
    clearInstallTargets(openclawPath);
    cleanupConfig(openclawPath);
  }
  
  if (existsSync(destDir)) rmSync(destDir, { recursive: true, force: true });
  
  // Copy compiled plugin
  mkdirSync(destDir, { recursive: true });
  copyDir(PLUGIN_SRC_DIR, destDir);
  
  // Copy telemetry server
  const serverDestDir = join(destDir, 'server');
  mkdirSync(serverDestDir, { recursive: true });
  copyDir(SERVER_SRC_DIR, serverDestDir);
  logOk(`Server files → ${serverDestDir}`);
  
  // Copy skills
  if (existsSync(SKILLS_SRC_DIR)) {
    const skillsDestDir = join(openclawPath, 'skills');
    mkdirSync(skillsDestDir, { recursive: true });
    copyDir(SKILLS_SRC_DIR, skillsDestDir);
    logOk(`Skills → ${skillsDestDir}`);
  }
  
  // Install runtime deps
  exec('npm install --omit=dev', { cwd: destDir, silent: !options.verbose });
  logOk('Runtime deps installed');
  
  return { destDir };
}

async function configure() {
  if (options.buildOnly) { logStep(5, 5, 'Skipping config'); return; }
  logStep(5, 5, 'Configuring OpenClaw...');
  
  const openclawPath = resolveOpenclawPath();
  const destDir = join(openclawPath, 'plugins', PLUGIN_NAME);
  
  try {
    const paths = getOpenclawConfig('plugins.load.paths', []);
    if (!paths.includes(destDir)) { 
      paths.push(destDir); 
      setOpenclawConfig('plugins.load.paths', paths); 
    }
    logOk(`plugins.load.paths includes ${destDir}`);
    
    const allow = getOpenclawConfig('plugins.allow', []);
    if (!allow.includes(PLUGIN_NAME)) { 
      allow.push(PLUGIN_NAME); 
      setOpenclawConfig('plugins.allow', allow); 
    }
    logOk(`plugins.allow includes ${PLUGIN_NAME}`);
    
    // Note: challengeUuid must be configured manually by user
    logOk('Plugin configured (remember to set challengeUuid in ~/.openclaw/openclaw.json)');
    
  } catch (err) {
    logWarn(`Config failed: ${err.message}`);
  }
}

function summary() {
  logStep(5, 5, 'Done!');
  console.log('');
  log('╔══════════════════════════════════════════════╗', 'cyan');
  log('║ HarborForge Monitor v0.1.0 Install Complete ║', 'cyan');
  log('╚══════════════════════════════════════════════╝', 'cyan');
  
  if (options.buildOnly) {
    log('\nBuild-only — plugin not installed.', 'yellow');
    return;
  }
  
  console.log('');
  log('Next steps:', 'blue');
  log(' 1. Register server in HarborForge Monitor to get challengeUuid', 'cyan');
  log(' 2. Edit ~/.openclaw/openclaw.json:', 'cyan');
  log('    {', 'cyan');
  log('      "plugins": {', 'cyan');
  log('        "harborforge-monitor": {', 'cyan');
  log('          "enabled": true,', 'cyan');
  log('          "challengeUuid": "your-challenge-uuid"', 'cyan');
  log('        }', 'cyan');
  log('      }', 'cyan');
  log('    }', 'cyan');
  log(' 3. openclaw gateway restart', 'cyan');
  console.log('');
}

async function uninstall() {
  log('Uninstalling HarborForge Monitor...', 'cyan');
  const openclawPath = resolveOpenclawPath();
  clearInstallTargets(openclawPath);
  cleanupConfig(openclawPath);
  log('\nRun: openclaw gateway restart', 'yellow');
}

async function main() {
  console.log('');
  log('╔══════════════════════════════════════════════╗', 'cyan');
  log('║ HarborForge Monitor Plugin Installer v0.1.0 ║', 'cyan');
  log('╚══════════════════════════════════════════════╝', 'cyan');
  console.log('');
  
  try {
    const env = detectEnvironment();
    
    if (options.uninstall) {
      await uninstall();
      process.exit(0);
    }
    
    checkDeps(env);
    await build();
    
    if (!options.buildOnly) {
      await install();
      await configure();
    }
    
    summary();
  } catch (err) {
    log(`\nInstallation failed: ${err.message}`, 'red');
    process.exit(1);
  }
}

main();
