/**
 * GracefulShutdown Tests
 *
 * Tests for the graceful shutdown handler.
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { GracefulShutdown } from './shutdown.js';
import type { CoordinationServer } from './index.js';
import type { ProcessManager } from '../process/index.js';
import type { LogManager } from '../logging/index.js';

/**
 * Creates a mock CoordinationServer for testing.
 */
function createMockServer(): CoordinationServer & {
  stop: ReturnType<typeof vi.fn>;
} {
  return {
    start: vi.fn(),
    stop: vi.fn().mockResolvedValue(undefined),
    isRunning: vi.fn().mockReturnValue(true),
    getPort: vi.fn().mockReturnValue(3847),
    getPidFile: vi.fn().mockReturnValue('/tmp/test.pid'),
  } as unknown as CoordinationServer & { stop: ReturnType<typeof vi.fn> };
}

/**
 * Creates a mock ProcessManager for testing.
 */
function createMockProcessManager(): ProcessManager & {
  stopAll: ReturnType<typeof vi.fn>;
} {
  return {
    spawn: vi.fn(),
    stop: vi.fn(),
    stopAll: vi.fn().mockResolvedValue(undefined),
    restart: vi.fn(),
    isRunning: vi.fn(),
  } as unknown as ProcessManager & { stopAll: ReturnType<typeof vi.fn> };
}

/**
 * Creates a mock LogManager for testing.
 */
function createMockLogManager(): LogManager {
  return {
    ensureLogDir: vi.fn(),
    ensureProcessDir: vi.fn(),
    getProcessDir: vi.fn(),
    getLogPath: vi.fn(),
    listLogs: vi.fn(),
    cleanOldLogs: vi.fn(),
    getBaseDir: vi.fn(),
  } as unknown as LogManager;
}

describe('GracefulShutdown', () => {
  let server: ReturnType<typeof createMockServer>;
  let processManager: ReturnType<typeof createMockProcessManager>;
  let logManager: LogManager;
  let shutdown: GracefulShutdown;

  beforeEach(() => {
    server = createMockServer();
    processManager = createMockProcessManager();
    logManager = createMockLogManager();
    shutdown = new GracefulShutdown(server, processManager, logManager);

    // Suppress console output during tests
    vi.spyOn(console, 'log').mockImplementation(() => {});
    vi.spyOn(console, 'error').mockImplementation(() => {});
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  describe('shutdown()', () => {
    it('should call server.stop()', async () => {
      await shutdown.shutdown();

      expect(server.stop).toHaveBeenCalledOnce();
    });

    it('should call processManager.stopAll()', async () => {
      await shutdown.shutdown();

      expect(processManager.stopAll).toHaveBeenCalledOnce();
    });

    it('should call server.stop() before processManager.stopAll()', async () => {
      const callOrder: string[] = [];

      server.stop.mockImplementation(async () => {
        callOrder.push('server.stop');
      });
      processManager.stopAll.mockImplementation(async () => {
        callOrder.push('processManager.stopAll');
      });

      await shutdown.shutdown();

      expect(callOrder).toEqual(['server.stop', 'processManager.stopAll']);
    });
  });

  describe('install()', () => {
    let originalListeners: {
      SIGTERM: NodeJS.SignalsListener[];
      SIGINT: NodeJS.SignalsListener[];
      SIGHUP: NodeJS.SignalsListener[];
    };

    beforeEach(() => {
      // Store original listeners
      originalListeners = {
        SIGTERM: process.listeners('SIGTERM') as NodeJS.SignalsListener[],
        SIGINT: process.listeners('SIGINT') as NodeJS.SignalsListener[],
        SIGHUP: process.listeners('SIGHUP') as NodeJS.SignalsListener[],
      };
    });

    afterEach(() => {
      // Remove all listeners we added and restore original ones
      process.removeAllListeners('SIGTERM');
      process.removeAllListeners('SIGINT');
      process.removeAllListeners('SIGHUP');

      originalListeners.SIGTERM.forEach((l) => process.on('SIGTERM', l));
      originalListeners.SIGINT.forEach((l) => process.on('SIGINT', l));
      originalListeners.SIGHUP.forEach((l) => process.on('SIGHUP', l));
    });

    it('should install SIGTERM handler', () => {
      const initialCount = process.listenerCount('SIGTERM');
      shutdown.install();
      expect(process.listenerCount('SIGTERM')).toBe(initialCount + 1);
    });

    it('should install SIGINT handler', () => {
      const initialCount = process.listenerCount('SIGINT');
      shutdown.install();
      expect(process.listenerCount('SIGINT')).toBe(initialCount + 1);
    });

    it('should install SIGHUP handler', () => {
      const initialCount = process.listenerCount('SIGHUP');
      shutdown.install();
      expect(process.listenerCount('SIGHUP')).toBe(initialCount + 1);
    });
  });
});
