import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
import { Shutdown } from '../src/shutdown';

describe('Shutdown Signal Handlers', () => {
  let shutdown: Shutdown;
  let processOnceSpy: any;
  let processExitSpy: any;
  const originalOnce = process.once;
  const originalExit = process.exit;

  beforeEach(() => {
    // Reset singleton instance
    (Shutdown as any).instance = null;

    // Mock process.once to capture signal handlers
    const listeners: Record<string, Function[]> = {};
    processOnceSpy = mock((event: string, handler: Function) => {
      if (!listeners[event]) {
        listeners[event] = [];
      }
      listeners[event].push(handler);
      return process;
    });
    process.once = processOnceSpy as any;

    // Mock process.exit
    processExitSpy = mock((code?: number) => {
      // Just record the call, don't actually exit
      return undefined as never;
    });
    process.exit = processExitSpy as any;

    // Store listeners for manual triggering
    (global as any).__testListeners = listeners;
  });

  afterEach(() => {
    // Restore original methods
    process.once = originalOnce;
    process.exit = originalExit;

    // Clean up
    (Shutdown as any).instance = null;
    delete (global as any).__testListeners;
  });

  describe('Signal Handler Registration', () => {
    it('should register signal handlers on initialization with autoRegister', () => {
      shutdown = new Shutdown({ autoRegister: true });

      // Check that signals were registered
      expect(processOnceSpy).toHaveBeenCalledWith('SIGTERM', expect.any(Function));
      expect(processOnceSpy).toHaveBeenCalledWith('SIGINT', expect.any(Function));
    });

    it('should not register handlers when autoRegister is false', () => {
      shutdown = new Shutdown({ autoRegister: false });

      expect(processOnceSpy).not.toHaveBeenCalled();
    });

    it('should not register handlers twice', () => {
      shutdown = new Shutdown({ autoRegister: true });
      const callCount = processOnceSpy.mock.calls.length;

      // Try to setup handlers again (internally)
      shutdown['setupSignalHandlers']();

      // Should not register additional handlers
      expect(processOnceSpy.mock.calls.length).toBe(callCount);
    });
  });

  describe('Signal Handler Behavior', () => {
    it('should handle SIGTERM signal', async () => {
      shutdown = new Shutdown({ autoRegister: true });
      const callback = mock(async () => {});
      shutdown.onShutdown(callback);

      const listeners = (global as any).__testListeners;
      const sigtermHandler = listeners['SIGTERM']?.[0];
      
      expect(sigtermHandler).toBeDefined();

      // Mock the shutdown method to track it was called
      const shutdownSpy = mock(shutdown.shutdown.bind(shutdown));
      shutdown.shutdown = shutdownSpy;

      // Trigger SIGTERM
      await sigtermHandler();

      // Verify shutdown was called
      expect(shutdownSpy).toHaveBeenCalled();
      expect(processExitSpy).toHaveBeenCalledWith(0);
    });

    it('should handle SIGINT signal', async () => {
      shutdown = new Shutdown({ autoRegister: true });
      const callback = mock(async () => {});
      shutdown.onShutdown(callback);

      const listeners = (global as any).__testListeners;
      const sigintHandler = listeners['SIGINT']?.[0];
      
      expect(sigintHandler).toBeDefined();

      // Mock the shutdown method
      const shutdownSpy = mock(shutdown.shutdown.bind(shutdown));
      shutdown.shutdown = shutdownSpy;

      // Trigger SIGINT
      await sigintHandler();

      // Verify shutdown was called
      expect(shutdownSpy).toHaveBeenCalled();
      expect(processExitSpy).toHaveBeenCalledWith(0);
    });

    it('should exit with code 1 on shutdown error', async () => {
      shutdown = new Shutdown({ autoRegister: true });

      // Make shutdown throw an error
      shutdown.shutdown = mock(async () => {
        throw new Error('Shutdown failed');
      });

      const listeners = (global as any).__testListeners;
      const sigtermHandler = listeners['SIGTERM']?.[0];

      // Trigger SIGTERM
      await sigtermHandler();

      // Should exit with code 1 on error
      expect(processExitSpy).toHaveBeenCalledWith(1);
    });

    it('should not process signal if already shutting down', async () => {
      shutdown = new Shutdown({ autoRegister: true });

      // Set shutting down flag
      shutdown['isShuttingDown'] = true;

      const listeners = (global as any).__testListeners;
      const sigtermHandler = listeners['SIGTERM']?.[0];

      // Mock shutdown to ensure it's not called
      const shutdownSpy = mock(shutdown.shutdown.bind(shutdown));
      shutdown.shutdown = shutdownSpy;

      // Trigger SIGTERM
      await sigtermHandler();

      // Should not call shutdown or exit
      expect(shutdownSpy).not.toHaveBeenCalled();
      expect(processExitSpy).not.toHaveBeenCalled();
    });
  });

  describe('Singleton Behavior with Signals', () => {
    it('should use singleton instance for signal handling', () => {
      const instance1 = Shutdown.getInstance();
      const instance2 = Shutdown.getInstance();

      expect(instance1).toBe(instance2);
      
      // Only one set of signal handlers should be registered
      expect(processOnceSpy).toHaveBeenCalledTimes(2); // SIGTERM and SIGINT
    });

    it('should handle callbacks registered before signal', async () => {
      shutdown = new Shutdown({ autoRegister: true });
      
      const callback1 = mock(async () => {});
      const callback2 = mock(async () => {});
      
      shutdown.onShutdown(callback1, 10);
      shutdown.onShutdown(callback2, 20);

      const listeners = (global as any).__testListeners;
      const sigtermHandler = listeners['SIGTERM']?.[0];

      // Replace shutdown method to test callback execution
      const originalShutdown = shutdown.shutdown.bind(shutdown);
      let shutdownCalled = false;
      shutdown.shutdown = mock(async () => {
        shutdownCalled = true;
        await originalShutdown();
      });

      // Trigger signal
      await sigtermHandler();

      expect(shutdownCalled).toBe(true);
      expect(processExitSpy).toHaveBeenCalledWith(0);
    });
  });

  describe('Edge Cases', () => {
    it('should handle missing signal handler gracefully', () => {
      shutdown = new Shutdown({ autoRegister: true });

      const listeners = (global as any).__testListeners;
      
      // Verify handlers exist
      expect(listeners['SIGTERM']).toBeDefined();
      expect(listeners['SIGINT']).toBeDefined();
      expect(listeners['SIGTERM'].length).toBeGreaterThan(0);
      expect(listeners['SIGINT'].length).toBeGreaterThan(0);
    });

    it('should work with default options', () => {
      // Create with defaults
      shutdown = new Shutdown();

      // Should auto-register by default
      expect(processOnceSpy).toHaveBeenCalled();
    });
  });
});