// src/services/systemService.test.ts
import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { logger } from './logger.server';
import type { ExecException } from 'child_process';

// Mock logger
vi.mock('./logger.server', () => ({
  logger: {
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
  },
}));

// Import the class, not the singleton instance, to apply Dependency Injection
import { SystemService } from './systemService';

describe('SystemService', () => {
  let systemService: SystemService;
  let mockExecAsync: Mock;

  beforeEach(() => {
    vi.clearAllMocks();
    // Create a mock function for our dependency
    mockExecAsync = vi.fn();
    // Instantiate the service with the mock dependency
    systemService = new SystemService(mockExecAsync);
  });

  describe('getPm2Status', () => {
    it('should return success: true when process is online', async () => {
      // This stdout mimics the output of `pm2 describe <app_name>`
      const stdout = `Describing process with id 0 - name flyer-crawler-api
      │ status            │ online                                      │
      │ name              │ flyer-crawler-api                           │`;
      mockExecAsync.mockResolvedValue({ stdout, stderr: '' });

      const result = await systemService.getPm2Status();

      expect(result).toEqual({
        success: true,
        message: 'Application is online and running under PM2.',
      });
    });

    it('should return success: false when process is stopped', async () => {
      const stdout = `Describing process with id 0 - name flyer-crawler-api
      │ status            │ stopped                                     │
      │ name              │ flyer-crawler-api                           │`;
      mockExecAsync.mockResolvedValue({ stdout, stderr: '' });

      const result = await systemService.getPm2Status();

      expect(result).toEqual({
        success: false,
        message: 'Application process exists but is not online.',
      });
    });

    it('should throw error if stderr has content', async () => {
      mockExecAsync.mockResolvedValue({ stdout: 'some stdout', stderr: 'some stderr warning' });

      await expect(systemService.getPm2Status()).rejects.toThrow(
        'PM2 command produced an error: some stderr warning',
      );
    });

    it('should return success: false when process does not exist', async () => {
      const error = new Error('Command failed') as ExecException & { stdout?: string; stderr?: string };
      error.code = 1;
      error.stderr = "[PM2][ERROR] Process or Namespace flyer-crawler-api doesn't exist";

      mockExecAsync.mockRejectedValue(error);

      const result = await systemService.getPm2Status();

      expect(result).toEqual({
        success: false,
        message: 'Application process is not running under PM2.',
      });
      expect(logger.warn).toHaveBeenCalledWith(
        expect.stringContaining('PM2 process "flyer-crawler-api" not found'),
      );
    });
  });
});