/**
 * CLI Router for Onebox
 */

import { logger } from './logging.ts';
import { projectInfo } from './info.ts';
import { getErrorMessage } from './utils/error.ts';
import { Onebox } from './classes/onebox.ts';
import { OneboxDaemon } from './classes/daemon.ts';
import { OneboxSystemd } from './classes/systemd.ts';

export async function runCli(): Promise<void> {
  const args = Deno.args;

  if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
    printHelp();
    return;
  }

  if (args.includes('--version') || args.includes('-v')) {
    console.log(`${projectInfo.name} v${projectInfo.version}`);
    return;
  }

  const command = args[0];
  const subcommand = args[1];

  try {
    // === LIGHTWEIGHT COMMANDS (no init()) ===
    if (command === 'systemd') {
      await handleSystemdCommand(subcommand, args.slice(2));
      return;
    }

    if (command === 'upgrade') {
      await handleUpgradeCommand();
      return;
    }

    // === HEAVY COMMANDS (require full init()) ===

    // Server command has special handling (doesn't shut down)
    if (command === 'server') {
      const onebox = new Onebox();
      await onebox.init();
      await handleServerCommand(onebox, args.slice(1));
      // Server command runs forever (or until Ctrl+C), so this never returns
      return;
    }

    // Initialize Onebox
    const onebox = new Onebox();
    await onebox.init();

    // Route commands
    switch (command) {
      case 'service':
        await handleServiceCommand(onebox, subcommand, args.slice(2));
        break;

      case 'registry':
        await handleRegistryCommand(onebox, subcommand, args.slice(2));
        break;

      case 'dns':
        await handleDnsCommand(onebox, subcommand, args.slice(2));
        break;

      case 'ssl':
        await handleSslCommand(onebox, subcommand, args.slice(2));
        break;

      case 'nginx':
        await handleNginxCommand(onebox, subcommand, args.slice(2));
        break;

      case 'config':
        await handleConfigCommand(onebox, subcommand, args.slice(2));
        break;

      case 'status':
        await handleStatusCommand(onebox);
        break;

      default:
        logger.error(`Unknown command: ${command}`);
        printHelp();
        Deno.exit(1);
    }

    // Cleanup
    await onebox.shutdown();
  } catch (error) {
    logger.error(getErrorMessage(error));
    Deno.exit(1);
  }
}

// Service commands
async function handleServiceCommand(onebox: Onebox, subcommand: string, args: string[]) {
  switch (subcommand) {
    case 'add': {
      const name = args[0];
      const image = getArg(args, '--image');
      const domain = getArg(args, '--domain');
      const port = parseInt(getArg(args, '--port') || '80', 10);
      const envArgs = args.filter((a) => a.startsWith('--env=')).map((a) => a.slice(6));
      const envVars: Record<string, string> = {};
      for (const env of envArgs) {
        const [key, value] = env.split('=');
        envVars[key] = value;
      }

      await onebox.services.deployService({ name, image, port, domain, envVars });
      break;
    }

    case 'remove':
      await onebox.services.removeService(args[0]);
      break;

    case 'start':
      await onebox.services.startService(args[0]);
      break;

    case 'stop':
      await onebox.services.stopService(args[0]);
      break;

    case 'restart':
      await onebox.services.restartService(args[0]);
      break;

    case 'list': {
      const services = onebox.services.listServices();
      logger.table(
        ['Name', 'Image', 'Status', 'Domain', 'Port'],
        services.map((s) => [s.name, s.image, s.status, s.domain || '-', s.port.toString()])
      );
      break;
    }

    case 'logs': {
      const logs = await onebox.services.getServiceLogs(args[0]);
      console.log(logs);
      break;
    }

    default:
      logger.error(`Unknown service subcommand: ${subcommand}`);
  }
}

