import os
import time
import random
import json
from typing import Any, Dict, List, Optional
from datetime import datetime
from pathlib import Path
import yt_dlp
from src.base_scraper import BaseScraper, ScraperConfig


class YouTubeScraper(BaseScraper):
    """YouTube channel scraper using yt-dlp."""
    
    def __init__(self, config: ScraperConfig):
        super().__init__(config)
        self.username = os.getenv('YOUTUBE_USERNAME')
        self.password = os.getenv('YOUTUBE_PASSWORD')
        self.channel_url = os.getenv('YOUTUBE_CHANNEL_URL', 'https://www.youtube.com/@HVACKnowItAll')
        # Use videos tab URL to get individual videos instead of playlists
        self.videos_url = self.channel_url.rstrip('/') + '/videos'
        
        # Cookies file for session persistence
        self.cookies_file = self.config.data_dir / '.cookies' / 'youtube_cookies.txt'
        self.cookies_file.parent.mkdir(parents=True, exist_ok=True)
        
        # User agents for rotation
        self.user_agents = [
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
            'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        ]

    def _get_ydl_options(self) -> Dict[str, Any]:
        """Get yt-dlp options with authentication and rate limiting."""
        options = {
            'quiet': True,
            'no_warnings': True,
            'extract_flat': False,  # Get full video info
            'ignoreerrors': True,  # Continue on error
            'cookiefile': str(self.cookies_file),
            'cookiesfrombrowser': None,  # Don't use browser cookies
            'username': self.username,
            'password': self.password,
            'ratelimit': 100000,  # 100KB/s rate limit
            'sleep_interval': 1,  # Sleep between downloads
            'max_sleep_interval': 3,
            'user_agent': random.choice(self.user_agents),
            'referer': 'https://www.youtube.com/',
            'add_header': ['Accept-Language:en-US,en;q=0.9'],
        }
        
        # Add proxy if configured
        proxy = os.getenv('YOUTUBE_PROXY')
        if proxy:
            options['proxy'] = proxy
        
        return options

    def _humanized_delay(self, min_seconds: float = 2, max_seconds: float = 5) -> None:
        """Add humanized random delay between requests."""
        delay = random.uniform(min_seconds, max_seconds)
        self.logger.debug(f"Waiting {delay:.2f} seconds...")
        time.sleep(delay)

    def fetch_channel_videos(self, max_videos: int = 50) -> List[Dict[str, Any]]:
        """Fetch video list from YouTube channel."""
        videos = []
        
        try:
            self.logger.info(f"Fetching videos from channel: {self.videos_url}")
            
            ydl_opts = self._get_ydl_options()
            ydl_opts['extract_flat'] = True  # Just get video list, not full info
            ydl_opts['playlistend'] = max_videos
            
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                channel_info = ydl.extract_info(self.videos_url, download=False)
                
                if 'entries' in channel_info:
                    # Filter out None entries and get actual videos
                    videos = [v for v in channel_info['entries'] if v is not None]
                    self.logger.info(f"Found {len(videos)} videos in channel")
                else:
                    self.logger.warning("No entries found in channel info")
            
            # Save cookies for next session
            if self.cookies_file.exists():
                self.logger.debug("Cookies saved for next session")
                
        except Exception as e:
            self.logger.error(f"Error fetching channel videos: {e}")
        
        return videos

    def fetch_video_details(self, video_id: str) -> Optional[Dict[str, Any]]:
        """Fetch detailed information for a specific video."""
        try:
            video_url = f"https://www.youtube.com/watch?v={video_id}"
            
            ydl_opts = self._get_ydl_options()
            ydl_opts['extract_flat'] = False  # Get full video info
            
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                video_info = ydl.extract_info(video_url, download=False)
                return video_info
                
        except Exception as e:
            self.logger.error(f"Error fetching video {video_id}: {e}")
            return None

    def _get_video_type(self, video: Dict[str, Any]) -> str:
        """Determine video type (video, short, live)."""
        duration = video.get('duration', 0)
        is_live = video.get('is_live', False)
        
        if is_live:
            return 'live'
        elif duration and duration < 60:  # Less than 60 seconds
            return 'short'
        else:
            return 'video'

    def fetch_content(self) -> List[Dict[str, Any]]:
        """Fetch and enrich video content with rate limiting."""
        # First get list of videos
        videos = self.fetch_channel_videos()
        
        if not videos:
            return []
        
        # Enrich each video with detailed information
        enriched_videos = []
        
        for i, video in enumerate(videos):
            try:
                video_id = video.get('id')
                if not video_id:
                    continue
                
                self.logger.info(f"Fetching details for video {i+1}/{len(videos)}: {video_id}")
                
                # Add humanized delay between requests
                if i > 0:
                    self._humanized_delay()
                
                # Fetch full video details
                detailed_info = self.fetch_video_details(video_id)
                
                if detailed_info:
                    # Add video type
                    detailed_info['type'] = self._get_video_type(detailed_info)
                    enriched_videos.append(detailed_info)
                    
                    # Extra delay after every 5 videos
                    if (i + 1) % 5 == 0:
                        self.logger.info("Taking longer break after 5 videos...")
                        self._humanized_delay(5, 10)
                
            except Exception as e:
                self.logger.error(f"Error enriching video {video.get('id')}: {e}")
                continue
        
        self.logger.info(f"Successfully enriched {len(enriched_videos)} videos")
        return enriched_videos

    def format_markdown(self, videos: List[Dict[str, Any]]) -> str:
        """Format videos as markdown."""
        markdown_sections = []
        
        for video in videos:
            section = []
            
            # ID
            video_id = video.get('id', 'N/A')
            section.append(f"# ID: {video_id}")
            section.append("")
            
            # Title
            title = video.get('title', 'Untitled')
            section.append(f"## Title: {title}")
            section.append("")
            
            # Type
            video_type = video.get('type', self._get_video_type(video))
            section.append(f"## Type: {video_type}")
            section.append("")
            
            # Author/Uploader
            author = video.get('uploader', 'Unknown')
            section.append(f"## Author: {author}")
            section.append("")
            
            # Link
            link = video.get('webpage_url', f"https://www.youtube.com/watch?v={video_id}")
            section.append(f"## Link: {link}")
            section.append("")
            
            # Upload Date
            upload_date = video.get('upload_date', '')
            if upload_date and len(upload_date) == 8:  # YYYYMMDD format
                formatted_date = f"{upload_date[:4]}-{upload_date[4:6]}-{upload_date[6:]}"
                section.append(f"## Upload Date: {formatted_date}")
            else:
                section.append(f"## Upload Date: {upload_date}")
            section.append("")
            
            # Views
            view_count = video.get('view_count', 0)
            section.append(f"## Views: {view_count}")
            section.append("")
            
            # Likes
            like_count = video.get('like_count', 0)
            section.append(f"## Likes: {like_count}")
            section.append("")
            
            # Comments
            comment_count = video.get('comment_count', 0)
            section.append(f"## Comments: {comment_count}")
            section.append("")
            
            # Duration
            duration = video.get('duration', 0)
            section.append(f"## Duration: {duration} seconds")
            section.append("")
            
            # Tags
            tags = video.get('tags', [])
            if tags:
                tags_str = ', '.join(tags[:10])  # Limit to first 10 tags
                section.append(f"## Tags: {tags_str}")
                section.append("")
            
            # Thumbnail
            thumbnail = video.get('thumbnail', '')
            if thumbnail:
                section.append(f"## Thumbnail: {thumbnail}")
                section.append("")
            
            # Description
            section.append("## Description:")
            description = video.get('description', '')
            if description:
                # Limit description to first 500 characters
                if len(description) > 500:
                    description = description[:500] + "..."
                section.append(description)
            section.append("")
            
            # Separator
            section.append("-" * 50)
            section.append("")
            
            markdown_sections.append('\n'.join(section))
        
        return '\n'.join(markdown_sections)

    def get_incremental_items(self, items: List[Dict[str, Any]], state: Dict[str, Any]) -> List[Dict[str, Any]]:
        """Get only new videos since last sync."""
        if not state:
            return items
        
        last_video_id = state.get('last_video_id')
        last_video_date = state.get('last_video_date')
        
        if not last_video_id:
            return items
        
        # Filter for videos newer than the last synced
        new_items = []
        for item in items:
            video_id = item.get('id')
            upload_date = item.get('upload_date', '')
            
            # Check if this is a new video
            if video_id == last_video_id:
                break  # Found the last synced video, stop here
            
            # Also check by date as backup
            if upload_date and last_video_date and upload_date <= last_video_date:
                continue
            
            new_items.append(item)
        
        return new_items

    def update_state(self, state: Dict[str, Any], items: List[Dict[str, Any]]) -> Dict[str, Any]:
        """Update state with latest video information."""
        if not items:
            return state
        
        # Get the first item (most recent)
        latest_item = items[0]
        
        state['last_video_id'] = latest_item.get('id')
        state['last_video_date'] = latest_item.get('upload_date')
        state['last_video_title'] = latest_item.get('title')
        state['last_sync'] = datetime.now(self.tz).isoformat()
        state['video_count'] = len(items)
        
        return state