/**
 * DSS Server Manager
 *
 * Manages the Python server subprocess.
 */

import { spawn, ChildProcess } from 'child_process';
import { existsSync, writeFileSync, readFileSync, unlinkSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

interface ServerOptions {
  port: number;
  dev: boolean;
  cwd: string;
}

let serverProcess: ChildProcess | null = null;

export function getPythonPath(): string {
  // Check for bundled Python venv in npm package (dist/lib -> ../../python/venv)
  const bundledPython = join(__dirname, '../../python/venv/bin/python3');
  if (existsSync(bundledPython)) {
    return bundledPython;
  }

  // Development path (src/lib -> ../../python/venv)
  const devPython = join(__dirname, '../../../python/venv/bin/python3');
  if (existsSync(devPython)) {
    return devPython;
  }

  return 'python3';
}

export function getServerPath(): string {
  // Check for bundled server or use from package
  const bundledServer = join(__dirname, '../../python/api/server.py');
  if (existsSync(bundledServer)) {
    return dirname(bundledServer);
  }

  // Fall back to relative path from CLI (cli/dist/lib -> ../../../../tools/api)
  const devServer = join(__dirname, '../../../../tools/api');
  if (existsSync(join(devServer, 'server.py'))) {
    return devServer;
  }

  // Also check development src path (cli/src/lib -> ../../../../tools/api)
  const srcServer = join(__dirname, '../../../tools/api');
  if (existsSync(join(srcServer, 'server.py'))) {
    return srcServer;
  }

  throw new Error('Could not find DSS server. Run from the design-system-swarm directory or install the full package.');
}

export function getPidFile(cwd: string): string {
  return join(cwd, '.dss', 'dss.pid');
}

export function getLogFile(cwd: string): string {
  return join(cwd, '.dss', 'dss.log');
}

export function isServerRunning(cwd: string): boolean {
  const pidFile = getPidFile(cwd);
  if (!existsSync(pidFile)) {
    return false;
  }

  try {
    const pid = parseInt(readFileSync(pidFile, 'utf-8').trim(), 10);
    // Check if process exists
    process.kill(pid, 0);
    return true;
  } catch {
    // Process doesn't exist, clean up stale PID file
    try {
      unlinkSync(pidFile);
    } catch {
      // Ignore
    }
    return false;
  }
}

export function getServerPid(cwd: string): number | null {
  const pidFile = getPidFile(cwd);
  if (!existsSync(pidFile)) {
    return null;
  }

  try {
    return parseInt(readFileSync(pidFile, 'utf-8').trim(), 10);
  } catch {
    return null;
  }
}

export async function startServer(options: ServerOptions): Promise<ChildProcess> {
  const { port, dev, cwd } = options;

  if (isServerRunning(cwd)) {
    throw new Error(`Server is already running (PID: ${getServerPid(cwd)})`);
  }

  const pythonPath = getPythonPath();
  const serverDir = getServerPath();

  // Build command args
  const args = [
    '-m', 'uvicorn',
    'server:app',
    '--host', '0.0.0.0',
    '--port', String(port),
  ];

  if (dev) {
    args.push('--reload');
  }

  // Set environment
  const env = {
    ...process.env,
    PYTHONPATH: join(serverDir, '..'),
    PORT: String(port),
  };

  // Spawn server process
  serverProcess = spawn(pythonPath, args, {
    cwd: serverDir,
    env,
    stdio: dev ? 'inherit' : ['ignore', 'pipe', 'pipe'],
    detached: !dev,
  });

  if (!dev && serverProcess.pid) {
    // Write PID file
    const dssDir = join(cwd, '.dss');
    if (!existsSync(dssDir)) {
      const { mkdirSync } = await import('fs');
      mkdirSync(dssDir, { recursive: true });
    }

    writeFileSync(getPidFile(cwd), String(serverProcess.pid));

    // Write logs
    const logStream = await import('fs').then(fs =>
      fs.createWriteStream(getLogFile(cwd), { flags: 'a' })
    );

    serverProcess.stdout?.pipe(logStream);
    serverProcess.stderr?.pipe(logStream);

    // Detach from parent
    serverProcess.unref();
  }

  return serverProcess;
}

export async function stopServer(cwd: string): Promise<boolean> {
  const pid = getServerPid(cwd);
  if (!pid) {
    return false;
  }

  try {
    process.kill(pid, 'SIGTERM');
    unlinkSync(getPidFile(cwd));
    return true;
  } catch {
    return false;
  }
}

export async function waitForServer(port: number, timeout = 10000): Promise<boolean> {
  const start = Date.now();
  const url = `http://localhost:${port}/health`;

  while (Date.now() - start < timeout) {
    try {
      const response = await fetch(url);
      if (response.ok) {
        return true;
      }
    } catch {
      // Server not ready yet
    }
    await new Promise(resolve => setTimeout(resolve, 200));
  }

  return false;
}
