const express = require('express');
const router = express.Router();
const { spawn, exec } = require('child_process');
const axios = require('axios');
const https = require('https');
const { SocksProxyAgent } = require('socks-proxy-agent');
const { HttpsProxyAgent } = require('https-proxy-agent');
const { authenticate } = require('../middleware/auth');
const { heavyLimiter, readLimiter } = require('../middleware/rateLimiter');
const { db } = require('../database/db');
const logger = require('../utils/logger');

// HTTPS agent to bypass SSL certificate verification for IPTV streams
const httpsAgent = new https.Agent({
  rejectUnauthorized: false
});

// Check if user has active VPN connection and return appropriate agent
const getVPNAgent = async (userId) => {
  return new Promise((resolve) => {
    db.get(
      'SELECT connected FROM vpn_settings WHERE user_id = ? AND connected = 1',
      [userId],
      (err, row) => {
        if (err || !row) {
          // No VPN, use standard HTTPS agent
          resolve(httpsAgent);
        } else {
          // VPN active - traffic will automatically route through VPN interface
          // Use standard agent, OS routing will handle VPN
          logger.info('VPN active for user, traffic will route through VPN');
          resolve(httpsAgent);
        }
      }
    );
  });
};

// Check hardware acceleration availability
const checkHardwareAcceleration = () => {
  const capabilities = {
    quicksync: false,
    nvenc: false,
    vaapi: false,
    videotoolbox: false
  };
  
  // Check for Intel Quick Sync (typically /dev/dri/renderD128)
  try {
    const fs = require('fs');
    if (fs.existsSync('/dev/dri/renderD128')) {
      capabilities.quicksync = true;
      capabilities.vaapi = true;
    }
  } catch (err) {
    logger.debug('Quick Sync not available');
  }
  
  // Check for NVIDIA
  try {
    const { execSync } = require('child_process');
    execSync('nvidia-smi', { stdio: 'ignore' });
    capabilities.nvenc = true;
  } catch (err) {
    logger.debug('NVENC not available');
  }
  
  return capabilities;
};

// Get hardware acceleration capabilities
router.get('/capabilities', authenticate, readLimiter, (req, res) => {
  const capabilities = checkHardwareAcceleration();
  res.json(capabilities);
});

// Get user's stream settings
const getStreamSettings = (userId, callback) => {
  db.get(
    'SELECT value FROM settings WHERE user_id = ? AND key = ?',
    [userId, 'stream_settings'],
    (err, result) => {
      if (err || !result) {
        // Default settings
        return callback({
          hwaccel: 'auto',
          hwaccel_device: '/dev/dri/renderD128',
          codec: 'h264',
          preset: 'veryfast',
          buffer_size: '2M',
          max_bitrate: '8M'
        });
      }
      
      try {
        callback(JSON.parse(result.value));
      } catch {
        callback({
          hwaccel: 'auto',
          hwaccel_device: '/dev/dri/renderD128',
          codec: 'h264',
          preset: 'veryfast',
          buffer_size: '2M',
          max_bitrate: '8M'
        });
      }
    }
  );
};

