const { spawn, exec } = require('child_process');
const path = require('path');
const fs = require('fs');
const https = require('https');
const { app } = require('electron');

const BIN_DIR = app && app.isPackaged
  ? path.join(process.resourcesPath, 'app.asar.unpacked', 'bin')
  : path.join(__dirname, '..', 'bin');

const YTDLP_PATH = path.join(BIN_DIR, 'yt-dlp.exe');
const FFMPEG_PATH = path.join(BIN_DIR, 'ffmpeg.exe');

console.log('[DEBUG] Downloader Module initialized.');
console.log('[DEBUG] Binary directory path:', BIN_DIR);
console.log('[DEBUG] yt-dlp path:', YTDLP_PATH);
console.log('[DEBUG] ffmpeg path:', FFMPEG_PATH);

// Ensure binary directory exists
if (!fs.existsSync(BIN_DIR)) {
  console.log('[DEBUG] Binary directory does not exist, creating:', BIN_DIR);
  fs.mkdirSync(BIN_DIR, { recursive: true });
}

// Download helper function with progress callback
function downloadFile(url, dest, onProgress) {
  console.log(`[DEBUG] Starting download for: ${url} -> Target: ${dest}`);
  return new Promise((resolve, reject) => {
    let file;

    function makeRequest(requestUrl) {
      console.log(`[DEBUG] Requesting URL: ${requestUrl}`);
      https.get(requestUrl, (response) => {
        console.log(`[DEBUG] Received status code: ${response.statusCode} for ${requestUrl}`);
        
        // Follow HTTP redirects (301, 302, 307, 308)
        if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
          const redirectUrl = response.headers.location;
          console.log(`[DEBUG] Redirecting to: ${redirectUrl}`);
          makeRequest(redirectUrl);
          return;
        }

        if (response.statusCode !== 200) {
          console.error(`[DEBUG] Error: Download failed with status ${response.statusCode}`);
          reject(new Error(`Failed to download: status ${response.statusCode}`));
          return;
        }

        console.log(`[DEBUG] Connection successful. Writing to disk: ${dest}`);
        file = fs.createWriteStream(dest);
        const totalSize = parseInt(response.headers['content-length'], 10);
        console.log(`[DEBUG] Total file size: ${totalSize ? (totalSize / (1024 * 1024)).toFixed(2) + ' MB' : 'unknown'}`);
        let downloaded = 0;

        response.on('data', (chunk) => {
          downloaded += chunk.length;
          file.write(chunk);
          if (onProgress && totalSize) {
            const percent = Math.round((downloaded / totalSize) * 100);
            onProgress(percent);
          }
        });

        response.on('end', () => {
          file.end();
          console.log(`[DEBUG] File download completed successfully: ${dest}`);
          resolve();
        });
      }).on('error', (err) => {
        console.error(`[DEBUG] Network error during request:`, err);
        if (file) {
          fs.unlink(dest, () => {
            console.log(`[DEBUG] Cleaned up partial file on error: ${dest}`);
          });
        }
        reject(err);
      });
    }

    makeRequest(url);
  });
}

// Unzip using PowerShell (Windows native)
function unzipWindows(zipPath, destDir) {
  console.log(`[DEBUG] Extracting archive: ${zipPath} to ${destDir}`);
  return new Promise((resolve, reject) => {
    const cmd = `powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`;
    console.log(`[DEBUG] Executing extract command: ${cmd}`);
    exec(cmd, (error) => {
      if (error) {
        console.error(`[DEBUG] Extraction failed:`, error);
        reject(error);
      } else {
        console.log(`[DEBUG] Extraction completed successfully.`);
        resolve();
      }
    });
  });
}

