/**
 * Graceful Shutdown Handler
 *
 * Orchestrates clean shutdown of the coordination server.
 * Handles SIGTERM, SIGINT, SIGHUP with proper cleanup sequence.
 */

import type { CoordinationServer } from './index.js';
import type { ProcessManager } from '../process/index.js';
import type { LogManager } from '../logging/index.js';
import type { PreviewManager } from '../preview/index.js';

/** Timeout before force exit in milliseconds */
const SHUTDOWN_TIMEOUT_MS = 10000;

/**
 * Handles graceful shutdown of the coordination server.
 *
 * Shutdown sequence:
 * 1. Log shutdown initiation
 * 2. Stop accepting new connections
 * 3. Stop all managed processes
 * 4. Close all log file handles (not implemented yet in LogManager)
 * 5. Remove PID file
 * 6. Exit with code 0
 *
 * If cleanup takes longer than SHUTDOWN_TIMEOUT_MS, force exit with code 1.
 * Double SIGINT forces immediate exit.
 */
export class GracefulShutdown {
  private readonly server: CoordinationServer;
  private readonly processManager: ProcessManager;
  private readonly logManager: LogManager;
  private readonly previewManager?: PreviewManager;
  private isShuttingDown = false;
  private forceExitCount = 0;

  constructor(
    server: CoordinationServer,
    processManager: ProcessManager,
    logManager: LogManager,
    previewManager?: PreviewManager,
  ) {
    this.server = server;
    this.processManager = processManager;
    this.logManager = logManager;
    this.previewManager = previewManager;
  }

  /**
   * Installs signal handlers for graceful shutdown.
   * Call this after the server has started.
   */
  install(): void {
    // Handle SIGTERM (kill, docker stop)
    process.on('SIGTERM', () => this.handleSignal('SIGTERM'));

    // Handle SIGINT (Ctrl+C)
    process.on('SIGINT', () => this.handleSignal('SIGINT'));

    // Handle SIGHUP (terminal closed)
    process.on('SIGHUP', () => this.handleSignal('SIGHUP'));
  }

  /**
   * Handles a shutdown signal.
   * @param signal - The signal that triggered shutdown
   */
  private async handleSignal(signal: string): Promise<void> {
    // Handle double SIGINT for force exit
    if (signal === 'SIGINT' && this.isShuttingDown) {
      this.forceExitCount++;
      if (this.forceExitCount >= 1) {
        console.log('\nForce exit requested. Exiting immediately.');
        process.exit(1);
      }
    }

    // Only run shutdown sequence once
    if (this.isShuttingDown) {
      console.log(`\nAlready shutting down... Press Ctrl+C again to force exit.`);
      return;
    }

    this.isShuttingDown = true;
    console.log(`\nReceived ${signal}, shutting down gracefully...`);

    // Set timeout for force exit
    const forceExitTimer = setTimeout(() => {
      console.error('Shutdown timeout exceeded. Forcing exit.');
      process.exit(1);
    }, SHUTDOWN_TIMEOUT_MS);

    // Ensure timeout doesn't keep the process alive
    forceExitTimer.unref();

    try {
      await this.shutdown();
      clearTimeout(forceExitTimer);
      console.log('Shutdown complete.');
      process.exit(0);
    } catch (error) {
      clearTimeout(forceExitTimer);
      console.error('Error during shutdown:', (error as Error).message);
      process.exit(1);
    }
  }

  /**
   * Performs the shutdown sequence.
   */
  async shutdown(): Promise<void> {
    // Step 1: Stop the HTTP server (stops accepting new connections)
    console.log('  Stopping HTTP server...');
    await this.server.stop();

    // Step 2: Stop all managed processes
    console.log('  Stopping managed processes...');
    await this.processManager.stopAll();

    // Step 3: Stop all preview deployments
    if (this.previewManager) {
      console.log('  Stopping preview deployments...');
      await this.previewManager.stopAll();
    }

    // Step 4: Clean up log manager resources (future: close open file handles)
    // Currently LogManager doesn't maintain persistent handles that need closing
    // This is a placeholder for future cleanup needs
    console.log('  Cleaning up resources...');

    // Step 4: PID file is removed by server.stop()
    // Nothing additional needed here
  }
}