// Universal proxy for all streams with geo-blocking bypass
router.get('/proxy/:channelId', authenticate, heavyLimiter, async (req, res) => {
  const { channelId } = req.params;
  console.log(`[STREAM] Proxy request for channel ${channelId}`);
  
  try {
    const channel = await new Promise((resolve, reject) => {
      db.get('SELECT url, name, is_radio FROM channels WHERE id = ?', [channelId], (err, row) => {
        if (err) {
          console.error('[STREAM] Database error:', err);
          reject(err);
        } else if (!row) {
          console.error('[STREAM] Channel not found:', channelId);
          reject(new Error('Channel not found'));
        } else {
          console.log('[STREAM] Found channel:', row.name);
          resolve(row);
        }
      });
    });

    if (!channel.url) {
      return res.status(400).json({ error: 'Channel has no URL' });
    }

    logger.info(`Proxying ${channel.is_radio ? 'radio' : 'video'} stream: ${channel.name} - ${channel.url}`);

    // Extract origin from URL for proper spoofing
    const urlObj = new URL(channel.url);
    const origin = `${urlObj.protocol}//${urlObj.hostname}`;
    const referer = origin;

    // Get VPN-aware agent for this user
    const agent = await getVPNAgent(req.user.userId);

    const requestConfig = {
      method: 'GET',
      url: channel.url,
      responseType: channel.url.includes('.m3u8') ? 'text' : 'stream',
      timeout: 30000,
      validateStatus: (status) => status < 500,
      httpsAgent: agent,
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
        'Accept': '*/*',
        'Accept-Language': 'en-US,en;q=0.9',
        'Accept-Encoding': 'identity',
        'Origin': origin,
        'Referer': referer,
        'Connection': 'keep-alive',
        'Sec-Fetch-Dest': 'empty',
        'Sec-Fetch-Mode': 'cors',
        'Sec-Fetch-Site': 'cross-site'
      }
    };

    // Proxy with origin spoofing to bypass geo-blocking
    const response = await axios(requestConfig);

    if (response.status >= 400) {
      logger.error(`Stream returned status ${response.status}`);
      return res.status(response.status).json({ error: 'Stream unavailable' });
    }

    // Handle HLS manifests - rewrite URLs to go through our proxy
    if (channel.url.includes('.m3u8')) {
      const m3u8Content = response.data;
      const baseUrl = channel.url.substring(0, channel.url.lastIndexOf('/') + 1);
      
      // Rewrite relative URLs in playlist to absolute URLs through our proxy
      const rewrittenContent = m3u8Content.split('\n').map(line => {
        if (line.startsWith('#') || line.trim() === '') {
          return line;
        }
        // Convert relative to absolute URL
        let absoluteUrl = line;
        if (!line.startsWith('http')) {
          absoluteUrl = baseUrl + line;
        }
        // Proxy the URL through our server
        const proxyUrl = `/api/stream/hls-segment?url=${encodeURIComponent(absoluteUrl)}&token=${req.query.token}`;
        return proxyUrl;
      }).join('\n');

      res.setHeader('Content-Type', 'application/vnd.apple.mpegurl');
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader('Access-Control-Allow-Headers', '*');
      res.setHeader('Cache-Control', 'no-cache');
      return res.send(rewrittenContent);
    }

    // For binary streams, pipe directly
    res.setHeader('Content-Type', response.headers['content-type'] || 'application/octet-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Headers', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS');
    
    if (response.headers['content-length']) {
      res.setHeader('Content-Length', response.headers['content-length']);
    }
    if (response.headers['content-range']) {
      res.setHeader('Content-Range', response.headers['content-range']);
      res.setHeader('Accept-Ranges', 'bytes');
    }

    response.data.pipe(res);

    response.data.on('error', (error) => {
      logger.error('Stream error:', error.message);
      if (!res.headersSent) {
        res.status(500).json({ error: 'Stream failed' });
      }
    });

    req.on('close', () => {
      logger.info('Client disconnected from stream');
      if (response.data && !response.data.destroyed) {
        response.data.destroy();
      }
    });

  } catch (error) {
    console.error('[STREAM] Proxy error:', error);
    logger.error('Proxy error:', error.message);
    if (!res.headersSent) {
      res.status(500).json({ error: 'Failed to proxy stream', details: error.message });
    }
  }
});

// Proxy HLS segments (playlists and .ts chunks)
router.get('/hls-segment', authenticate, heavyLimiter, async (req, res) => {
  const { url } = req.query;
  
  if (!url) {
    return res.status(400).json({ error: 'URL parameter required' });
  }

  try {
    const urlObj = new URL(url);
    const origin = `${urlObj.protocol}//${urlObj.hostname}`;

    logger.info(`Proxying HLS segment: ${url}`);

    const response = await axios({
      method: 'GET',
      url: url,
      responseType: url.includes('.m3u8') ? 'text' : 'stream',
      timeout: 15000,
      httpsAgent: httpsAgent,
      validateStatus: (status) => status < 500,
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        'Accept': '*/*',
        'Origin': origin,
        'Referer': origin,
        'Connection': 'keep-alive'
      }
    });

    if (response.status >= 400) {
      return res.status(response.status).json({ error: 'Segment unavailable' });
    }

    // Handle nested m3u8 playlists
    if (url.includes('.m3u8')) {
      const m3u8Content = response.data;
      const baseUrl = url.substring(0, url.lastIndexOf('/') + 1);
      
      const rewrittenContent = m3u8Content.split('\n').map(line => {
        if (line.startsWith('#') || line.trim() === '') {
          return line;
        }
        let absoluteUrl = line;
        if (!line.startsWith('http')) {
          absoluteUrl = baseUrl + line;
        }
        return `/api/stream/hls-segment?url=${encodeURIComponent(absoluteUrl)}&token=${req.query.token}`;
      }).join('\n');

      res.setHeader('Content-Type', 'application/vnd.apple.mpegurl');
      res.setHeader('Access-Control-Allow-Origin', '*');
      res.setHeader('Cache-Control', 'no-cache');
      return res.send(rewrittenContent);
    }

    // Stream binary segments
    res.setHeader('Content-Type', response.headers['content-type'] || 'video/mp2t');
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Cache-Control', 'public, max-age=300');
    
    if (response.headers['content-length']) {
      res.setHeader('Content-Length', response.headers['content-length']);
    }

    response.data.pipe(res);

    response.data.on('error', (error) => {
      logger.error('HLS segment error:', error.message);
      if (!res.headersSent) {
        res.status(500).end();
      }
    });

  } catch (error) {
    logger.error('HLS segment proxy error:', error.message);
    if (!res.headersSent) {
      res.status(500).json({ error: 'Failed to proxy segment' });
    }
  }
});

