#!/usr/bin/env node

/**
 * URVault Automatic Installer
 * Cross-platform installer that handles everything automatically.
 * Pass --auto for zero-prompt mode (all defaults, used by platform installers).
 */

import readline from 'readline';
import { execSync } from 'child_process';
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import os from 'os';

const AUTO = process.argv.includes('--auto');

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

function question(query) {
  return new Promise(resolve => rl.question(query, resolve));
}

/** In --auto mode returns the default immediately; otherwise prompts. */
async function prompt(query, defaultValue = '') {
  if (AUTO) return defaultValue;
  const answer = await question(query);
  return (answer && answer.trim()) ? answer.trim() : defaultValue;
}

function log(message, type = 'info') {
  const colors = {
    info: '\x1b[36m',
    success: '\x1b[32m',
    error: '\x1b[31m',
    warning: '\x1b[33m',
    reset: '\x1b[0m'
  };
  console.log(`${colors[type]}${message}${colors.reset}`);
}

function generateSecret(length = 32) {
  return crypto.randomBytes(length).toString('hex');
}

function validateIdentifier(value, name) {
  if (!/^[a-zA-Z0-9_]{1,64}$/.test(value)) {
    log(`❌ Invalid ${name} "${value}": only letters, numbers, and underscores are allowed (max 64 chars)`, 'error');
    process.exit(1);
  }
}

function sanitizeSqlString(value) {
  return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
}

function getLocalIP() {
  const interfaces = os.networkInterfaces();
  for (const name of Object.keys(interfaces)) {
    for (const iface of interfaces[name]) {
      if (iface.family === 'IPv4' && !iface.internal) {
        return iface.address;
      }
    }
  }
  return '0.0.0.0';
}

function detectPlatform() {
  const platform = os.platform();
  if (platform === 'win32') return 'windows';
  if (platform === 'darwin') return 'macos';
  return 'linux';
}

function checkNodeVersion() {
  const version = process.version;
  const major = parseInt(version.split('.')[0].substring(1));
  if (major < 18) {
    log('❌ Node.js 18+ required. Current version: ' + version, 'error');
    log('Please install Node.js 18 or higher from https://nodejs.org/', 'warning');
    process.exit(1);
  }
  log('✅ Node.js version: ' + version, 'success');
}

function checkMySQL() {
  try {
    const platform = detectPlatform();
    if (platform === 'windows') {
      execSync('mysql --version', { stdio: 'ignore' });
    } else {
      execSync('which mysql', { stdio: 'ignore' });
    }
    log('✅ MySQL is installed', 'success');
    return true;
  } catch (error) {
    log('⚠️  MySQL not detected', 'warning');
    return false;
  }
}

function detectMySQLAuth() {
  const platform = detectPlatform();

  if (platform === 'windows') {
    // Check for root password supplied by the platform installer (Install URVault.bat)
    const envPw = process.env.URVAULT_MYSQL_ROOT_PW;
    if (envPw !== undefined) {
      const hasPassword = envPw.length > 0;
      log(`✅ MySQL: Using ${hasPassword ? 'pre-supplied' : 'no'} root password`, 'success');
      return { useSudo: false, needsPassword: false, rootPassword: envPw, platform: 'windows' };
    }
    // Interactive fallback
    log('✅ MySQL: Using password authentication (Windows)', 'success');
    return { useSudo: false, needsPassword: true, platform: 'windows' };
  }

  // Try passwordless socket first (Linux default)
  try {
    execSync('mysql -u root -e "SELECT 1;" 2>/dev/null', { stdio: 'ignore' });
    log('✅ MySQL: Using socket authentication (no password)', 'success');
    return { useSudo: false, needsPassword: false };
  } catch (error) {
    // Try sudo socket (Ubuntu default)
    try {
      execSync('sudo -n mysql -u root -e "SELECT 1;" 2>/dev/null', { stdio: 'ignore' });
      log('✅ MySQL: Using sudo socket authentication', 'success');
      return { useSudo: true, needsPassword: false };
    } catch (error) {
      log('✅ MySQL: Using password authentication', 'success');
      return { useSudo: false, needsPassword: true };
    }
  }
}