// Check and install dependencies
async function checkDependencies(onStatusUpdate) {
  console.log('[DEBUG] Checking applications and libraries dependencies...');
  
  let needsYtDlp = true;
  if (fs.existsSync(YTDLP_PATH)) {
    const stats = fs.statSync(YTDLP_PATH);
    console.log(`[DEBUG] Found yt-dlp.exe. Size: ${(stats.size / (1024 * 1024)).toFixed(2)} MB`);
    if (stats.size > 1024 * 1024) {
      needsYtDlp = false;
      console.log(`[DEBUG] yt-dlp.exe is valid.`);
    } else {
      console.log(`[DEBUG] yt-dlp.exe is corrupted or incomplete. Deleting to redownload...`);
      try { fs.unlinkSync(YTDLP_PATH); } catch (e) { console.error('[DEBUG] Failed to delete corrupted yt-dlp:', e); }
    }
  } else {
    console.log(`[DEBUG] yt-dlp.exe not found.`);
  }

  let needsFfmpeg = true;
  if (fs.existsSync(FFMPEG_PATH)) {
    const stats = fs.statSync(FFMPEG_PATH);
    console.log(`[DEBUG] Found ffmpeg.exe. Size: ${(stats.size / (1024 * 1024)).toFixed(2)} MB`);
    if (stats.size > 1024 * 1024) {
      needsFfmpeg = false;
      console.log(`[DEBUG] ffmpeg.exe is valid.`);
    } else {
      console.log(`[DEBUG] ffmpeg.exe is corrupted or incomplete. Deleting to redownload...`);
      try { fs.unlinkSync(FFMPEG_PATH); } catch (e) { console.error('[DEBUG] Failed to delete corrupted ffmpeg:', e); }
    }
  } else {
    console.log(`[DEBUG] ffmpeg.exe not found.`);
  }

  if (!needsYtDlp && !needsFfmpeg) {
    console.log('[DEBUG] All dependencies are verified and ready.');
    return true;
  }

  if (needsYtDlp) {
    onStatusUpdate('Downloading yt-dlp...', 10);
    const ytDlpUrl = 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe';
    await downloadFile(ytDlpUrl, YTDLP_PATH, (pct) => {
      onStatusUpdate(`Downloading yt-dlp... ${pct}%`, Math.round(pct * 0.4));
    });
  }

  if (needsFfmpeg) {
    onStatusUpdate('Downloading ffmpeg...', 45);
    const ffmpegZipUrl = 'https://github.com/ffbinaries/ffbinaries-prebuilt/releases/download/v4.4.1/ffmpeg-4.4.1-win-64.zip';
    const tempZip = path.join(BIN_DIR, 'ffmpeg.zip');
    
    await downloadFile(ffmpegZipUrl, tempZip, (pct) => {
      onStatusUpdate(`Downloading ffmpeg... ${pct}%`, 40 + Math.round(pct * 0.5));
    });

    onStatusUpdate('Extracting ffmpeg...', 95);
    try {
      await unzipWindows(tempZip, BIN_DIR);
    } finally {
      if (fs.existsSync(tempZip)) {
        console.log(`[DEBUG] Cleaning up temporary archive: ${tempZip}`);
        try { fs.unlinkSync(tempZip); } catch (e) {}
      }
    }
  }

  onStatusUpdate('Dependencies ready!', 100);
  console.log('[DEBUG] Dependencies installation completed successfully.');
  return true;
}

