import ffmpegInstaller from "@ffmpeg-installer/ffmpeg";
import { exec } from "child_process";
import fs from "fs";
import os from "os";
import path from "path";
import { Camera, DeviceType, Display, Microphone } from "./types";

// FFmpeg path setup
export const ffmpegPath = ffmpegInstaller.path.replace(
  "app.asar",
  "app.asar.unpacked"
);

// Utility function to create a unique session directory
export function createOutputDirectory(customPath?: string): string {
  const baseDir =
    customPath || path.join(os.homedir(), "recorder", new Date().toISOString());

  if (!fs.existsSync(baseDir)) {
    fs.mkdirSync(baseDir, { recursive: true });
  }

  return baseDir;
}

// Get absolute path
export function getAbsolutePath(filePath: string): string {
  return path.isAbsolute(filePath) ? filePath : path.resolve(filePath);
}

// Wait for a file to be ready (useful after stopping a recording)
export function waitForFile(
  filePath: string,
  maxAttempts = 10
): Promise<boolean> {
  return new Promise((resolve) => {
    let attempts = 0;

    const checkFile = () => {
      attempts++;

      if (fs.existsSync(filePath)) {
        try {
          const stats = fs.statSync(filePath);
          if (stats.size > 0) {
            resolve(true);
            return;
          }
        } catch (error) {
          console.error(`Error checking file stats: ${error}`);
          // Continue to next attempt
        }
      }

      if (attempts >= maxAttempts) {
        resolve(false);
        return;
      }

      setTimeout(checkFile, 500);
    };

    checkFile();
  });
}

// Get video metadata
export function getVideoMetadata(filePath: string): Promise<{
  dimensions: { width: number; height: number };
  durationInSeconds: number;
} | null> {
  return new Promise((resolve) => {
    if (!fs.existsSync(filePath)) {
      console.error(`File does not exist: ${filePath}`);
      resolve(null);
      return;
    }

    const command = `"${ffmpegPath}" -i "${filePath}" -hide_banner -v error`;

    exec(command, (error, _, stderr) => {
      try {
        const dimensionsMatch = stderr.match(/Stream #0:0.*?(\d+)x(\d+)/);
        const durationMatch = stderr.match(/Duration: (\d+):(\d+):(\d+\.\d+)/);

        if (
          dimensionsMatch &&
          dimensionsMatch[1] &&
          dimensionsMatch[2] &&
          durationMatch &&
          durationMatch[1] &&
          durationMatch[2] &&
          durationMatch[3]
        ) {
          const width = parseInt(dimensionsMatch[1], 10);
          const height = parseInt(dimensionsMatch[2], 10);

          const hours = parseInt(durationMatch[1], 10);
          const minutes = parseInt(durationMatch[2], 10);
          const seconds = parseFloat(durationMatch[3]);
          const durationInSeconds = hours * 3600 + minutes * 60 + seconds;

          resolve({
            dimensions: { width, height },
            durationInSeconds,
          });
        } else {
          console.warn(`Could not parse metadata for ${filePath}`);
          resolve(null);
        }
      } catch (e) {
        console.error("Error parsing video metadata:", e);
        resolve(null);
      }
    });
  });
}

// Get device index using AVFoundation
export function getDeviceIndex(deviceType: DeviceType): Promise<number | null> {
  return new Promise((resolve) => {
    try {
      const command = `"${ffmpegPath}" -f avfoundation -list_devices true -i ""`;

      const childProcess = exec(command, (error, _, stderr) => {
        if (error && !stderr) {
          console.error(`Failed to get AVFoundation devices: ${error.message}`);
          resolve(null);
          return;
        }

        const output = stderr || "";
        let deviceIndex: number | null = null;

        // Different search patterns based on device type
        let searchPattern: RegExp;
        switch (deviceType) {
          case "display":
            searchPattern = /\[(\d+)\]\s+Capture screen/i;
            break;
          case "camera":
            searchPattern = /\[(\d+)\]\s+(?:FaceTime|Camera|Webcam|HD Camera)/i;
            break;
          case "microphone":
            searchPattern =
              /\[(\d+)\]\s+(?:Built-in|System|MacBook\s+Pro)\s+(?:Microphone|Mic|Input)/i;
            break;
          default:
            // This should never happen due to typescript, but just in case
            console.error(`Unknown device type: ${deviceType}`);
            resolve(null);
            return;
        }

        // Search through output lines for matching device
        const lines = output.split("\n");
        for (const line of lines) {
          const match = line.match(searchPattern);
          if (match && match[1]) {
            deviceIndex = parseInt(match[1], 10);
            break;
          }
        }

        resolve(deviceIndex);
      });

      // Set a timeout to avoid hanging
      if (childProcess) {
        const timeout = setTimeout(() => {
          if (childProcess && !childProcess.killed) {
            try {
              childProcess.kill();
            } catch (e) {
              console.error("Error killing ffmpeg process:", e);
            }
            console.warn("getDeviceIndex process timed out");
            resolve(null);
          }
        }, 5000);

        childProcess.on("exit", () => clearTimeout(timeout));
      }
    } catch (error) {
      console.error("Error in getDeviceIndex:", error);
      resolve(null);
    }
  });
}

// Get available displays (screens)
export async function getDisplays(): Promise<Display[]> {
  const displayIndex = await getDeviceIndex("display");
  if (displayIndex === null) {
    return [];
  }

  return [{ id: displayIndex.toString(), name: `Display ${displayIndex}` }];
}

// Get available cameras
export async function getCameras(): Promise<Camera[]> {
  const cameraIndex = await getDeviceIndex("camera");
  if (cameraIndex === null) {
    return [];
  }

  return [{ id: cameraIndex.toString(), name: `Camera ${cameraIndex}` }];
}

// Get available microphones
export async function getMicrophones(): Promise<Microphone[]> {
  const microphoneIndex = await getDeviceIndex("microphone");
  if (microphoneIndex === null) {
    return [];
  }

  return [
    { id: microphoneIndex.toString(), name: `Microphone ${microphoneIndex}` },
  ];
}