async function createDatabase(config, mysqlAuth) {
  log('\n📊 Setting up database...', 'info');

  // If the platform installer pre-created the database, skip the mysql CLI entirely
  if (process.env.URVAULT_DB_READY === '1') {
    log('  ✅ Database pre-configured by installer', 'info');
    return true;
  }

  validateIdentifier(config.DB_NAME, 'Database Name');
  validateIdentifier(config.DB_USER, 'MySQL User');

  const mysqlCmd = mysqlAuth.useSudo ? 'sudo mysql' : 'mysql';
  const rootPassFlag = (mysqlAuth.needsPassword || mysqlAuth.rootPassword)
    ? ` -u root -p${mysqlAuth.rootPassword || ''}`
    : ' -u root';
  const mysqlRootCmd = mysqlAuth.useSudo ? 'sudo mysql' : `mysql${rootPassFlag}`;

  try {
    // Check if database exists
    const checkDbCmd = `${mysqlRootCmd} -e "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='${config.DB_NAME}';"`;
    let databaseExists = false;
    let hasExistingTables = false;

    try {
      const result = execSync(checkDbCmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
      databaseExists = result.includes(config.DB_NAME);

      if (databaseExists) {
        const checkTablesCmd = `${mysqlRootCmd} ${config.DB_NAME} -e "SHOW TABLES;"`;
        const tablesResult = execSync(checkTablesCmd, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] });
        hasExistingTables = tablesResult.includes('backups') || tablesResult.includes('devices') || tablesResult.includes('admin');
      }
    } catch (error) {
      // Database doesn't exist yet
    }

    if (databaseExists) {
      if (AUTO) {
        log('\n  ℹ️  Database already exists — keeping existing data', 'info');
        return true;
      }

      log('\n' + '─'.repeat(60), 'warning');
      log('⚠️  DATABASE ALREADY EXISTS', 'warning');
      log('─'.repeat(60), 'warning');

      if (hasExistingTables) {
        log('\n⚠️  The database contains existing tables with data.', 'warning');
        log('\nYou have two options:', 'info');
        log('  1. FRESH INSTALL - Drop and recreate (ALL DATA WILL BE LOST)', 'warning');
        log('  2. KEEP DATA - Use existing database (may fail if schema is incompatible)', 'info');
      } else {
        log('\n⚠️  The database exists but appears to be empty.', 'info');
        log('\nYou have two options:', 'info');
        log('  1. FRESH INSTALL - Drop and recreate (recommended)', 'info');
        log('  2. KEEP DATABASE - Use existing database', 'info');
      }

      const dropDb = await question('\nDo you want a FRESH INSTALL? (y/n): ');

      if (dropDb.toLowerCase() === 'y') {
        log('\n🗑️  Dropping existing database...', 'warning');
        execSync(`${mysqlRootCmd} -e "DROP DATABASE IF EXISTS ${config.DB_NAME};"`, { stdio: 'pipe' });
        log('✅ Database dropped', 'success');
      } else {
        log('\n✅ Keeping existing database', 'info');
        if (hasExistingTables) {
          log('⚠️  WARNING: Migrations may fail if schema is incompatible.', 'warning');
        }
        return true;
      }
    }

    log('\n🔨 Creating database and user...', 'info');
    const safePassword = sanitizeSqlString(config.DB_PASSWORD);
    const setupCmd = `${mysqlRootCmd} -e "CREATE DATABASE IF NOT EXISTS ${config.DB_NAME}; CREATE USER IF NOT EXISTS '${config.DB_USER}'@'localhost' IDENTIFIED BY '${safePassword}'; ALTER USER '${config.DB_USER}'@'localhost' IDENTIFIED BY '${safePassword}'; GRANT ALL PRIVILEGES ON ${config.DB_NAME}.* TO '${config.DB_USER}'@'localhost'; FLUSH PRIVILEGES;"`;
    execSync(setupCmd, { stdio: 'pipe' });
    log('✅ Database and user created', 'success');

    // Register MySQL as a Windows service so it auto-starts on reboot
    if (mysqlAuth.platform === 'windows') {
      try {
        const mysqldPath = execSync('where mysqld', { encoding: 'utf8', stdio: ['pipe','pipe','pipe'] }).trim().split('\n')[0].trim();
        const datadirResult = execSync(`mysql${rootPassFlag} -e "SHOW VARIABLES LIKE 'datadir';"`, { encoding: 'utf8', stdio: 'pipe' });
        const m = datadirResult.match(/datadir\s+(.+)/);
        if (mysqldPath && m) {
          const datadir = m[1].trim();
          try { execSync(`"${mysqldPath}" --install MySQL84 --datadir="${datadir}"`, { stdio: 'pipe' }); } catch (e) {}
          try { execSync('net start MySQL84', { stdio: 'pipe' }); } catch (e) {}
          log('✅ MySQL registered as auto-start service (MySQL84)', 'success');
        }
      } catch (e) {
        log('⚠️  MySQL service registration skipped -- MySQL must be running before starting URVault', 'warning');
      }
    }

    return true;
  } catch (error) {
    log('❌ Failed to create database: ' + error.message, 'error');
    return false;
  }
}