// Registry commands
async function handleRegistryCommand(onebox: Onebox, subcommand: string, args: string[]) {
  switch (subcommand) {
    case 'add': {
      const url = getArg(args, '--url');
      const username = getArg(args, '--username');
      const password = getArg(args, '--password');
      await onebox.registries.addRegistry(url, username, password);
      break;
    }

    case 'remove':
      await onebox.registries.removeRegistry(getArg(args, '--url'));
      break;

    case 'list': {
      const registries = onebox.registries.listRegistries();
      logger.table(
        ['URL', 'Username'],
        registries.map((r) => [r.url, r.username])
      );
      break;
    }

    default:
      logger.error(`Unknown registry subcommand: ${subcommand}`);
  }
}

// DNS commands
async function handleDnsCommand(onebox: Onebox, subcommand: string, args: string[]) {
  switch (subcommand) {
    case 'add':
      await onebox.dns.addDNSRecord(args[0]);
      break;

    case 'remove':
      await onebox.dns.removeDNSRecord(args[0]);
      break;

    case 'list': {
      const records = onebox.dns.listDNSRecords();
      logger.table(
        ['Domain', 'Type', 'Value'],
        records.map((r) => [r.domain, r.type, r.value])
      );
      break;
    }

    case 'sync':
      await onebox.dns.syncFromCloudflare();
      break;

    default:
      logger.error(`Unknown dns subcommand: ${subcommand}`);
  }
}

// SSL commands
async function handleSslCommand(onebox: Onebox, subcommand: string, args: string[]) {
  switch (subcommand) {
    case 'renew':
      if (args[0]) {
        await onebox.ssl.renewCertificate(args[0]);
      } else {
        await onebox.ssl.renewExpiring();
      }
      break;

    case 'list': {
      const certs = onebox.ssl.listCertificates();
      logger.table(
        ['Domain', 'Expiry', 'Issuer'],
        certs.map((c) => [c.domain, new Date(c.expiryDate).toISOString(), c.issuer])
      );
      break;
    }

    case 'force-renew':
      await onebox.ssl.renewCertificate(args[0]);
      break;

    default:
      logger.error(`Unknown ssl subcommand: ${subcommand}`);
  }
}

// Reverse proxy commands (formerly nginx commands)
async function handleNginxCommand(onebox: Onebox, subcommand: string, _args: string[]) {
  switch (subcommand) {
    case 'reload':
      // Reload routes and certificates
      await onebox.reverseProxy.reloadRoutes();
      await onebox.reverseProxy.reloadCertificates();
      logger.success('Reverse proxy configuration reloaded');
      break;

    case 'test':
      // Verify reverse proxy is running
      const proxyStatus = onebox.reverseProxy.getStatus();
      if (proxyStatus.http.running || proxyStatus.https.running) {
        logger.success('Reverse proxy is running');
        logger.info(`HTTP: ${proxyStatus.http.running ? 'active' : 'inactive'} (port ${proxyStatus.http.port})`);
        logger.info(`HTTPS: ${proxyStatus.https.running ? 'active' : 'inactive'} (port ${proxyStatus.https.port})`);
        logger.info(`Routes: ${proxyStatus.routes}, Certificates: ${proxyStatus.https.certificates}`);
      } else {
        logger.error('Reverse proxy is not running');
      }
      break;

    case 'status': {
      const status = onebox.reverseProxy.getStatus();
      logger.info(`Reverse proxy status:`);
      logger.info(`  HTTP: ${status.http.running ? 'running' : 'stopped'} (port ${status.http.port})`);
      logger.info(`  HTTPS: ${status.https.running ? 'running' : 'stopped'} (port ${status.https.port})`);
      logger.info(`  Routes: ${status.routes}`);
      logger.info(`  Certificates: ${status.https.certificates}`);
      break;
    }

    default:
      logger.error(`Unknown nginx subcommand: ${subcommand}`);
  }
}

