#!/usr/bin/env node

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

const BINARY_NAME = 'orchestra';
const ENV_COPY_NAME = 'gw-env-copy';
const SHELL_SCRIPTS = ['gwr.sh', 'gw.sh', 'gw-bridge.sh', 'orchestra-local.sh'];
const SHELL_DIRS = ['shell'];
const SUPPORTED_PLATFORMS = {
  'darwin-x64': 'macos-intel',
  'darwin-arm64': 'macos-arm64',
  'linux-x64': 'linux-x64',
  'linux-arm64': 'linux-arm64',
};

const packageRoot = __dirname;
const resourcesDir = path.join(packageRoot, 'resources');
const distDir = path.join(packageRoot, 'dist');
const scriptsDir = path.join(resourcesDir, 'scripts');
const apiDir = path.join(resourcesDir, 'api');
const prebuiltDir = path.join(resourcesDir, 'prebuilt');
const projectRoot = path.resolve(packageRoot, '..', '..');
const hasLocalSource = fs.existsSync(path.join(projectRoot, 'gw-tui'));

function ensureDir(dir) {
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true });
  }
}

function cleanDir(dir) {
  if (fs.existsSync(dir)) {
    fs.rmSync(dir, { recursive: true, force: true });
  }
  ensureDir(dir);
}

function getPlatformKey() {
  const key = `${process.platform}-${process.arch}`;
  const mapped = SUPPORTED_PLATFORMS[key];
  if (!mapped) {
    console.error(`Unsupported platform: ${key}`);
    console.error(`Supported platforms: ${Object.keys(SUPPORTED_PLATFORMS).join(', ')}`);
    process.exit(1);
  }
  return mapped;
}

function copyRecursive(src, dest) {
  const stats = fs.statSync(src);
  if (stats.isDirectory()) {
    ensureDir(dest);
    for (const entry of fs.readdirSync(src)) {
      copyRecursive(path.join(src, entry), path.join(dest, entry));
    }
  } else {
    ensureDir(path.dirname(dest));
    fs.copyFileSync(src, dest);
    fs.chmodSync(dest, 0o755);
  }
}

function copyShellScripts() {
  let sourceBase = scriptsDir;
  if (!fs.existsSync(sourceBase)) {
    if (!hasLocalSource) {
      console.error('Shell scripts are missing from the package.');
      process.exit(1);
    }
    sourceBase = projectRoot;
  }

  for (const script of SHELL_SCRIPTS) {
    const sourcePath = path.join(sourceBase, script);
    if (!fs.existsSync(sourcePath)) {
      console.error(`Required script missing: ${sourcePath}`);
      process.exit(1);
    }
    const destinationPath = path.join(distDir, script);
    copyRecursive(sourcePath, destinationPath);
  }

  for (const dirName of SHELL_DIRS) {
    const sourcePath = path.join(sourceBase, dirName);
    if (!fs.existsSync(sourcePath)) {
      console.error(`Required shell directory missing: ${sourcePath}`);
      process.exit(1);
    }
    const destinationPath = path.join(distDir, dirName);
    copyRecursive(sourcePath, destinationPath);
  }
}

function copyApiScripts() {
  let sourceBase = apiDir;
  if (!fs.existsSync(sourceBase)) {
    if (!hasLocalSource) {
      console.error('API scripts are missing from the package.');
      process.exit(1);
    }
    sourceBase = path.join(projectRoot, 'api');
  }

  const destApiDir = path.join(distDir, 'api');
  ensureDir(destApiDir);

  const apiFiles = fs.readdirSync(sourceBase).filter((file) => file.endsWith('.sh'));
  for (const file of apiFiles) {
    const sourcePath = path.join(sourceBase, file);
    const destinationPath = path.join(destApiDir, file);
    fs.copyFileSync(sourcePath, destinationPath);
    fs.chmodSync(destinationPath, 0o755);
  }
}

function linkCompatibilityBinary(binaryPath) {
  const gwTuiPath = path.join(distDir, 'gw-tui');
  try {
    fs.rmSync(gwTuiPath, { force: true });
  } catch (err) {
    if (err.code !== 'ENOENT') {
      throw err;
    }
  }

  try {
    fs.symlinkSync(binaryPath, gwTuiPath);
  } catch (err) {
    fs.copyFileSync(binaryPath, gwTuiPath);
    fs.chmodSync(gwTuiPath, 0o755);
  }
}

