#!/usr/bin/env python3
"""
Intelligent YouTube authentication handler with bot detection
Based on compendium project's successful implementation
"""

import re
import time
import logging
from typing import Dict, Any, Optional, List
from pathlib import Path
from datetime import datetime, timedelta
import yt_dlp
from .cookie_manager import CookieManager

logger = logging.getLogger(__name__)

class YouTubeAuthHandler:
    """Handle YouTube authentication with bot detection and recovery"""
    
    # Bot detection patterns from compendium
    BOT_DETECTION_PATTERNS = [
        r"sign in to confirm you're not a bot",
        r"this helps protect our community", 
        r"unusual traffic",
        r"automated requests",
        r"rate.*limit",
        r"HTTP Error 403",
        r"429 Too Many Requests",
        r"quota exceeded",
        r"temporarily blocked",
        r"suspicious activity",
        r"verify.*human",
        r"captcha",
        r"robot",
        r"please try again later",
        r"slow down",
        r"access denied",
        r"service unavailable"
    ]
    
    def __init__(self):
        self.cookie_manager = CookieManager()
        self.failure_count = 0
        self.last_failure_time = None
        self.cooldown_duration = 5 * 60  # 5 minutes
        self.mass_failure_threshold = 10  # Trigger recovery after 10 failures
        self.authenticated = False
        
    def is_bot_detection_error(self, error_message: str) -> bool:
        """Check if error message indicates bot detection"""
        
        error_lower = error_message.lower()
        for pattern in self.BOT_DETECTION_PATTERNS:
            if re.search(pattern, error_lower):
                logger.warning(f"Bot detection pattern matched: {pattern}")
                return True
        return False
    
    def is_in_cooldown(self) -> bool:
        """Check if we're in cooldown period"""
        
        if self.last_failure_time is None:
            return False
            
        elapsed = time.time() - self.last_failure_time
        return elapsed < self.cooldown_duration
    
    def record_failure(self, error_message: str):
        """Record authentication failure"""
        
        self.failure_count += 1
        self.last_failure_time = time.time()
        self.authenticated = False
        
        logger.error(f"Authentication failure #{self.failure_count}: {error_message}")
        
        if self.failure_count >= self.mass_failure_threshold:
            logger.critical(f"Mass failure detected ({self.failure_count} failures)")
            self._trigger_recovery()
    
    def record_success(self):
        """Record successful authentication"""
        
        self.failure_count = 0
        self.last_failure_time = None
        self.authenticated = True
        logger.info("Authentication successful - failure count reset")
    
    def _trigger_recovery(self):
        """Trigger recovery procedures after mass failures"""
        
        logger.info("Triggering authentication recovery procedures...")
        
        # Clean up old cookies
        self.cookie_manager.cleanup_old_backups(keep_count=3)
        
        # Force cooldown
        self.last_failure_time = time.time()
        
        logger.info(f"Recovery complete - entering {self.cooldown_duration}s cooldown")
    
    def get_ytdlp_options(self, include_auth: bool = True, use_browser_cookies: bool = True) -> Dict[str, Any]:
        """Get optimized yt-dlp options with 2025 authentication methods"""
        
        base_opts = {
            'quiet': True,
            'no_warnings': True,
            'writesubtitles': True,
            'writeautomaticsub': True,
            'subtitleslangs': ['en'],
            'socket_timeout': 30,
            'extractor_retries': 3,
            'fragment_retries': 10,
            'retry_sleep_functions': {'http': lambda n: min(10 * n, 60)},
            'skip_download': True,
            # Critical: Add sleep intervals as per compendium
            'sleep_interval_requests': 15,  # 15 seconds between requests (compendium uses 10+)
            'sleep_interval': 5,  # 5 seconds between downloads
            'max_sleep_interval': 30,  # Max sleep interval
            # Add rate limiting
            'ratelimit': 50000,  # 50KB/s to be more conservative
            'ignoreerrors': True,  # Continue on errors
            # 2025 User-Agent (latest Chrome)
            'user_agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
            'referer': 'https://www.youtube.com/',
            'http_headers': {
                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                'Accept-Language': 'en-us,en;q=0.5',
                'Accept-Encoding': 'gzip,deflate',
                'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
                'Keep-Alive': '300',
                'Connection': 'keep-alive',
            }
        }
        
        if include_auth:
            # Prioritize browser cookies as per yt-dlp 2025 recommendations
            if use_browser_cookies:
                try:
                    # Use Firefox browser cookies directly (2025 recommended method)
                    base_opts['cookiesfrombrowser'] = ('firefox', '/home/ben/snap/firefox/common/.mozilla/firefox/7a3tcyzf.default')
                    logger.debug("Using direct Firefox browser cookies (2025 method)")
                except Exception as e:
                    logger.warning(f"Browser cookie error: {e}")
                    # Fallback to auto-discovery
                    base_opts['cookiesfrombrowser'] = ('firefox',)
                    logger.debug("Using Firefox browser cookies with auto-discovery")
            else:
                # Fallback to cookie file method
                try:
                    cookie_path = self.cookie_manager.find_valid_cookies()
                    if cookie_path:
                        base_opts['cookiefile'] = str(cookie_path)
                        logger.debug(f"Using cookie file: {cookie_path}")
                    else:
                        logger.warning("No valid cookies found")
                except Exception as e:
                    logger.warning(f"Cookie management error: {e}")
        
        return base_opts
    
    def extract_video_info(self, video_url: str, max_retries: int = 3) -> Optional[Dict[str, Any]]:
        """Extract video info with 2025 authentication and retry logic"""
        
        if self.is_in_cooldown():
            remaining = self.cooldown_duration - (time.time() - self.last_failure_time)
            logger.warning(f"In cooldown - {remaining:.0f}s remaining")
            return None
        
        # Try both browser cookies and file cookies
        auth_methods = [
            ("browser_cookies", True),   # 2025 recommended method
            ("file_cookies", False)      # Fallback method
        ]
        
        for method_name, use_browser in auth_methods:
            logger.info(f"Trying authentication method: {method_name}")
            
            for attempt in range(max_retries):
                try:
                    ydl_opts = self.get_ytdlp_options(use_browser_cookies=use_browser)
                    
                    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                        logger.debug(f"Extracting video info ({method_name}, attempt {attempt + 1}/{max_retries}): {video_url}")
                        info = ydl.extract_info(video_url, download=False)
                        
                        if info:
                            logger.info(f"✅ Success with {method_name}")
                            self.record_success()
                            return info
                        
                except Exception as e:
                    error_msg = str(e)
                    logger.error(f"{method_name} attempt {attempt + 1} failed: {error_msg}")
                    
                    if self.is_bot_detection_error(error_msg):
                        self.record_failure(error_msg)
                        
                        # If bot detection with browser cookies, try longer delay
                        if use_browser and attempt < max_retries - 1:
                            delay = (attempt + 1) * 60  # 60s, 120s, 180s for browser method
                            logger.info(f"Bot detection with browser cookies - waiting {delay}s before retry")
                            time.sleep(delay)
                        elif attempt < max_retries - 1:
                            delay = (attempt + 1) * 30  # 30s, 60s, 90s for file method
                            logger.info(f"Bot detection - waiting {delay}s before retry")
                            time.sleep(delay)
                    else:
                        # Non-bot error, shorter delay
                        if attempt < max_retries - 1:
                            time.sleep(10)
            
            # If this method failed completely, try next method
            logger.warning(f"Method {method_name} failed after {max_retries} attempts")
        
        logger.error(f"All authentication methods failed after {max_retries} attempts each")
        return None
    
    def test_authentication(self) -> bool:
        """Test authentication with a known video"""
        
        test_video = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"  # Rick Roll - always available
        
        logger.info("Testing YouTube authentication...")
        info = self.extract_video_info(test_video, max_retries=1)
        
        if info:
            logger.info("✅ Authentication test successful")
            return True
        else:
            logger.error("❌ Authentication test failed")
            return False
    
    def get_status(self) -> Dict[str, Any]:
        """Get current authentication status"""
        
        cookie_path = self.cookie_manager.find_valid_cookies()
        
        status = {
            'authenticated': self.authenticated,
            'failure_count': self.failure_count,
            'in_cooldown': self.is_in_cooldown(),
            'cooldown_remaining': 0,
            'has_valid_cookies': cookie_path is not None,
            'cookie_path': str(cookie_path) if cookie_path else None,
        }
        
        if self.is_in_cooldown() and self.last_failure_time:
            status['cooldown_remaining'] = max(0, self.cooldown_duration - (time.time() - self.last_failure_time))
        
        return status
    
    def force_reauthentication(self):
        """Force re-authentication on next request"""
        
        logger.info("Forcing re-authentication...")
        self.authenticated = False
        self.failure_count = 0
        self.last_failure_time = None
    
    def update_cookies_from_browser(self) -> bool:
        """Update cookies from browser session - Compendium method"""
        
        logger.info("Attempting to update cookies from browser using compendium method...")
        
        # Snap Firefox path for this system
        browser_profiles = [
            ('firefox', '/home/ben/snap/firefox/common/.mozilla/firefox/7a3tcyzf.default'),
            ('firefox', None),  # Let yt-dlp auto-discover
            ('chrome', None),
            ('chromium', None)
        ]
        
        for browser, profile_path in browser_profiles:
            try:
                logger.info(f"Trying to extract cookies from {browser}" + (f" (profile: {profile_path})" if profile_path else ""))
                
                # Use yt-dlp to extract cookies from browser
                if profile_path:
                    temp_opts = {
                        'cookiesfrombrowser': (browser, profile_path),
                        'quiet': False,  # Enable output to see what's happening
                        'skip_download': True,
                        'no_warnings': False,
                    }
                else:
                    temp_opts = {
                        'cookiesfrombrowser': (browser,),
                        'quiet': False,
                        'skip_download': True, 
                        'no_warnings': False,
                    }
                
                # Test with a simple video first
                test_video = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
                
                logger.info(f"Testing {browser} cookies with test video...")
                with yt_dlp.YoutubeDL(temp_opts) as ydl:
                    info = ydl.extract_info(test_video, download=False)
                    
                    if info and not self.is_bot_detection_error(str(info)):
                        logger.info(f"✅ Successfully authenticated with {browser} cookies!")
                        
                        # Now save the working cookies
                        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
                        cookie_path = Path(f"data_production_backlog/.cookies/youtube_cookies_{browser}_{timestamp}.txt")
                        cookie_path.parent.mkdir(parents=True, exist_ok=True)
                        
                        save_opts = temp_opts.copy()
                        save_opts['cookiefile'] = str(cookie_path)
                        
                        logger.info(f"Saving working {browser} cookies to {cookie_path}")
                        with yt_dlp.YoutubeDL(save_opts) as ydl2:
                            # Save cookies by doing another extraction
                            ydl2.extract_info(test_video, download=False)
                        
                        if cookie_path.exists() and cookie_path.stat().st_size > 100:
                            # Update main cookie file using compendium atomic method
                            success = self.cookie_manager.update_cookies(cookie_path)
                            if success:
                                logger.info(f"✅ Cookies successfully updated from {browser}")
                                self.record_success()
                                return True
                        else:
                            logger.warning(f"Cookie file was not created or is too small: {cookie_path}")
                        
            except Exception as e:
                error_msg = str(e)
                logger.warning(f"Failed to extract cookies from {browser}: {error_msg}")
                
                # Check if this is a bot detection error
                if self.is_bot_detection_error(error_msg):
                    logger.error(f"Bot detection error with {browser} - this browser session may be flagged")
                continue
        
        logger.error("Failed to extract working cookies from any browser")
        return False

# Convenience functions
def get_auth_handler() -> YouTubeAuthHandler:
    """Get YouTube authentication handler"""
    return YouTubeAuthHandler()

def test_youtube_access() -> bool:
    """Test YouTube access"""
    handler = YouTubeAuthHandler()
    return handler.test_authentication()

def extract_youtube_video(video_url: str) -> Optional[Dict[str, Any]]:
    """Extract YouTube video with authentication"""
    handler = YouTubeAuthHandler()
    return handler.extract_video_info(video_url)