import { exec } from 'child_process';
import { promisify } from 'util';
import { readFile, unlink, mkdir, copyFile } from 'fs/promises';
import { existsSync } from 'fs';
import path from 'path';
import type { VideoInfo, FormatOption, DownloadRequest, DownloadResponse } from './types';

const execAsync = promisify(exec);

// Get yt-dlp path from environment variable or use default
// Read at runtime to ensure env vars are loaded
function getYtDlpPath(): string {
  return process.env.YT_DLP_PATH || 'yt-dlp';
}

// Get ffmpeg location flag for yt-dlp (optional)
function getFfmpegLocationFlag(): string {
  const ffmpegPath = process.env.FFMPEG_PATH;
  return ffmpegPath ? `--ffmpeg-location "${ffmpegPath}"` : '';
}

const DOWNLOADS_DIR = path.join(process.cwd(), 'downloads');
const PUBLIC_DOWNLOADS_DIR = path.join(process.cwd(), 'public', 'downloads');

// Sanitize filename for safe storage
function sanitizeFilename(filename: string): string {
  return filename
    .replace(/[^a-z0-9]/gi, '_')
    .replace(/_+/g, '_')
    .substring(0, 200);
}

// Ensure directories exist
async function ensureDirectories() {
  if (!existsSync(DOWNLOADS_DIR)) {
    await mkdir(DOWNLOADS_DIR, { recursive: true });
  }
  if (!existsSync(PUBLIC_DOWNLOADS_DIR)) {
    await mkdir(PUBLIC_DOWNLOADS_DIR, { recursive: true });
  }
}

export async function getVideoInfo(url: string): Promise<VideoInfo | null> {
  try {
    await ensureDirectories();
    
    // Get video info as JSON
    const ytDlpPath = getYtDlpPath();
    console.log('[getVideoInfo] Using yt-dlp path:', ytDlpPath);
    
    // Increase maxBuffer to handle large JSON responses from long videos
    // Default is 1MB, increase to 10MB to handle videos with many formats
    const { stdout } = await execAsync(
      `"${ytDlpPath}" --dump-json --no-download "${url}"`,
      { maxBuffer: 10 * 1024 * 1024 } // 10MB buffer
    );
    
    const info = JSON.parse(stdout);
    
    const formats: FormatOption[] = (info.formats || []).map((f: any) => ({
      formatId: f.format_id,
      ext: f.ext,
      resolution: f.resolution || `${f.width}x${f.height}`,
      filesize: f.filesize,
      formatNote: f.format_note,
    }));

    return {
      title: info.title || 'Unknown',
      duration: info.duration || 0,
      thumbnail: info.thumbnail,
      formats,
    };
  } catch (error) {
    console.error('Error getting video info:', error);
    return null;
  }
}

export async function downloadVideo(
  request: DownloadRequest
): Promise<DownloadResponse> {
  try {
    await ensureDirectories();
    
    const { url, format, formatType } = request;
    const fileId = `${Date.now()}-${Math.random().toString(36).substring(7)}`;
    
    let ytDlpFormat = '';
    let outputExt = format;
    
    if (formatType === 'audio') {
      // Audio formats
      if (format === 'mp3') {
        ytDlpFormat = 'bestaudio[ext=m4a]/bestaudio';
        outputExt = 'mp3';
      } else if (format === 'wav') {
        ytDlpFormat = 'bestaudio[ext=m4a]/bestaudio';
        outputExt = 'wav';
      } else {
        ytDlpFormat = `bestaudio[ext=${format}]/bestaudio`;
      }
    } else {
      // Video formats
      ytDlpFormat = `bestvideo[ext=${format}]+bestaudio[ext=m4a]/best[ext=${format}]/best`;
    }
    
    const outputPath = path.join(DOWNLOADS_DIR, `${fileId}.%(ext)s`);
    const finalPath = path.join(PUBLIC_DOWNLOADS_DIR, `${fileId}.${outputExt}`);
    
    // Get yt-dlp path and ffmpeg location at runtime
    const ytDlpPath = getYtDlpPath();
    const ffmpegFlag = getFfmpegLocationFlag();
    console.log('[downloadVideo] Using yt-dlp path:', ytDlpPath);
    if (ffmpegFlag) console.log('[downloadVideo] Using ffmpeg location flag:', ffmpegFlag);
    
    // Download with yt-dlp
    let command = `"${ytDlpPath}" ${ffmpegFlag} -f "${ytDlpFormat}" -o "${outputPath}" "${url}"`;
    
    if (formatType === 'audio' && format === 'mp3') {
      // For MP3, we need to extract audio and convert
      command = `"${ytDlpPath}" ${ffmpegFlag} -f "bestaudio[ext=m4a]/bestaudio" -x --audio-format mp3 --audio-quality 0 -o "${outputPath}" "${url}"`;
    } else if (formatType === 'audio' && format === 'wav') {
      // For WAV, extract and convert
      command = `"${ytDlpPath}" ${ffmpegFlag} -f "bestaudio[ext=m4a]/bestaudio" -x --audio-format wav -o "${outputPath}" "${url}"`;
    }
    
    // Increase maxBuffer for downloads as well
    await execAsync(command, { maxBuffer: 10 * 1024 * 1024 }); // 10MB buffer
    
    // Find the downloaded file
    const files = await import('fs/promises').then(fs => 
      fs.readdir(DOWNLOADS_DIR)
    );
    const downloadedFile = files.find(f => f.startsWith(fileId));
    
    if (!downloadedFile) {
      throw new Error('Downloaded file not found');
    }
    
    const downloadedPath = path.join(DOWNLOADS_DIR, downloadedFile);
    const downloadedExt = path.extname(downloadedFile).slice(1);
    
    // Get video info for response (before moving file)
    const videoInfo = await getVideoInfo(url);
    const safeFilename = sanitizeFilename(videoInfo?.title || 'video');
    
    // If extension matches, just copy. Otherwise, rename during copy
    if (downloadedExt === outputExt) {
      // Copy file to public directory
      await copyFile(downloadedPath, finalPath);
      await unlink(downloadedPath);
    } else {
      // For format conversions, yt-dlp should have already converted
      // But if extension doesn't match, copy with new extension
      await copyFile(downloadedPath, finalPath);
      await unlink(downloadedPath);
    }
    
    return {
      success: true,
      downloadUrl: `/downloads/${fileId}.${outputExt}`,
      filename: `${safeFilename}.${outputExt}`,
      videoInfo: videoInfo || undefined,
    };
  } catch (error: any) {
    console.error('Error downloading video:', error);
    return {
      success: false,
      error: error.message || 'Failed to download video',
    };
  }
}