function createEnvFile(config) {
  // On re-install: preserve identity/crypto keys so paired devices stay connected.
  const preserveKeys = ['JWT_SECRET', 'ENCRYPTION_KEY', 'ADMIN_KEY', 'MAILBOX_SECRET', 'SERVER_UUID'];
  const preserved = {};
  if (fs.existsSync('.env')) {
    const existing = fs.readFileSync('.env', 'utf8');
    for (const key of preserveKeys) {
      const m = existing.match(new RegExp(`^${key}=(.+)`, 'm'));
      if (m && m[1].trim()) preserved[key] = m[1].trim();
    }
    if (Object.keys(preserved).length > 0) {
      log('  Re-install detected -- preserving existing security keys so paired devices stay connected', 'info');
    }
  }

  const envContent = `# URVault Configuration
# Generated by installer on ${new Date().toISOString()}

# Database
DB_HOST=${config.DB_HOST}
DB_PORT=${config.DB_PORT}
DB_USER=${config.DB_USER}
DB_PASSWORD=${config.DB_PASSWORD}
DB_NAME=${config.DB_NAME}

# Server
PORT=${config.PORT}
SERVER_PORT=${config.PORT}
SERVER_HOST=0.0.0.0
NODE_ENV=production
HOST=0.0.0.0

# Storage
STORAGE_PATH=${config.STORAGE_PATH}
MAX_BACKUP_SIZE=10737418240

# Security (auto-generated -- do not share)
JWT_SECRET=${preserved.JWT_SECRET || config.JWT_SECRET}
ENCRYPTION_KEY=${preserved.ENCRYPTION_KEY || config.ENCRYPTION_KEY}
ADMIN_KEY=${preserved.ADMIN_KEY || config.ADMIN_KEY}

# Relay
MAILBOX_URL=https://urvault.net
MAILBOX_SECRET=${preserved.MAILBOX_SECRET || config.MAILBOX_SECRET}

# Network (LOCAL_IP updated automatically on each startup)
LOCAL_IP=${config.LOCAL_IP}
# EXTERNAL_URL set automatically at startup via Cloudflare Tunnel
EXTERNAL_URL=
SERVER_UUID=${preserved.SERVER_UUID || config.SERVER_UUID}
`;

  fs.writeFileSync('.env', envContent);
  log('✅ Configuration file created', 'success');
}

function createStorageDirectory(storagePath) {
  try {
    if (!fs.existsSync(storagePath)) {
      fs.mkdirSync(storagePath, { recursive: true });
      log(`✅ Storage directory created: ${storagePath}`, 'success');
    } else {
      log(`✅ Storage directory exists: ${storagePath}`, 'success');
    }
  } catch (error) {
    log(`⚠️  Could not create storage directory: ${error.message}`, 'warning');
  }
}