function installPrebuiltBinary() {
  const platform = getPlatformKey();
  const source = path.join(prebuiltDir, platform, BINARY_NAME);
  const envCopySource = path.join(prebuiltDir, platform, ENV_COPY_NAME);
  
  if (!fs.existsSync(source)) {
    console.log(`⚠️  No prebuilt binary found for ${platform}`);
    
    // Check if we have binaries for other platforms (for debugging)
    if (fs.existsSync(prebuiltDir)) {
      const availablePlatforms = fs.readdirSync(prebuiltDir).filter(item => {
        const platformPath = path.join(prebuiltDir, item);
        return fs.statSync(platformPath).isDirectory() && 
               fs.existsSync(path.join(platformPath, BINARY_NAME));
      });
      
      if (availablePlatforms.length > 0) {
        console.log(`   Available platforms: ${availablePlatforms.join(', ')}`);
      }
    }
    
    return false;
  }

  const destination = path.join(distDir, BINARY_NAME);
  fs.copyFileSync(source, destination);
  fs.chmodSync(destination, 0o755);
  linkCompatibilityBinary(destination);
  if (fs.existsSync(envCopySource)) {
    const envCopyDestination = path.join(distDir, ENV_COPY_NAME);
    fs.copyFileSync(envCopySource, envCopyDestination);
    fs.chmodSync(envCopyDestination, 0o755);
  } else {
    console.warn(`⚠️  Missing ${ENV_COPY_NAME} for ${platform}`);
  }
  return true;
}

function buildFromSource() {
  if (!hasLocalSource) {
    console.error('❌ No prebuilt binary available and source tree not found.');
    console.error('');
    console.error('This usually happens when:');
    console.error('1. The npm package was built on a different platform (e.g., macOS vs Linux)');
    console.error('2. The binary for your platform is missing from the package');
    console.error('');
    console.error('Solutions:');
    console.error('1. Install from source (requires Rust toolchain):');
    console.error('   git clone https://github.com/humanunsupervised/orchestra.git');
    console.error('   cd orchestra');
    console.error('   cargo build --release');
    console.error('');
    console.error('2. Use a pre-built release:');
    console.error('   Download from: https://github.com/humanunsupervised/orchestra/releases');
    console.error('');
    console.error('3. Install Rust and build locally:');
    console.error('   curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh');
    console.error('   source ~/.cargo/env');
    console.error('   cargo build --release');
    process.exit(1);
  }

  try {
    execSync('cargo --version', { stdio: 'ignore' });
  } catch (error) {
    console.error('❌ Rust toolchain not found.');
    console.error('');
    console.error('Install Rust from https://rustup.rs/');
    console.error('Then run: curl --proto \'=https\' --tlsv1.2 -sSf https://sh.rustup.rs | sh');
    process.exit(1);
  }

  console.log('🔨 Building from source... (this may take a few minutes)');
  console.log('   Working directory:', path.join(projectRoot, 'gw-tui'));
  
  try {
    execSync('cargo build --release', { stdio: 'inherit', cwd: path.join(projectRoot, 'gw-tui') });
  } catch (error) {
    console.error('❌ Build failed.');
    console.error('');
    console.error('Common issues:');
    console.error('- Missing system dependencies (openssl, pkg-config, etc.)');
    console.error('- Network issues during dependency download');
    console.error('');
    console.error('Try installing dependencies:');
    console.error('- Ubuntu/Debian: sudo apt-get install libssl-dev pkg-config');
    console.error('- macOS: brew install openssl pkg-config');
    console.error('- Fedora: sudo dnf install openssl-devel pkgconfig');
    process.exit(1);
  }

  const builtBinary = path.join(projectRoot, 'gw-tui', 'target', 'release', 'gw-tui');
  if (!fs.existsSync(builtBinary)) {
    console.error('❌ Build finished but gw-tui binary was not produced.');
    console.error('   Expected location:', builtBinary);
    process.exit(1);
  }

  const destination = path.join(distDir, BINARY_NAME);
  fs.copyFileSync(builtBinary, destination);
  fs.chmodSync(destination, 0o755);
  linkCompatibilityBinary(destination);

  const envCopyBinary = path.join(projectRoot, 'gw-tui', 'target', 'release', ENV_COPY_NAME);
  if (!fs.existsSync(envCopyBinary)) {
    console.error(`❌ ${ENV_COPY_NAME} not found after build.`);
    console.error('   Expected location:', envCopyBinary);
    process.exit(1);
  }
  const envCopyDestination = path.join(distDir, ENV_COPY_NAME);
  fs.copyFileSync(envCopyBinary, envCopyDestination);
  fs.chmodSync(envCopyDestination, 0o755);
  
  console.log('✅ Successfully built from source!');
}