// Server command
async function handleServerCommand(onebox: Onebox, args: string[]) {
  const ephemeral = args.includes('--ephemeral');
  const port = parseInt(getArg(args, '--port') || '3000', 10);
  const monitor = args.includes('--monitor') || ephemeral; // ephemeral includes monitoring

  if (ephemeral) {
    // Ensure no daemon is running
    try {
      await OneboxDaemon.ensureNoDaemon();
    } catch (error) {
      logger.error('Cannot start in ephemeral mode: Daemon is already running');
      logger.info('Stop the daemon first: onebox systemd stop');
      logger.info('Or run without --ephemeral to use the existing daemon');
      Deno.exit(1);
    }
  }

  logger.info('Starting Onebox server...');

  // Start OpsServer (serves new UI + TypedRequest API)
  await onebox.opsServer.start(port);

  // Start monitoring if requested
  if (monitor) {
    logger.info('Starting monitoring loop...');
    onebox.daemon.startMonitoring();
  }

  logger.success(`Onebox server running on http://localhost:${port}`);
  if (ephemeral) {
    logger.info('Running in ephemeral mode - Press Ctrl+C to stop');
  } else {
    logger.info('Press Ctrl+C to stop');
  }

  // Setup signal handlers
  const shutdown = async () => {
    logger.info('Shutting down...');
    if (monitor) {
      onebox.daemon.stopMonitoring();
    }
    await onebox.opsServer.stop();
    await onebox.shutdown();
    Deno.exit(0);
  };

  Deno.addSignalListener('SIGINT', shutdown);
  Deno.addSignalListener('SIGTERM', shutdown);

  // Keep alive
  while (true) {
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
}

// Systemd service commands (lightweight — no Onebox init)
async function handleSystemdCommand(subcommand: string, _args: string[]) {
  const systemd = new OneboxSystemd();

  switch (subcommand) {
    case 'enable':
      await systemd.enable();
      break;

    case 'disable':
      await systemd.disable();
      break;

    case 'start':
      await systemd.start();
      break;

    case 'stop':
      await systemd.stop();
      break;

    case 'status': {
      const status = await systemd.getStatus();
      logger.info(`Service status: ${status}`);
      break;
    }

    case 'logs':
      await systemd.showLogs();
      break;

    case 'start-daemon': {
      // This is what systemd's ExecStart calls — full init + daemon loop
      const onebox = new Onebox();
      await onebox.init();
      await onebox.daemon.start();
      // start() blocks (keepAlive loop) until SIGTERM/SIGINT
      break;
    }

    default:
      logger.error(`Unknown systemd subcommand: ${subcommand}`);
      logger.info('Available: enable, disable, start, stop, status, logs');
  }
}

// Config commands
async function handleConfigCommand(onebox: Onebox, subcommand: string, args: string[]) {
  switch (subcommand) {
    case 'show': {
      const settings = onebox.database.getAllSettings();
      logger.table(
        ['Key', 'Value'],
        Object.entries(settings).map(([k, v]) => [k, v])
      );
      break;
    }

    case 'set':
      onebox.database.setSetting(args[0], args[1]);
      logger.success(`Setting ${args[0]} updated`);
      break;

    default:
      logger.error(`Unknown config subcommand: ${subcommand}`);
  }
}

// Status command
async function handleStatusCommand(onebox: Onebox) {
  const status = await onebox.getSystemStatus();
  console.log(JSON.stringify(status, null, 2));
}

// Upgrade command - self-update onebox to latest version
async function handleUpgradeCommand(): Promise<void> {
  // Check if running as root
  if (Deno.uid() !== 0) {
    logger.error('This command must be run as root to upgrade Onebox.');
    logger.info('Try: sudo onebox upgrade');
    Deno.exit(1);
  }

  logger.info('Checking for updates...');

  try {
    // Get current version
    const currentVersion = projectInfo.version;

    // Fetch latest version from Gitea API
    const apiUrl = 'https://code.foss.global/api/v1/repos/serve.zone/onebox/releases/latest';
    const curlCmd = new Deno.Command('curl', {
      args: ['-sSL', apiUrl],
      stdout: 'piped',
      stderr: 'piped',
    });
    const curlResult = await curlCmd.output();
    const response = new TextDecoder().decode(curlResult.stdout);
    const release = JSON.parse(response);
    const latestVersion = release.tag_name as string; // e.g., "v1.11.0"

    // Normalize versions for comparison (ensure both have "v" prefix)
    const normalizedCurrent = currentVersion.startsWith('v')
      ? currentVersion
      : `v${currentVersion}`;
    const normalizedLatest = latestVersion.startsWith('v')
      ? latestVersion
      : `v${latestVersion}`;

    console.log(`  Current version: ${normalizedCurrent}`);
    console.log(`  Latest version:  ${normalizedLatest}`);
    console.log('');

    // Compare normalized versions
    if (normalizedCurrent === normalizedLatest) {
      logger.success('Already up to date!');
      return;
    }

    logger.info(`New version available: ${latestVersion}`);
    logger.info('Downloading and installing...');
    console.log('');

    // Download and run the install script
    const installUrl = 'https://code.foss.global/serve.zone/onebox/raw/branch/main/install.sh';
    const installCmd = new Deno.Command('bash', {
      args: ['-c', `curl -sSL ${installUrl} | bash`],
      stdin: 'inherit',
      stdout: 'inherit',
      stderr: 'inherit',
    });
    const installResult = await installCmd.output();

    if (!installResult.success) {
      logger.error('Upgrade failed');
      Deno.exit(1);
    }

    console.log('');
    logger.success(`Upgraded to ${latestVersion}`);
  } catch (error) {
    logger.error(`Upgrade failed: ${getErrorMessage(error)}`);
    Deno.exit(1);
  }
}

// Helpers
function getArg(args: string[], flag: string): string {
  const arg = args.find((a) => a.startsWith(`${flag}=`));
  return arg ? arg.split('=')[1] : '';
}

function printHelp(): void {
  console.log(`
Onebox v${projectInfo.version} - Self-hosted container platform

Usage: onebox <command> [options]

Commands:
  server [--ephemeral] [--port <port>] [--monitor]
      Start HTTP server
      --ephemeral: Run in foreground (development mode, checks no daemon running)
      --port: HTTP server port (default: 3000)
      --monitor: Enable monitoring loop (included with --ephemeral)

  service add <name> --image <image> [--domain <domain>] [--port <port>] [--env KEY=VALUE]
  service remove <name>
  service start <name>
  service stop <name>
  service restart <name>
  service list
  service logs <name>

  registry add --url <url> --username <user> --password <pass>
  registry remove --url <url>
  registry list

  dns add <domain>
  dns remove <domain>
  dns list
  dns sync

  ssl renew [domain]
  ssl list
  ssl force-renew <domain>

  nginx reload
  nginx test
  nginx status

  systemd enable             Install and enable systemd service
  systemd disable            Stop, disable, and remove systemd service
  systemd start              Start onebox via systemctl
  systemd stop               Stop onebox via systemctl
  systemd status             Show systemd service status
  systemd logs               Follow service logs (journalctl)

  config show
  config set <key> <value>

  status

  upgrade
      Upgrade Onebox to the latest version (requires root)

Options:
  --help, -h     Show this help message
  --version, -v  Show version
  --debug        Enable debug logging

Development Workflow:
  deno task dev                        # Start ephemeral server with monitoring
  onebox service add ...               # In another terminal

Production Workflow:
  onebox systemd enable                # Install and enable systemd service
  onebox systemd start                 # Start via systemctl
  onebox service add ...               # CLI manages services

Examples:
  onebox server --ephemeral            # Start dev server
  onebox service add myapp --image nginx:latest --domain app.example.com --port 80
  onebox registry add --url registry.example.com --username user --password pass
  onebox systemd enable
  onebox systemd start
`);
}
