// src/services/systemService.ts
import { exec as nodeExec, type ExecException } from 'child_process';
import { promisify } from 'util';
import { logger } from './logger.server';

// Define a type for the exec function for better type safety and testability.
// It matches the signature of a promisified child_process.exec.
export type ExecAsync = (
  command: string,
) => Promise<{ stdout: string; stderr: string }>;

export class SystemService {
  private execAsync: ExecAsync;

  constructor(execAsync: ExecAsync) {
    this.execAsync = execAsync;
  }

  async getPm2Status(): Promise<{ success: boolean; message: string }> {
    try {
      const { stdout, stderr } = await this.execAsync('pm2 describe flyer-crawler-api');

      // If the command runs but produces output on stderr, treat it as an error.
      // This handles cases where pm2 might issue warnings but still exit 0.
      if (stderr) {
        throw new Error(`PM2 command produced an error: ${stderr}`);
      }

      const isOnline = /│\s*status\s*│\s*online\s*│/m.test(stdout);
      const message = isOnline
        ? 'Application is online and running under PM2.'
        : 'Application process exists but is not online.';
      return { success: isOnline, message };
    } catch (error: ExecException | any) {
      // If the command fails (non-zero exit code), check if it's because the process doesn't exist.
      // This is a normal "not found" case, not a system error.
      // The error message can be in stdout or stderr depending on the pm2 version.
      const output = error.stdout || error.stderr || '';
      if (output.includes("doesn't exist")) {
        logger.warn('[SystemService] PM2 process "flyer-crawler-api" not found.');
        return {
          success: false,
          message: 'Application process is not running under PM2.',
        };
      }
      // For any other error, log it and re-throw to be handled as a 500.
      logger.error({ error: error.stderr || error.message }, '[SystemService] Error executing pm2 describe:');
      throw error;
    }
  }
}

// Instantiate the service with the real dependency for the application
const realExecAsync = promisify(nodeExec);
export const systemService = new SystemService(realExecAsync);