function runMigrations() {
  log('\n🔄 Running database migrations...', 'info');
  try {
    execSync('node run-migrations.js', { stdio: 'inherit' });
    log('✅ Database migrations completed', 'success');
    return true;
  } catch (error) {
    // Non-zero exit from migrations is often harmless (tables already exist),
    // but surface the message so users can see if something is genuinely wrong.
    log('⚠️  Migrations reported an issue: ' + error.message, 'warning');
    log('    This is usually OK if the database tables already exist.', 'warning');
    log('    If the server fails to start, check your DB_PASSWORD in .env and re-run.', 'warning');
    return true;
  }
}

function setupMdns(config) {
  const platform = detectPlatform();

  if (platform === 'windows') {
    log('ℹ️  mDNS on Windows requires Bonjour (optional). See docs for details.', 'info');
    return;
  }

  if (platform === 'macos') {
    try {
      const startScript = fs.readFileSync('start-server.sh', 'utf8');
      if (!startScript.includes('dns-sd')) {
        const dnsSd = `
# Register mDNS service (macOS Bonjour)
dns-sd -R "URVault" _urvault._tcp . ${config.PORT} uuid=${config.SERVER_UUID} &
DNS_SD_PID=$!
trap "kill $DNS_SD_PID 2>/dev/null" EXIT
`;
        const patched = startScript.replace('echo "Starting server..."', dnsSd + 'echo "Starting server..."');
        fs.writeFileSync('start-server.sh', patched);
      }
      log('✅ mDNS configured (macOS Bonjour)', 'success');
    } catch (e) {
      log(`⚠️  mDNS setup skipped: ${e.message}`, 'warning');
    }
    return;
  }

  // Linux — avahi
  try {
    try { execSync('which avahi-daemon', { stdio: 'pipe' }); }
    catch (e) {
      log('Installing avahi-daemon...', 'info');
      execSync('apt-get install -y avahi-daemon avahi-utils', { stdio: 'inherit' });
    }
    const serviceXml = `<?xml version="1.0" standalone='no'?>
<!DOCTYPE service-group SYSTEM "avahi-service.dtd">
<service-group>
  <name replace-wildcards="yes">URVault on %h</name>
  <service>
    <type>_urvault._tcp</type>
    <port>${config.PORT}</port>
    <txt-record>uuid=${config.SERVER_UUID}</txt-record>
  </service>
  <service>
    <type>_http._tcp</type>
    <port>${config.PORT}</port>
    <txt-record>uuid=${config.SERVER_UUID}</txt-record>
  </service>
</service-group>`;
    fs.writeFileSync('/etc/avahi/services/urvault.service', serviceXml);
    execSync('systemctl enable avahi-daemon && systemctl restart avahi-daemon', { stdio: 'pipe' });
    log('✅ mDNS configured -- server discoverable as urvault.local', 'success');
  } catch (e) {
    log(`⚠️  mDNS setup skipped: ${e.message}`, 'warning');
  }
}