function formatDuration(sec) {
  if (!sec) return '';
  const hrs = Math.floor(sec / 3600);
  const mins = Math.floor((sec % 3600) / 60);
  const secs = Math.floor(sec % 60);
  if (hrs > 0) {
    return `${hrs}:${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
  }
  return `${mins}:${secs.toString().padStart(2, '0')}`;
}

// Get video metadata (title, thumbnail, duration, etc.)
function getVideoInfo(url, browser) {
  console.log(`[DEBUG] Fetching info for URL: ${url}`);
  return new Promise((resolve, reject) => {
    let args = ['-J', '--flat-playlist'];
    if (browser) {
      args.push('--cookies-from-browser', browser);
    }
    args.push(url);
    
    console.log(`[DEBUG] Spawning process: ${YTDLP_PATH} ${args.join(' ')}`);
    const proc = spawn(YTDLP_PATH, args);
    let stdout = '';
    let stderr = '';

    proc.stdout.on('data', (data) => { stdout += data; });
    proc.stderr.on('data', (data) => { stderr += data; });

    proc.on('close', (code) => {
      console.log(`[DEBUG] yt-dlp info process exited with code ${code}`);
      if (code !== 0) {
        console.error(`[DEBUG] yt-dlp error output:`, stderr);
        reject(new Error(stderr || 'Failed to fetch details'));
        return;
      }
      try {
        const info = JSON.parse(stdout);
        
        if (info._type === 'playlist' || info.entries) {
          console.log(`[DEBUG] Playlist metadata fetched successfully for: "${info.title}" (${info.entries.length} items)`);
          const entries = info.entries.map((entry, index) => {
            let thumb = '';
            if (entry.thumbnail) thumb = entry.thumbnail;
            else if (entry.thumbnails && entry.thumbnails.length > 0) thumb = entry.thumbnails[0].url;

            let durStr = '';
            if (entry.duration_string) durStr = entry.duration_string;
            else if (entry.duration) durStr = formatDuration(entry.duration);

            return {
              id: entry.id || `track-${index}`,
              title: entry.title || `Track ${index + 1}`,
              thumbnail: thumb,
              duration: durStr,
              uploader: entry.uploader || info.uploader || 'Playlist',
              url: entry.url || `https://www.youtube.com/watch?v=${entry.id}`
            };
          });

          resolve({
            isPlaylist: true,
            playlistTitle: info.title,
            entries: entries
          });
        } else {
          console.log(`[DEBUG] Single video metadata fetched successfully for: "${info.title}"`);
          let thumb = '';
          if (info.thumbnail) thumb = info.thumbnail;
          else if (info.thumbnails && info.thumbnails.length > 0) thumb = info.thumbnails[0].url;

          resolve({
            isPlaylist: false,
            entries: [{
              id: info.id || 'video',
              title: info.title,
              thumbnail: thumb,
              duration: info.duration_string || (info.duration ? formatDuration(info.duration) : ''),
              uploader: info.uploader || 'Bilinmiyor',
              url: url
            }]
          });
        }
      } catch (err) {
        console.error(`[DEBUG] JSON parsing error on yt-dlp output:`, err);
        reject(err);
      }
    });
  });
}

// Download single item helper
function downloadSingle(url, format, quality, outputDir, browser, onProgress) {
  return new Promise((resolve, reject) => {
    let args = [];
    const outputTemplate = path.join(outputDir, '%(title)s.%(ext)s');
    const startTime = Date.now();
    let totalSize = 'Bilinmiyor';

    if (format === 'mp3') {
      const q = quality ? `${quality}K` : '320K';
      args = [
        '--no-playlist',
        '--ffmpeg-location', FFMPEG_PATH,
        '-x',
        '--audio-format', 'mp3',
        '--audio-quality', q,
        '-o', outputTemplate
      ];
    } else {
      const res = quality || '1080';
      args = [
        '--no-playlist',
        '--ffmpeg-location', FFMPEG_PATH,
        '-f', `bv*[height<=${res}][ext=mp4]+ba[ext=m4a]/b[height<=${res}][ext=mp4]/bv*[height<=${res}]+ba/b[height<=${res}]/bestvideo+bestaudio/best`,
        '-o', outputTemplate
      ];
    }

    if (browser) {
      args.push('--cookies-from-browser', browser);
    }
    args.push(url);

    console.log(`[DEBUG] Spawning single downloader process: ${YTDLP_PATH} ${args.join(' ')}`);
    const proc = spawn(YTDLP_PATH, args);

    proc.stdout.on('data', (data) => {
      const line = data.toString();
      
      const sizeMatch = line.match(/of\s+(\d+(?:\.\d+)?\s*[a-zA-Z]+)/);
      if (sizeMatch) {
        totalSize = sizeMatch[1];
      }

      const match = line.match(/\[download\]\s+(\d+\.\d+)%/);
      if (match && onProgress) {
        const percent = parseFloat(match[1]);
        onProgress(percent, `İndiriliyor... %${percent}`);
      } else if (line.includes('[ExtractAudio]') || line.includes('[Merger]')) {
        onProgress(95, 'İşleniyor/Dönüştürülüyor...');
      }
    });

    proc.stderr.on('data', (data) => {
      console.warn(`[DEBUG] [yt-dlp stderr]:`, data.toString());
    });

    proc.on('close', (code) => {
      console.log(`[DEBUG] Single downloader process exited with code ${code}`);
      if (code === 0) {
        const durationSec = ((Date.now() - startTime) / 1000).toFixed(1);
        resolve({
          size: totalSize,
          duration: `${durationSec} saniye`
        });
      } else {
        reject(new Error(`Download exited with code ${code}`));
      }
    });
  });
}

