import { parse } from "path";
import { readdir, copyFile, unlink } from "fs/promises";
import { join } from "path";
import { existsSync, mkdirSync, statSync } from "fs";
import { resolve } from "path";
import readline from "readline";
import { exec } from "child_process";
import { promisify } from "util";

const execAsync = promisify(exec);

// Add this line to increase the buffer size
const MAX_BUFFER = 1024 * 1024 * 100; // 100 MB

const videoExtensions = [".mkv", ".avi", ".mp4", ".mov", ".wmv"];

function sanitizeName(name: string): string {
  return name
    .toLowerCase()
    .replace(/\s/g, "_")
    .replace(/[^a-z0-9_]/g, "");
}

function createInterface(): readline.Interface {
  return readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
}

async function processVideos(inputFolder: string): Promise<void> {
  const inputFolderName = parse(inputFolder).name;
  const outputFolder = join(parse(inputFolder).dir, "output", inputFolderName);

  console.log("Dry run:");
  await findVideos(inputFolder, outputFolder, true);

  const rl = createInterface();

  const answer = await new Promise<string>((resolve) => {
    rl.question("Create folders? (y/n): ", resolve);
  });

  rl.close();

  if (answer.toLowerCase() === "y") {
    console.log("\nCreating folders and converting videos:");
    await findVideos(inputFolder, outputFolder, false);
  } else {
    console.log("Folder creation and video conversion cancelled.");
  }
}

async function findVideos(
  inputPath: string,
  outputPath: string,
  dryRun: boolean
): Promise<void> {
  const entries = await readdir(inputPath, { withFileTypes: true });

  for (const entry of entries) {
    const fullInputPath = join(inputPath, entry.name);
    const sanitizedName = sanitizeName(parse(entry.name).name); // Remove extension before sanitizing
    const fullOutputPath = join(outputPath, sanitizedName);

    if (entry.isDirectory()) {
      await findVideos(fullInputPath, fullOutputPath, dryRun);
    } else if (
      entry.isFile() &&
      videoExtensions.includes(parse(entry.name).ext.toLowerCase())
    ) {
      // Always add .mp4 extension to the output file
      const outputFilePath = fullOutputPath + ".mp4";

      console.log(`Input: ${fullInputPath}`);
      console.log(`Output: ${outputFilePath}`);

      if (!dryRun) {
        const outputDir = parse(outputFilePath).dir;
        if (!existsSync(outputDir)) {
          mkdirSync(outputDir, { recursive: true });
          console.log(`Created directory: ${outputDir}`);
        }

        const inputExt = parse(fullInputPath).ext.toLowerCase();

        let success = false;
        success = await convertToMp4(fullInputPath, outputFilePath);
        //if (inputExt === ".mp4") {
        //  success = await copyMp4(fullInputPath, outputFilePath);
        //} else {
        //  success = await convertToMp4(fullInputPath, outputFilePath);
        //}

        if (success) {
          await deleteOriginalFile(fullInputPath);
        }
      }

      console.log("---");
    }
  }
}

async function convertToMp4(
  inputPath: string,
  outputPath: string
): Promise<boolean> {
  //const ffmpegCommand = `ffmpeg -nostdin -i "${inputPath}" -c:v libx264 -preset fast -crf 18 -c:a aac -b:a 96k -ac 2 -ar 48000 -movflags +faststart "${outputPath}"`;
  const ffmpegCommand = `ffmpeg -nostdin -i "${inputPath}" -c:v libx264 -preset fast -crf 23 -c:a aac -b:a 96k -ac 2 -ar 48000 -c:s mov_text -movflags +faststart "${outputPath}"`;

  try {
    await execAsync(ffmpegCommand, { maxBuffer: MAX_BUFFER });
    console.log(`Conversion successful: ${inputPath} -> ${outputPath}`);
    return true;
  } catch (error) {
    console.error(`Conversion failed for ${inputPath}:`, error);
    return false;
  }
}

async function copyMp4(
  inputPath: string,
  outputPath: string
): Promise<boolean> {
  try {
    await copyFile(inputPath, outputPath);
    console.log(`Copy successful: ${inputPath} -> ${outputPath}`);
    return true;
  } catch (error) {
    console.error(`Copy failed for ${inputPath}:`, error);
    return false;
  }
}

async function deleteOriginalFile(filePath: string): Promise<void> {
  try {
    await unlink(filePath);
    console.log(`Deleted original file: ${filePath}`);
  } catch (error) {
    console.error(`Failed to delete original file ${filePath}:`, error);
  }
}

// Get the input folder from command line arguments
const inputArg = process.argv[2];

if (!inputArg) {
  console.error("Please provide an input folder as a command line argument.");
  process.exit(1);
}

const inputFolder = resolve(inputArg);

if (!existsSync(inputFolder) || !statSync(inputFolder).isDirectory()) {
  console.error("The provided path does not exist or is not a directory.");
  process.exit(1);
}

// to run: bun run.ts folder_name
processVideos(inputFolder);