async function downloadCloudflared() {
  log('\n☁️  Setting up remote access (Cloudflare Tunnel)...', 'info');

  const platform = detectPlatform();
  const arch = os.arch();
  const filename = platform === 'windows' ? 'cloudflared.exe' : 'cloudflared';

  if (fs.existsSync(filename)) {
    log('✅ cloudflared already present', 'success');
    return true;
  }
  try {
    execSync(platform === 'windows' ? 'where cloudflared' : 'which cloudflared', { stdio: 'ignore' });
    log('✅ cloudflared found in PATH', 'success');
    return true;
  } catch (e) {}

  const urls = {
    windows:    'https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64.exe',
    macos_arm64:'https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-darwin-arm64',
    macos_x64:  'https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-darwin-amd64',
    linux_arm64:'https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-arm64',
    linux_x64:  'https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64',
  };
  let url;
  if (platform === 'windows') url = urls.windows;
  else if (platform === 'macos') url = arch === 'arm64' ? urls.macos_arm64 : urls.macos_x64;
  else url = arch === 'arm64' ? urls.linux_arm64 : urls.linux_x64;

  log(`   Downloading cloudflared for ${platform}/${arch}...`, 'info');
  try {
    if (platform === 'windows') {
      execSync(`powershell -Command "Invoke-WebRequest -Uri '${url}' -OutFile '${filename}'"`, { stdio: 'inherit' });
    } else {
      execSync(`curl -L --progress-bar -o "${filename}" "${url}"`, { stdio: 'inherit' });
      execSync(`chmod +x "${filename}"`, { stdio: 'ignore' });
    }
    log('✅ cloudflared downloaded -- remote access enabled', 'success');
    return true;
  } catch (e) {
    log('⚠️  Could not download cloudflared. Remote access will be unavailable.', 'warning');
    return false;
  }
}