// Stream proxy with hardware acceleration (for transcoding if needed)
router.get('/proxy-ffmpeg/:channelId', authenticate, heavyLimiter, (req, res) => {
  const { channelId } = req.params;
  
  db.get(
    'SELECT url FROM channels WHERE id = ?',
    [channelId],
    (err, channel) => {
      if (err || !channel) {
        return res.status(404).json({ error: 'Channel not found' });
      }
      
      getStreamSettings(req.user.userId, (settings) => {
        const capabilities = checkHardwareAcceleration();
        
        // Build FFmpeg command with hardware acceleration
        const ffmpegArgs = [
          '-re',
          '-i', channel.url,
          '-c:v', 'copy',
          '-c:a', 'copy',
          '-f', 'mpegts',
          'pipe:1'
        ];
        
        // Add hardware acceleration if enabled and available
        if (settings.hwaccel !== 'none') {
          if (settings.hwaccel === 'quicksync' && capabilities.quicksync) {
            ffmpegArgs.unshift(
              '-hwaccel', 'qsv',
              '-hwaccel_device', settings.hwaccel_device || '/dev/dri/renderD128',
              '-hwaccel_output_format', 'qsv'
            );
          } else if (settings.hwaccel === 'vaapi' && capabilities.vaapi) {
            ffmpegArgs.unshift(
              '-hwaccel', 'vaapi',
              '-hwaccel_device', settings.hwaccel_device || '/dev/dri/renderD128',
              '-hwaccel_output_format', 'vaapi'
            );
          } else if (settings.hwaccel === 'nvenc' && capabilities.nvenc) {
            ffmpegArgs.unshift(
              '-hwaccel', 'cuda',
              '-hwaccel_output_format', 'cuda'
            );
          } else if (settings.hwaccel === 'auto') {
            // Auto-detect best available
            if (capabilities.quicksync) {
              ffmpegArgs.unshift(
                '-hwaccel', 'qsv',
                '-hwaccel_device', '/dev/dri/renderD128',
                '-hwaccel_output_format', 'qsv'
              );
            } else if (capabilities.nvenc) {
              ffmpegArgs.unshift(
                '-hwaccel', 'cuda',
                '-hwaccel_output_format', 'cuda'
              );
            }
          }
        }
        
        logger.info(`Streaming channel ${channelId} with args:`, ffmpegArgs);
        
        const ffmpeg = spawn('ffmpeg', ffmpegArgs);
        
        res.setHeader('Content-Type', 'video/mp2t');
        res.setHeader('Cache-Control', 'no-cache');
        
        ffmpeg.stdout.pipe(res);
        
        ffmpeg.stderr.on('data', (data) => {
          logger.debug(`FFmpeg: ${data}`);
        });
        
        ffmpeg.on('error', (error) => {
          logger.error('FFmpeg error:', error);
          if (!res.headersSent) {
            res.status(500).json({ error: 'Stream failed' });
          }
        });
        
        ffmpeg.on('close', (code) => {
          logger.info(`FFmpeg closed with code ${code}`);
        });
        
        req.on('close', () => {
          logger.info('Client disconnected, stopping stream');
          ffmpeg.kill('SIGTERM');
        });
      });
    }
  );
});

module.exports = router;
