import { spawn, type ChildProcess } from "child_process";
import { EventEmitter } from "events";

interface FFMPEGProcess {
  process: ChildProcess;
  startTime: Date;
  sessionId: number;
}

export class FFMPEGManager extends EventEmitter {
  private processes: Map<number, FFMPEGProcess>;
  private readonly MAX_DURATION = 25000; // 25 seconds timeout

  constructor() {
    super();
    this.processes = new Map();
  }

  async startProcess(sessionId: number, audioSource: string, outputPath: string): Promise<void> {
    // Stop existing process if any
    if (this.processes.has(sessionId)) {
      await this.stopProcess(sessionId);
    }

    return new Promise((resolve, reject) => {
      const ffmpegArgs = [
        "-re",
        "-probesize",
        "32",
        "-analyzeduration",
        "0",
        "-i",
        audioSource,
        "-t",
        "20",
        "-bufsize",
        "64k",
        "-acodec",
        "pcm_s16le",
        "-ac",
        "1",
        "-ar",
        "16000",
        "-y",
        outputPath
      ];

      console.log(`[FFMPEG] Starting process for session ${sessionId} with args:`, ffmpegArgs.join(" "));

      const process = spawn("ffmpeg", ffmpegArgs);
      let stdoutData = "";
      let stderrData = "";

      process.stdout?.on("data", (data) => {
        stdoutData += data.toString();
        this.emit("stdout", { sessionId, data: data.toString() });
      });

      process.stderr?.on("data", (data) => {
        stderrData += data.toString();
        this.emit("stderr", { sessionId, data: data.toString() });
      });

      process.on("close", (code) => {
        this.processes.delete(sessionId);

        if (code === 0 || code === null) { // Add null check for graceful termination
          console.log(`[FFMPEG] Process completed successfully for session ${sessionId}`);
          resolve();
        } else {
          console.error(`[FFMPEG] Process failed for session ${sessionId} with code ${code}`);
          console.error("stderr:", stderrData);
          reject(new Error(`FFMPEG process failed with code ${code}`));
        }
      });

      process.on("error", (err) => {
        console.error(`[FFMPEG] Process error for session ${sessionId}:`, err);
        this.processes.delete(sessionId);
        reject(err);
      });

      // Store the process
      this.processes.set(sessionId, {
        process,
        startTime: new Date(),
        sessionId
      });

      // Set timeout
      setTimeout(() => {
        if (this.processes.has(sessionId)) {
          this.stopProcess(sessionId)
            .catch(console.error);
        }
      }, this.MAX_DURATION);
    });
  }

  async stopProcess(sessionId: number): Promise<void> {
    const processInfo = this.processes.get(sessionId);
    if (!processInfo) return;

    return new Promise((resolve) => {
      processInfo.process.on("close", () => {
        this.processes.delete(sessionId);
        console.log(`[FFMPEG] Process stopped for session ${sessionId}`);
        resolve();
      });

      processInfo.process.kill();
    });
  }

  async stopAllProcesses(): Promise<void> {
    const promises = Array.from(this.processes.keys()).map(sessionId => 
      this.stopProcess(sessionId)
    );
    await Promise.all(promises);
  }

  isProcessRunning(sessionId: number): boolean {
    return this.processes.has(sessionId);
  }

  getRunningProcesses(): number[] {
    return Array.from(this.processes.keys());
  }
}

// Export a singleton instance
export const ffmpegManager = new FFMPEGManager();