function assetsReady() {
  return (
    fs.existsSync(path.join(distDir, BINARY_NAME)) &&
    fs.existsSync(path.join(distDir, ENV_COPY_NAME)) &&
    fs.existsSync(path.join(distDir, 'gwr.sh'))
  );
}

function printShellWrapperInstructions(binaryPath) {
  const distPathEscaped = distDir.split('\\').join('\\\\');
  const binaryPathEscaped = binaryPath.split('\\').join('\\\\');

  console.log('\n📝 Shell wrapper functions needed for directory switching:\n');
  console.log(`# GW Orchestrator shell wrappers
gwr() {
  local dist_path="${distPathEscaped}"
  local bin_path="${binaryPathEscaped}"
  local out="$(GW_TUI_BIN="$bin_path" bash "$dist_path/gwr.sh" "$@")"
  local status=$?
  local cd_line="$(echo "$out" | grep -m1 '^cd')"
  [[ -n $cd_line ]] && eval "$cd_line"
  echo "$out" | grep -v '^cd'
  return $status
}

gw() {
  local dist_path="${distPathEscaped}"
  local out="$(bash "$dist_path/gw.sh" "$@")"
  local status=$?
  local cd_line="$(echo "$out" | grep -m1 '^cd')"
  [[ -n $cd_line ]] && eval "$cd_line"
  echo "$out" | grep -v '^cd'
  return $status
}`);
  console.log('\nAdd these to your ~/.bashrc or ~/.zshrc, then run: source ~/.bashrc (or source ~/.zshrc)\n');
}

function performSetup({ quiet = false, showInstructions = false } = {}) {
  if (!quiet) {
    console.log('Installing Orchestra...\n');
  }

  cleanDir(distDir);
  copyShellScripts();
  copyApiScripts();

  const binaryInstalled = installPrebuiltBinary();
  if (!binaryInstalled) {
    buildFromSource();
  }

  const binaryPath = path.join(distDir, BINARY_NAME);

  if (!quiet) {
    console.log('\n✅ Installation complete!');
  }
  if (showInstructions) {
    printShellWrapperInstructions(binaryPath);
  }
}

function ensureAssets() {
  if (!assetsReady()) {
    performSetup({ quiet: false, showInstructions: false });
  }
}

function runCommand(command, args) {
  ensureAssets();

  let scriptName;
  if (command === 'gw') {
    scriptName = 'gw.sh';
  } else if (command === 'orchestra-local') {
    scriptName = 'orchestra-local.sh';
  } else {
    scriptName = 'gwr.sh';
  }
  const scriptPath = path.join(distDir, scriptName);
  if (!fs.existsSync(scriptPath)) {
    console.error(`Missing script: ${scriptPath}`);
    process.exit(1);
  }

  const env = { ...process.env };
  if (command !== 'gw' && command !== 'orchestra-local') {
    env.GW_TUI_BIN = path.join(distDir, BINARY_NAME);
  }

  const result = spawnSync('bash', [scriptPath, ...args], {
    stdio: 'inherit',
    env,
  });

  if (result.error) {
    console.error(`Failed to launch ${command}:`, result.error.message);
    process.exit(result.status ?? 1);
  }

  process.exit(result.status ?? 0);
}

function parseArgs() {
  const argv = process.argv.slice(2);
  let invoke = null;
  const args = [];

  for (let i = 0; i < argv.length; i += 1) {
    const value = argv[i];
    if (value === '--invoke' && i + 1 < argv.length) {
      invoke = argv[i + 1];
      i += 1;
      continue;
    }
    if (value.startsWith('--invoke=')) {
      invoke = value.slice('--invoke='.length);
      continue;
    }
    args.push(value);
  }

  return { invoke, args };
}

function main() {
  const lifecycle = process.env.npm_lifecycle_event;
  const { invoke, args } = parseArgs();

  if (lifecycle === 'postinstall') {
    performSetup({ quiet: false, showInstructions: true });
    return;
  }

  if (invoke) {
    let command;
    if (invoke === 'gw') {
      command = 'gw';
    } else if (invoke === 'orchestra-local') {
      command = 'orchestra-local';
    } else {
      command = 'gwr';
    }
    runCommand(command, args);
    return;
  }

  // If someone runs `node install.js` manually, treat it as a setup utility.
  performSetup({ quiet: false, showInstructions: true });
}

main();