function createStartupScripts(platform, config) {
  if (platform === 'windows') {
    const batchContent = `@echo off
setlocal EnableDelayedExpansion
cd /d "%~dp0"
echo === URVault startup ===

:: Update LOCAL_IP in .env
for /f "tokens=2 delims=:" %%a in ('ipconfig ^| findstr /C:"IPv4 Address" ^| findstr /V "169.254"') do (
    set "RAW_IP=%%a" & set "CIP=!RAW_IP: =!" & goto :ipfound
)
:ipfound
if defined CIP (
    node -e "try{const fs=require('fs');let e=fs.readFileSync('.env','utf8');e=e.replace(/^LOCAL_IP=.*/m,'LOCAL_IP=!CIP!');fs.writeFileSync('.env',e);}catch(e){}"
    echo Local IP: !CIP!
)

:: Read PORT from .env
for /f "tokens=1,2 delims==" %%k in (.env) do if "%%k"=="PORT" set PORT=%%l
if not defined PORT set PORT=5173

:: Start Cloudflare tunnel watchdog
if exist cloudflared.exe (
    echo Starting remote access tunnel...
    start /B powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0tunnel-watchdog.ps1" -Port !PORT! -EnvFile "%~dp0.env"
) else (
    echo cloudflared.exe not found -- remote access unavailable.
)

:: Ensure MySQL is running
echo Ensuring database is running...
powershell -NoProfile -Command "try{$s=Get-Service 'MySQL*' -EA SilentlyContinue | Select-Object -First 1; if($s -and $s.Status -ne 'Running'){Start-Service $s.Name -EA SilentlyContinue}}catch{}" >nul 2>&1

:: Run migrations
echo Running database migrations...
node run-migrations.js 2>nul || echo Migrations skipped.

:: Start server
echo Starting server...
node dist\\server.js
`;
    fs.writeFileSync('start-server.bat', batchContent);

    const watchdogContent = `param([string]$Port, [string]$EnvFile)
while ($true) {
    $log = [System.IO.Path]::GetTempFileName()
    $proc = Start-Process -FilePath '.\\cloudflared.exe' \`
        -ArgumentList "tunnel --url http://localhost:$Port --no-autoupdate" \`
        -RedirectStandardError $log -PassThru -NoNewWindow
    $url = $null
    for ($i = 0; $i -lt 60; $i++) {
        Start-Sleep -Seconds 1
        $txt = try { Get-Content $log -Raw -ErrorAction Stop } catch { '' }
        if ($txt -match 'https://[a-z0-9-]+\\.trycloudflare\\.com') {
            $url = $Matches[0]; break
        }
    }
    if ($url) {
        $e = Get-Content $EnvFile -Raw
        $e = $e -replace '(?m)^EXTERNAL_URL=.*', "EXTERNAL_URL=$url"
        $e = $e -replace '(?m)^# EXTERNAL_URL=.*', "EXTERNAL_URL=$url"
        Set-Content $EnvFile $e -NoNewline
        Write-Host "Remote access URL: $url"
        $lines = $e -split '\r?\n'
        $mu = ($lines | Where-Object { $_ -match '^MAILBOX_URL=' }) -replace '^MAILBOX_URL=',''
        $ms = ($lines | Where-Object { $_ -match '^MAILBOX_SECRET=' }) -replace '^MAILBOX_SECRET=',''
        $su = ($lines | Where-Object { $_ -match '^SERVER_UUID=' }) -replace '^SERVER_UUID=',''
        if ($mu -and $ms -and $su) {
            $body = ConvertTo-Json @{ uuid=$su; url=$url; secret=$ms }
            try { Invoke-RestMethod -Uri "$mu/api/register" -Method Post -Body $body -ContentType 'application/json' } catch {}
        }
    } else {
        Write-Host 'Warning: Tunnel URL not detected -- remote access unavailable this session.'
    }
    $proc | Wait-Process -ErrorAction SilentlyContinue
    Remove-Item $log -ErrorAction SilentlyContinue
    Write-Host 'Tunnel exited -- restarting in 10s...'
    Start-Sleep -Seconds 10
}
`;
    fs.writeFileSync('tunnel-watchdog.ps1', watchdogContent);
    log('✅ Created start-server.bat + tunnel-watchdog.ps1', 'success');

  } else {
    const shellContent = `#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
echo "=== URVault startup ==="

# Update LOCAL_IP in .env
if command -v ip >/dev/null 2>&1; then
  CURRENT_IP=$(ip route get 1.1.1.1 2>/dev/null | awk '{print $7; exit}')
elif command -v route >/dev/null 2>&1; then
  CURRENT_IP=$(route get default 2>/dev/null | awk '/interface:/{print $2}' | xargs -I{} ipconfig getifaddr {} 2>/dev/null)
fi
if [ -n "$CURRENT_IP" ]; then
  sed -i.bak "s/^LOCAL_IP=.*/LOCAL_IP=$CURRENT_IP/" .env 2>/dev/null && rm -f .env.bak
  echo "Local IP: $CURRENT_IP"
fi

# Cloudflare tunnel watchdog
CF_CMD=""
if [ -f "./cloudflared" ]; then CF_CMD="./cloudflared"
elif command -v cloudflared >/dev/null 2>&1; then CF_CMD="cloudflared"; fi

register_with_mailbox() {
  local URL="$1"
  local MAILBOX_URL=$(grep -oP "^MAILBOX_URL=\K.*" .env 2>/dev/null)
  local MAILBOX_SECRET=$(grep -oP "^MAILBOX_SECRET=\K.*" .env 2>/dev/null)
  local SERVER_UUID=$(grep -oP "^SERVER_UUID=\K.*" .env 2>/dev/null)
  if [ -n "$MAILBOX_URL" ] && [ -n "$MAILBOX_SECRET" ] && [ -n "$SERVER_UUID" ]; then
    curl -s -X POST "$MAILBOX_URL/api/register" \
      -H "Content-Type: application/json" \
      -d "{\"uuid\":\"$SERVER_UUID\",\"url\":\"$URL\",\"secret\":\"$MAILBOX_SECRET\"}" 2>/dev/null || true
  fi
}

start_tunnel() {
  local CF_LOG=$(mktemp /tmp/cf_tunnel.XXXXXX)
  "$CF_CMD" tunnel --url "http://localhost:\${PORT:-5173}" --no-autoupdate 2>"$CF_LOG" &
  local CF_PID=$!
  local TUNNEL_URL=""
  for i in $(seq 1 60); do
    TUNNEL_URL=$(grep -oP "https://[a-z0-9-]+\\.trycloudflare\\.com" "$CF_LOG" 2>/dev/null | head -1)
    [ -n "$TUNNEL_URL" ] && break
    sleep 1
  done
  if [ -n "$TUNNEL_URL" ]; then
    sed -i.bak -e "s|^EXTERNAL_URL=.*|EXTERNAL_URL=$TUNNEL_URL|" .env 2>/dev/null && rm -f .env.bak
    echo "Remote access URL: $TUNNEL_URL"
    register_with_mailbox "$TUNNEL_URL"
  else
    echo "Warning: Tunnel URL not detected -- remote access unavailable this session."
  fi
  wait "$CF_PID"
  rm -f "$CF_LOG"
}

if [ -n "$CF_CMD" ]; then
  (while true; do start_tunnel; echo "Tunnel exited -- restarting in 10s..."; sleep 10; done) &
  WATCHDOG_PID=$!
  trap "kill $WATCHDOG_PID 2>/dev/null" EXIT
else
  echo "cloudflared not found -- remote access unavailable."
fi

echo "Running database migrations..."
node run-migrations.js
echo "Starting server..."
exec node dist/server.js
`;
    fs.writeFileSync('start-server.sh', shellContent);
    fs.chmodSync('start-server.sh', '755');
    log('✅ Created start-server.sh', 'success');
  }
}