// Module-level Queue State variables
let currentProcess = null;
let isPaused = false;
let isStopped = false;
let currentItemIndex = 0;
let downloadQueue = [];
let queueOutputDir = '';
let queueBrowser = null;
let queueOnProgressCallback = null;
let queueResults = [];
let queueStartTime = 0;
let activeResolve = null;
let activeReject = null;

function getQueueStats() {
  const durationSec = ((Date.now() - queueStartTime) / 1000).toFixed(1);
  const completedCount = queueResults.filter(r => r.success).length;
  return {
    totalItems: downloadQueue.length,
    completed: completedCount,
    duration: `${durationSec} saniye`,
    outputDir: queueOutputDir,
    results: queueResults
  };
}

function downloadSingleItemWithProcessRef(item) {
  return new Promise((resolve, reject) => {
    let args = [];
    const outputTemplate = path.join(queueOutputDir, '%(title)s.%(ext)s');
    const startTime = Date.now();
    let totalSize = 'Bilinmiyor';

    if (item.format === 'mp3') {
      const q = item.quality ? `${item.quality}K` : '320K';
      args = [
        '--no-playlist',
        '--ffmpeg-location', FFMPEG_PATH,
        '-x',
        '--audio-format', 'mp3',
        '--audio-quality', q,
        '-o', outputTemplate
      ];
    } else {
      const res = item.quality || '1080';
      args = [
        '--no-playlist',
        '--ffmpeg-location', FFMPEG_PATH,
        '-f', `bv*[height<=${res}][ext=mp4]+ba[ext=m4a]/b[height<=${res}][ext=mp4]/bv*[height<=${res}]+ba/b[height<=${res}]/bestvideo+bestaudio/best`,
        '-o', outputTemplate
      ];
    }

    if (queueBrowser) {
      args.push('--cookies-from-browser', queueBrowser);
    }
    args.push(item.url);

    console.log(`[DEBUG] Spawning single downloader process: ${YTDLP_PATH} ${args.join(' ')}`);
    currentProcess = spawn(YTDLP_PATH, args);

    currentProcess.stdout.on('data', (data) => {
      const line = data.toString();
      
      const sizeMatch = line.match(/of\s+(\d+(?:\.\d+)?\s*[a-zA-Z]+)/);
      if (sizeMatch) {
        totalSize = sizeMatch[1];
      }

      const match = line.match(/\[download\]\s+(\d+\.\d+)%/);
      if (match) {
        const percent = parseFloat(match[1]);
        queueOnProgressCallback({
          itemId: item.id,
          index: currentItemIndex,
          total: downloadQueue.length,
          percent: percent,
          status: `İndiriliyor...`,
          title: item.title
        });
      } else if (line.includes('[ExtractAudio]') || line.includes('[Merger]')) {
        queueOnProgressCallback({
          itemId: item.id,
          index: currentItemIndex,
          total: downloadQueue.length,
          percent: 95,
          status: `İşleniyor/Dönüştürülüyor...`,
          title: item.title
        });
      }
    });

    currentProcess.stderr.on('data', (data) => {
      console.warn(`[DEBUG] [yt-dlp stderr]:`, data.toString());
    });

    currentProcess.on('close', (code) => {
      console.log(`[DEBUG] Single downloader process exited with code ${code}`);
      currentProcess = null;
      if (code === 0) {
        const durationSec = ((Date.now() - startTime) / 1000).toFixed(1);
        resolve({
          size: totalSize,
          duration: `${durationSec} saniye`
        });
      } else {
        reject(new Error(`Download exited with code ${code}`));
      }
    });
  });
}

