import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { UnityClient } from './unity-client.js';
import { UnityConnectionManager } from './unity-connection-manager.js';
import { McpKeepaliveService } from './mcp-keepalive-service.js';
import { ENVIRONMENT, NOTIFICATION_METHODS, ServerShutdownReason } from './constants.js';
import { VibeLogger } from './utils/vibe-logger.js';
import { IUnityEventService } from './application/interfaces/unity-event-service.js';
import { INotificationService } from './application/interfaces/notification-service.js';
import { IProcessControlService } from './application/interfaces/process-control-service.js';

/**
 * Unity Event Handler - Manages Unity notifications and event processing
 * Implements segregated interfaces following Interface Segregation Principle
 *
 * Design document reference: Packages/src/TypeScriptServer~/ARCHITECTURE.md
 *
 * Related classes:
 * - UnityClient: Manages communication with Unity Editor
 * - UnityConnectionManager: Manages Unity connection
 * - Server: MCP server instance for sending notifications
 * - UnityMcpServer: Main server class that uses this handler
 * - McpKeepaliveService: Keepalive service for preventing Cursor idle timeout
 *
 * Key features:
 * - tools_changed notification sending
 * - Unity notification listener setup
 * - Signal handler configuration
 * - Graceful shutdown handling
 */
export class UnityEventHandler
  implements IUnityEventService, INotificationService, IProcessControlService
{
  private server: Server;
  private unityClient: UnityClient;
  private connectionManager: UnityConnectionManager;
  private keepaliveService: McpKeepaliveService;
  private readonly isDevelopment: boolean;
  private shuttingDown: boolean = false;
  private isNotifying: boolean = false;
  private isInitializationCompleted: boolean = false;
  private pendingToolsChangedNotification: boolean = false;

  constructor(
    server: Server,
    unityClient: UnityClient,
    connectionManager: UnityConnectionManager,
    keepaliveService: McpKeepaliveService,
  ) {
    this.server = server;
    this.unityClient = unityClient;
    this.connectionManager = connectionManager;
    this.keepaliveService = keepaliveService;
    this.isDevelopment = process.env.NODE_ENV === ENVIRONMENT.NODE_ENV_DEVELOPMENT;
  }

  /**
   * Called when MCP initialization is completed
   * Sends any pending notifications that were queued during initialization
   */
  onInitializationCompleted(): void {
    this.isInitializationCompleted = true;

    if (this.pendingToolsChangedNotification) {
      this.pendingToolsChangedNotification = false;
      this.sendToolsChangedNotification();
    }
  }

  /**
   * Setup Unity event listener for automatic tool updates
   */
  setupUnityEventListener(onToolsChanged: () => Promise<void>): void {
    // Listen for MCP standard notifications from Unity
    //
    // Note: tools/list_changed serves dual purposes in this project:
    // 1. Original MCP purpose: Notify that available tools have changed
    // 2. uLoopMCP additional purpose: Signal that Unity is ready after Domain Reload
    //
    // After Domain Reload, Unity sends this notification once initialization is complete
    // (~16 seconds after server restart). This tells TypeScript that Unity can now
    // reliably process requests. See constants.ts for more details.
    this.unityClient.onNotification(NOTIFICATION_METHODS.TOOLS_LIST_CHANGED, (_params: unknown) => {
      VibeLogger.logInfo(
        'unity_notification_received',
        'Unity notification received: notifications/tools/list_changed',
        undefined,
        undefined,
        'Unity notified that tool list has changed',
      );

      try {
        void onToolsChanged();
      } catch (error) {
        VibeLogger.logError(
          'unity_notification_error',
          'Failed to update dynamic tools via Unity notification',
          { error: error instanceof Error ? error.message : String(error) },
          undefined,
          'Error occurred while processing Unity tool list change notification',
        );
      }
    });

    // Listen for server shutdown notifications from Unity
    this.unityClient.onNotification(NOTIFICATION_METHODS.SERVER_SHUTDOWN, (params: unknown) => {
      const shutdownParams = params as { reason?: string };
      const reason: ServerShutdownReason =
        shutdownParams.reason === ServerShutdownReason.DOMAIN_RELOAD
          ? ServerShutdownReason.DOMAIN_RELOAD
          : ServerShutdownReason.EDITOR_QUIT;

      VibeLogger.logInfo(
        'unity_shutdown_notification',
        `Unity server shutdown notification received: ${reason}`,
        { reason },
        undefined,
        `Unity server is shutting down due to ${reason}`,
      );

      // Store shutdown reason in UnityClient for message differentiation
      this.unityClient.setShutdownReason(reason);
    });
  }

  /**
   * Send tools changed notification (with concurrent call prevention and initialization check)
   *
   * Notification timing rules:
   * 1. Before initialization completed: queue the notification
   * 2. After initialization completed: send immediately
   * 3. Concurrent calls: prevented by isNotifying flag
   */
  sendToolsChangedNotification(): void {
    if (!this.isInitializationCompleted) {
      this.pendingToolsChangedNotification = true;
      if (this.isDevelopment) {
        VibeLogger.logDebug(
          'tools_notification_queued',
          'sendToolsChangedNotification queued: initialization not completed',
          undefined,
          undefined,
          'Notification will be sent after initialization completes',
        );
      }
      return;
    }

    if (this.isNotifying) {
      if (this.isDevelopment) {
        VibeLogger.logDebug(
          'tools_notification_skipped',
          'sendToolsChangedNotification skipped: already notifying',
          undefined,
          undefined,
          'Duplicate notification prevented',
        );
      }
      return;
    }

    this.isNotifying = true;
    try {
      void this.server.notification({
        method: NOTIFICATION_METHODS.TOOLS_LIST_CHANGED,
        params: {},
      });
      if (this.isDevelopment) {
        VibeLogger.logInfo(
          'tools_notification_sent',
          'tools/list_changed notification sent',
          undefined,
          undefined,
          'Successfully notified client of tool list changes',
        );
      }
    } catch (error) {
      VibeLogger.logError(
        'tools_notification_error',
        'Failed to send tools changed notification',
        { error: error instanceof Error ? error.message : String(error) },
        undefined,
        'Error occurred while sending tool list change notification',
      );
    } finally {
      this.isNotifying = false;
    }
  }

  /**
   * Setup signal handlers for graceful shutdown
   */
  setupSignalHandlers(): void {
    // Handle Ctrl+C (SIGINT)
    process.on('SIGINT', () => {
      VibeLogger.logInfo(
        'sigint_received',
        'Received SIGINT, shutting down...',
        undefined,
        undefined,
        'User pressed Ctrl+C, initiating graceful shutdown',
      );
      this.gracefulShutdown();
    });

    // Handle kill command (SIGTERM)
    process.on('SIGTERM', () => {
      VibeLogger.logInfo(
        'sigterm_received',
        'Received SIGTERM, shutting down...',
        undefined,
        undefined,
        'Process termination signal received, initiating graceful shutdown',
      );
      this.gracefulShutdown();
    });

    // Handle terminal close (SIGHUP)
    process.on('SIGHUP', () => {
      VibeLogger.logInfo(
        'sighup_received',
        'Received SIGHUP, shutting down...',
        undefined,
        undefined,
        'Terminal hangup signal received, initiating graceful shutdown',
      );
      this.gracefulShutdown();
    });

    // Handle stdin close (when parent process disconnects)
    // BUG FIX: Added STDIN monitoring to detect when Cursor/parent MCP client disconnects
    // This prevents orphaned Node processes from remaining after IDE shutdown
    process.stdin.on('close', () => {
      VibeLogger.logInfo(
        'stdin_closed',
        'STDIN closed, shutting down...',
        undefined,
        undefined,
        'Parent process disconnected, preventing orphaned process',
      );
      this.gracefulShutdown();
    });

    process.stdin.on('end', () => {
      VibeLogger.logInfo(
        'stdin_ended',
        'STDIN ended, shutting down...',
        undefined,
        undefined,
        'STDIN stream ended, initiating graceful shutdown',
      );
      this.gracefulShutdown();
    });

    // Handle uncaught exceptions
    // BUG FIX: Added comprehensive error handling to prevent hanging processes
    process.on('uncaughtException', (error) => {
      VibeLogger.logException(
        'uncaught_exception',
        error,
        undefined,
        undefined,
        'Uncaught exception occurred, shutting down safely',
      );
      this.gracefulShutdown();
    });

    process.on('unhandledRejection', (reason, _promise) => {
      VibeLogger.logError(
        'unhandled_rejection',
        'Unhandled promise rejection',
        { reason: String(reason) },
        undefined,
        'Unhandled promise rejection occurred, shutting down safely',
      );
      this.gracefulShutdown();
    });
  }

  /**
   * Graceful shutdown with proper cleanup
   * BUG FIX: Enhanced shutdown process to prevent orphaned Node processes
   */
  gracefulShutdown(): void {
    // Prevent multiple shutdown attempts
    if (this.shuttingDown) {
      return;
    }

    this.shuttingDown = true;
    VibeLogger.logInfo(
      'graceful_shutdown_start',
      'Starting graceful shutdown...',
      undefined,
      undefined,
      'Initiating graceful shutdown process',
    );

    try {
      // Stop keepalive service first
      this.keepaliveService.stop();

      // Disconnect from Unity and stop all intervals
      this.connectionManager.disconnect();

      // Clear any remaining timers to ensure clean exit
      if (global.gc) {
        global.gc();
      }
    } catch (error) {
      VibeLogger.logError(
        'cleanup_error',
        'Error during cleanup',
        { error: error instanceof Error ? error.message : String(error) },
        undefined,
        'Error occurred during graceful shutdown cleanup',
      );
    }

    VibeLogger.logInfo(
      'graceful_shutdown_complete',
      'Graceful shutdown completed',
      undefined,
      undefined,
      'All cleanup completed, process will exit',
    );
    process.exit(0);
  }

  /**
   * Check if shutdown is in progress
   */
  isShuttingDown(): boolean {
    return this.shuttingDown;
  }
}
