#!/usr/bin/env node
/**
 * Oikos Web Installer — temporary setup server.
 * Zero npm dependencies. Node.js built-ins only.
 * Run: node tools/installer/install-server.js
 */

import http from 'node:http';
import { readFileSync, writeFileSync } from 'node:fs';
import { spawn } from 'node:child_process';
import { randomBytes } from 'node:crypto';
import { resolve, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __dirname = dirname(fileURLToPath(import.meta.url));
const PROJECT_ROOT = resolve(__dirname, '..', '..');
const PORT = 8090;
const IDLE_TIMEOUT_MS = 30 * 60 * 1000;

let idleTimer = null;

function resetIdle(server) {
  clearTimeout(idleTimer);
  idleTimer = setTimeout(() => {
    console.log('\nIdle timeout — shutting down installer server.');
    server.close(() => process.exit(0));
  }, IDLE_TIMEOUT_MS);
}

function readBody(req) {
  return new Promise((resolve, reject) => {
    let data = '';
    req.on('data', chunk => { data += chunk; });
    req.on('end', () => {
      try { resolve(JSON.parse(data || '{}')); } catch { reject(new Error('Invalid JSON')); }
    });
    req.on('error', reject);
  });
}

function json(res, status, body) {
  const payload = JSON.stringify(body);
  res.writeHead(status, {
    'Content-Type': 'application/json',
    'Content-Length': Buffer.byteLength(payload),
    'Cache-Control': 'no-store',
  });
  res.end(payload);
}

const ENV_CATALOG = [
  { key: 'SESSION_SECRET',              type: 'auto',    label: 'Session Secret',           required: true  },
  { key: 'DB_ENCRYPTION_KEY',           type: 'auto',    label: 'Database Encryption Key',  required: true  },
  { key: 'OPENWEATHER_API_KEY',         type: 'user',    label: 'OpenWeather API Key',       required: false },
  { key: 'OPENWEATHER_CITY',            type: 'default', label: 'City',                     default: 'Berlin' },
  { key: 'OPENWEATHER_UNITS',           type: 'default', label: 'Units',                    default: 'metric' },
  { key: 'OPENWEATHER_LANG',            type: 'default', label: 'Language',                 default: 'de' },
  { key: 'GOOGLE_CLIENT_ID',            type: 'user',    label: 'Google Client ID',          required: false },
  { key: 'GOOGLE_CLIENT_SECRET',        type: 'user',    label: 'Google Client Secret',      required: false },
  { key: 'GOOGLE_REDIRECT_URI',         type: 'user',    label: 'Google Redirect URI',       required: false },
  { key: 'APPLE_USERNAME',              type: 'user',    label: 'Apple ID (email)',           required: false },
  { key: 'APPLE_APP_SPECIFIC_PASSWORD', type: 'user',    label: 'App-Specific Password',     required: false },
  { key: 'SYNC_INTERVAL_MINUTES',       type: 'default', label: 'Sync Interval (minutes)',  default: '15' },
];

async function route(req, res, server) {
  resetIdle(server);
  const url = new URL(req.url, `http://localhost:${PORT}`);

  if (req.method === 'GET' && url.pathname === '/') {
    const html = readFileSync(resolve(__dirname, 'install.html'));
    res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8', 'Content-Length': html.length, 'Cache-Control': 'no-store' });
    res.end(html);
    return;
  }

  if (req.method === 'GET' && url.pathname === '/api/defaults') {
    return json(res, 200, { catalog: ENV_CATALOG });
  }

  if (req.method === 'POST' && url.pathname === '/api/generate-secret') {
    return json(res, 200, { secret: randomBytes(32).toString('hex') });
  }

  if (req.method === 'POST' && url.pathname === '/api/save-env') {
    try {
      const body = await readBody(req);
      const lines = ['# Generated by Oikos Installer'];
      for (const [key, value] of Object.entries(body.env || {})) {
        if (value != null && value !== '') lines.push(`${key}=${value}`);
      }
      writeFileSync(resolve(PROJECT_ROOT, '.env'), lines.join('\n') + '\n', 'utf8');
      return json(res, 200, { ok: true });
    } catch (err) {
      return json(res, 500, { error: err.message });
    }
  }

  if (req.method === 'POST' && url.pathname === '/api/start') {
    const child = spawn('docker', ['compose', 'up', '-d'], { cwd: PROJECT_ROOT, stdio: 'pipe' });
    child.on('error', err => console.error('docker compose error:', err.message));
    return json(res, 200, { ok: true });
  }

  if (req.method === 'GET' && url.pathname === '/api/status') {
    return new Promise(resolvePromise => {
      const inspect = spawn('docker', ['inspect', '--format', '{{.State.Health.Status}}', 'oikos'], { stdio: 'pipe' });
      let out = '';
      inspect.stdout.on('data', d => { out += d.toString().trim(); });
      inspect.on('close', code => {
        if (code === 0 && out === 'healthy') return resolvePromise(json(res, 200, { status: 'running' }));
        if (code === 0 && (out === 'starting' || out === 'unhealthy')) return resolvePromise(json(res, 200, { status: 'starting' }));
        const state = spawn('docker', ['inspect', '--format', '{{.State.Status}}', 'oikos'], { stdio: 'pipe' });
        let stateOut = '';
        state.stdout.on('data', d => { stateOut += d.toString().trim(); });
        state.on('close', () => {
          if (stateOut === 'running') return resolvePromise(json(res, 200, { status: 'starting' }));
          const logs = spawn('docker', ['compose', 'logs', '--tail', '30'], { cwd: PROJECT_ROOT, stdio: 'pipe' });
          let logsOut = '';
          logs.stdout.on('data', d => { logsOut += d.toString(); });
          logs.stderr.on('data', d => { logsOut += d.toString(); });
          logs.on('close', () => resolvePromise(json(res, 200, { status: 'error', logs: logsOut })));
        });
      });
    });
  }

  if (req.method === 'POST' && url.pathname === '/api/create-admin') {
    try {
      const body = await readBody(req);
      const oikosPort = body.port || 3000;
      const payload = JSON.stringify({
        username: body.username,
        display_name: body.display_name,
        password: body.password,
      });

      const result = await new Promise((resolve, reject) => {
        const opts = {
          hostname: 'localhost', port: oikosPort,
          path: '/api/v1/auth/setup', method: 'POST',
          headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
        };
        const proxyReq = http.request(opts, proxyRes => {
          let data = '';
          proxyRes.on('data', d => { data += d; });
          proxyRes.on('end', () => {
            try { resolve({ status: proxyRes.statusCode, body: JSON.parse(data) }); }
            catch { resolve({ status: proxyRes.statusCode, body: { error: 'Invalid response from Oikos' } }); }
          });
        });
        proxyReq.on('error', reject);
        proxyReq.write(payload);
        proxyReq.end();
      });

      json(res, result.status, result.body);

      if (result.status === 201 || result.status === 403) {
        setTimeout(() => {
          console.log('\nSetup complete — shutting down installer server.');
          server.close(() => process.exit(0));
        }, 2000);
      }
    } catch (err) {
      json(res, 500, { error: err.message });
    }
    return;
  }

  json(res, 404, { error: 'Not found' });
}

const server = http.createServer((req, res) => {
  route(req, res, server).catch(err => json(res, 500, { error: err.message }));
});

server.listen(PORT, '127.0.0.1', () => {
  console.log(`\n  Oikos Web Installer`);
  console.log(`  Open: http://localhost:${PORT}\n`);
  resetIdle(server);
});