async function processQueue() {
  if (isStopped) {
    activeResolve({
      success: false,
      stopped: true,
      stats: getQueueStats()
    });
    return;
  }

  if (isPaused) {
    queueOnProgressCallback({
      itemId: downloadQueue[currentItemIndex].id,
      index: currentItemIndex,
      total: downloadQueue.length,
      percent: 0,
      status: `Duraklatıldı`,
      title: downloadQueue[currentItemIndex].title
    });
    return;
  }

  if (currentItemIndex >= downloadQueue.length) {
    activeResolve({
      success: true,
      stats: getQueueStats()
    });
    return;
  }

  const item = downloadQueue[currentItemIndex];
  console.log(`[DEBUG] Queue processing [${currentItemIndex+1}/${downloadQueue.length}]: "${item.title}"`);
  
  queueOnProgressCallback({
    itemId: item.id,
    index: currentItemIndex,
    total: downloadQueue.length,
    percent: 0,
    status: `İndiriliyor...`,
    title: item.title
  });

  try {
    const result = await downloadSingleItemWithProcessRef(item);
    
    queueResults.push({
      success: true,
      item,
      size: result.size,
      duration: result.duration
    });

    queueOnProgressCallback({
      itemId: item.id,
      index: currentItemIndex,
      total: downloadQueue.length,
      percent: 100,
      status: `Tamamlandı`,
      title: item.title
    });

    currentItemIndex++;
    processQueue(); // Process next item

  } catch (error) {
    if (isStopped) {
      console.log('[DEBUG] Process aborted by user stop command.');
      activeResolve({
        success: false,
        stopped: true,
        stats: getQueueStats()
      });
      return;
    }

    if (isPaused) {
      console.log('[DEBUG] Process suspended by user pause command.');
      return;
    }

    console.error(`[DEBUG] Queue error on item "${item.title}":`, error);
    queueResults.push({
      success: false,
      item,
      error: error.message
    });

    queueOnProgressCallback({
      itemId: item.id,
      index: currentItemIndex,
      total: downloadQueue.length,
      percent: 0,
      status: `Hata`,
      title: item.title,
      failed: true
    });

    currentItemIndex++;
    processQueue(); // Continue queue on item error
  }
}

// Download list of items sequentially
async function downloadMedia(items, outputDir, browser, onProgress) {
  return new Promise((resolve, reject) => {
    console.log(`[DEBUG] Media download requested for ${items.length} items.`);
    
    downloadQueue = items;
    queueOutputDir = outputDir;
    queueBrowser = browser;
    queueOnProgressCallback = onProgress;
    isPaused = false;
    isStopped = false;
    currentItemIndex = 0;
    queueResults = [];
    queueStartTime = Date.now();
    activeResolve = resolve;
    activeReject = reject;

    processQueue();
  });
}

function pauseDownload() {
  console.log('[DEBUG] Pause request received.');
  isPaused = true;
  if (currentProcess) {
    try {
      currentProcess.kill('SIGINT');
    } catch (e) {
      console.error('[DEBUG] Failed to interrupt process on pause:', e);
    }
  }
  return { success: true };
}

function resumeDownload() {
  console.log('[DEBUG] Resume request received.');
  if (!isPaused) return { success: false, error: 'Not paused' };
  isPaused = false;
  processQueue();
  return { success: true };
}

function stopDownload() {
  console.log('[DEBUG] Stop request received.');
  isStopped = true;
  if (currentProcess) {
    try {
      currentProcess.kill('SIGINT');
    } catch (e) {
      console.error('[DEBUG] Failed to interrupt process on stop:', e);
    }
  } else if (activeResolve) {
    activeResolve({
      success: false,
      stopped: true,
      stats: getQueueStats()
    });
  }
  return { success: true };
}

module.exports = {
  checkDependencies,
  getVideoInfo,
  downloadMedia,
  pauseDownload,
  resumeDownload,
  stopDownload
};