async function main() {
  // ── Terms of Service acceptance (skipped in --auto mode; platform installers handle it) ──
  if (!AUTO) {
    const _rl = readline.createInterface({ input: process.stdin, output: process.stdout });
    console.log('');
    console.log('='.repeat(60));
    console.log('  URVault Server — Terms of Service & License');
    console.log('='.repeat(60));
    console.log('');
    console.log('By installing URVault Server you agree that:');
    console.log('  • Software is provided AS IS with no warranty');
    console.log('  • URVault LLC is not liable for data loss or server failure');
    console.log('  • You are responsible for your server security');
    console.log('  • Governing law: State of Delaware, USA');
    console.log('');
    console.log('Full terms:     https://urvault.net/terms');
    console.log('Privacy policy: https://urvault.net/privacy');
    console.log('');
    const _accepted = await new Promise(resolve => _rl.question('Do you accept the Terms of Service? (yes/no): ', resolve));
    _rl.close();
    if (_accepted.trim().toLowerCase() !== 'yes') {
      console.log('');
      console.log('Installation cancelled. You must accept the Terms to install URVault.');
      process.exit(0);
    }
    console.log('');
  }
  // ─────────────────────────────────────────────────────────────────────────


  if (!AUTO) {
    console.clear();
    log('╔════════════════════════════════════════════════════════════╗', 'info');
    log('║            ☁  URVault Server Installer  ☁               ║', 'success');
    log('╚════════════════════════════════════════════════════════════╝', 'info');
    log('');
  } else {
    log('URVault auto-installer starting...', 'info');
  }

  const platform = detectPlatform();
  if (!AUTO) log(`🖥️  Platform: ${platform}`, 'info');

  checkNodeVersion();

  const mysqlInstalled = checkMySQL();
  if (!mysqlInstalled) {
    if (AUTO) {
      log('⚠️  MySQL not found -- continuing (database setup may fail)', 'warning');
    } else {
      log('\n⚠️  MySQL is required but not detected.', 'warning');
      const continueAnyway = await question('\nContinue anyway? (y/n): ');
      if (continueAnyway.toLowerCase() !== 'y') {
        log('Installation cancelled.', 'info');
        rl.close();
        process.exit(0);
      }
    }
  }

  const mysqlAuth = detectMySQLAuth();

  // Prompt for MySQL root password only in interactive mode and only when needed
  if (mysqlAuth.needsPassword && !mysqlAuth.rootPassword) {
    if (AUTO) {
      log('⚠️  MySQL root password not available in auto mode -- database setup may fail', 'warning');
      mysqlAuth.rootPassword = '';
    } else {
      log('\n🔑 MySQL root password required to create the database.', 'info');
      mysqlAuth.rootPassword = await question('MySQL root password: ');
    }
  }

  // --- Config ---
  const realUser = process.env.SUDO_USER || process.env.USER || os.userInfo().username;
  const homeDir = process.env.SUDO_USER ? `/home/${process.env.SUDO_USER}` : os.homedir();
  const defaultStorage = platform === 'windows'
    ? 'C:\\ProgramData\\URVault\\backups'
    : path.join(homeDir, 'urvault-backups');

  const config = {};

  if (!AUTO && !mysqlInstalled) {
    log('\n' + '─'.repeat(60), 'info');
    log('📋 DATABASE CONFIGURATION', 'info');
    log('─'.repeat(60), 'info');
  }

  config.DB_HOST     = await prompt('MySQL Host [localhost]: ', 'localhost');
  config.DB_PORT     = await prompt('MySQL Port [3306]: ', '3306');
  config.DB_USER     = process.env.URVAULT_DB_USER     || await prompt('MySQL User [urvault]: ', 'urvault');
  config.DB_PASSWORD = process.env.URVAULT_DB_PASSWORD || (AUTO ? generateSecret(16) : (await question('MySQL Password: ') || generateSecret(16)));
  config.DB_NAME     = process.env.URVAULT_DB_NAME     || await prompt('Database Name [urvault]: ', 'urvault');

  validateIdentifier(config.DB_USER, 'MySQL User');
  validateIdentifier(config.DB_NAME, 'Database Name');

  config.LOCAL_IP    = getLocalIP();
  config.PORT        = process.env.URVAULT_PORT || await prompt('\nServer Port [5173]: ', '5173');
  config.STORAGE_PATH = await prompt(`\nBackup Storage Path [${defaultStorage}]: `, defaultStorage);

  config.JWT_SECRET      = generateSecret(32);
  config.ENCRYPTION_KEY  = generateSecret(32);
  config.ADMIN_KEY       = generateSecret(32);
  config.MAILBOX_SECRET  = generateSecret(32);
  config.SERVER_UUID     = crypto.randomUUID();

  if (!AUTO) {
    log('\n' + '─'.repeat(60), 'info');
    log('📋 SUMMARY', 'info');
    log('─'.repeat(60), 'info');
    log(`\nDatabase: ${config.DB_NAME} on ${config.DB_HOST}:${config.DB_PORT}`, 'info');
    log(`Server:   http://${config.LOCAL_IP}:${config.PORT}`, 'info');
    log(`Storage:  ${config.STORAGE_PATH}`, 'info');

    const confirm = await question('\nProceed with installation? (y/n): ');
    if (confirm.toLowerCase() !== 'y') {
      log('Installation cancelled.', 'info');
      rl.close();
      process.exit(0);
    }
  }

  // --- Install ---
  log('\n' + '='.repeat(60), 'info');
  log('Installing...', 'success');
  log('='.repeat(60), 'info');

  createEnvFile(config);
  createStorageDirectory(config.STORAGE_PATH);

  if (!fs.existsSync('node_modules')) {
    log('\n📦 Installing dependencies...', 'info');
    execSync('npm install --omit=dev', { stdio: 'inherit' });
    log('✅ Dependencies installed', 'success');
  }

  const dbOk = await createDatabase(config, mysqlAuth);
  if (!dbOk) {
    log('\n❌ Database setup failed', 'error');
    rl.close();
    process.exit(1);
  }

  runMigrations();
  createStartupScripts(platform, config);
  setupMdns(config);
  await downloadCloudflared();

  log('\n' + '='.repeat(60), 'success');
  log('✅ INSTALLATION COMPLETE', 'success');
  log('='.repeat(60), 'success');

  if (!AUTO) {
    log('\n🚀 To start the server:', 'info');
    if (platform === 'windows') {
      log('   Double-click: start-server.bat', 'success');
    } else {
      log('   Run: ./start-server.sh', 'success');
    }
    log('\n📱 Next steps:', 'info');
    log('   1. Start the server', 'info');
    log(`   2. Open: http://localhost:${config.PORT}`, 'info');
    log('   3. Complete the admin setup wizard', 'info');
    log('   4. Pair your phone with the URVault app', 'info');
  }

  log('');
  rl.close();
}

main().catch(error => {
  log('\n❌ Installation failed: ' + error.message, 'error');
  rl.close();
  process.exit(1);
});
