"""YouTube site preset with oEmbed API and render support."""
import time
import random
import re
import json
import logging
import urllib.parse

logger = logging.getLogger("iploop.sites.youtube")


class YouTube:
    RATE_LIMIT = 5
    _last_request = 0

    def __init__(self, client):
        self.client = client

    def _rate_limit(self):
        elapsed = time.time() - YouTube._last_request
        if elapsed < self.RATE_LIMIT:
            time.sleep(self.RATE_LIMIT - elapsed + random.uniform(0, 2))
        YouTube._last_request = time.time()

    def _extract_video_data(self, html: str, video_id: str) -> dict:
        """Extract video data from YouTube HTML."""
        data = {"video_id": video_id}
        
        # Extract title from meta tags
        title_match = re.search(r'<meta property="og:title" content="([^"]*)"', html)
        if title_match:
            data['title'] = title_match.group(1)
        
        # Extract description from meta tags
        desc_match = re.search(r'<meta property="og:description" content="([^"]*)"', html)
        if desc_match:
            data['description'] = desc_match.group(1)
        
        # Extract view count from JSON-LD or page content
        views_match = re.search(r'"viewCount":"(\d+)"', html)
        if views_match:
            data['views'] = int(views_match.group(1))
        else:
            # Try alternative pattern
            views_match = re.search(r'(\d{1,3}(?:,\d{3})*|\d+(?:\.\d+)?[KMB]?)\s+views', html, re.IGNORECASE)
            if views_match:
                data['views'] = views_match.group(1)
        
        # Extract channel name
        channel_match = re.search(r'"author":"([^"]*)"', html)
        if channel_match:
            data['channel'] = channel_match.group(1)
        else:
            channel_match = re.search(r'<meta name="author" content="([^"]*)"', html)
            if channel_match:
                data['channel'] = channel_match.group(1)
        
        # Extract duration
        duration_match = re.search(r'"lengthSeconds":"(\d+)"', html)
        if duration_match:
            seconds = int(duration_match.group(1))
            minutes = seconds // 60
            seconds = seconds % 60
            data['duration'] = f"{minutes}:{seconds:02d}"
        
        # Extract upload date
        date_match = re.search(r'"uploadDate":"([^"]*)"', html)
        if date_match:
            data['upload_date'] = date_match.group(1)
        
        return data

    def _extract_text(self, html: str) -> str:
        """Strip HTML tags and return clean text."""
        import re
        # Remove HTML tags
        text = re.sub(r'<[^>]+>', '', html)
        # Clean up whitespace
        text = re.sub(r'\s+', ' ', text).strip()
        return text

    def _try_oembed_api(self, video_id: str) -> dict:
        """Try YouTube oEmbed API - parse JSON response directly."""
        url = f"https://www.youtube.com/oembed?url=https://www.youtube.com/watch?v={video_id}&format=json"
        
        try:
            resp = self.client.fetch(url, timeout=10, headers={'Accept': 'application/json'})
            
            if resp.status_code == 200:
                # Parse JSON response directly
                data = json.loads(resp.text)
                
                # For view count, scrape the page separately using proxy
                view_count = None
                try:
                    page_url = f"https://www.youtube.com/watch?v={video_id}"
                    page_resp = self.client.fetch(page_url, country="US")
                    if page_resp.status_code == 200:
                        views_match = re.search(r'"viewCount":"(\d+)"', page_resp.text)
                        if views_match:
                            view_count = int(views_match.group(1))
                except Exception:
                    pass
                
                return {
                    'video_id': video_id,
                    'status': 200,
                    'source': 'oembed',
                    'data': {
                        'title': data.get('title'),
                        'author_name': data.get('author_name'),
                        'thumbnail_url': data.get('thumbnail_url'),
                        'width': data.get('width'),
                        'height': data.get('height'),
                        'view_count': view_count
                    }
                }
        except Exception as e:
            logger.debug(f"YouTube oEmbed failed: {e}")
        
        return None

    def video(self, video_id, country="US", extract=True):
        """
        Fetch YouTube video with oEmbed API fallback.
        oEmbed is faster and doesn't need proxy/render.
        """
        # First try oEmbed API (no proxy needed)
        oembed_result = self._try_oembed_api(video_id)
        if oembed_result:
            return oembed_result
        
        # Fallback to full page fetch
        self._rate_limit()
        url = f"https://www.youtube.com/watch?v={video_id}"
        
        # Try render for better content
        if hasattr(self.client, 'render_fetch'):
            try:
                html = self.client.render_fetch(
                    url, 
                    country=country,
                    wait_for='#watch7-content',  # Wait for main content
                    wait_time=3
                )
                
                result = {
                    "video_id": video_id,
                    "url": url,
                    "status": 200,
                    "html": html,
                    "size_kb": len(html) // 1024,
                    "source": "render"
                }
                
                if extract:
                    result["data"] = self._extract_video_data(html, video_id)
                
                return result
                
            except Exception as e:
                logger.error(f"YouTube render failed: {e}")
        
        # Final fallback to HTTP
        resp = self.client.fetch(url, country=country)
        result = {
            "video_id": video_id,
            "url": url,
            "status": resp.status_code,
            "html": resp.text,
            "size_kb": len(resp.text) // 1024,
            "source": "http"
        }
        
        if extract and resp.status_code == 200:
            result["data"] = self._extract_video_data(resp.text, video_id)
        
        return result

    def search(self, query, country="US", extract=False):
        """
        Search YouTube. Requires render for best results.
        """
        self._rate_limit()
        encoded_query = urllib.parse.quote_plus(query)
        url = f"https://www.youtube.com/results?search_query={encoded_query}"
        
        # Search needs render for dynamic content
        if hasattr(self.client, 'render_fetch'):
            try:
                html = self.client.render_fetch(
                    url,
                    country=country,
                    wait_for='#contents',  # Wait for search results
                    wait_time=5
                )
                
                result = {
                    "query": query,
                    "url": url,
                    "status": 200,
                    "html": html,
                    "size_kb": len(html) // 1024,
                    "source": "render"
                }
                
                if extract:
                    result["results"] = self._extract_search_results(html)
                
                return result
                
            except Exception as e:
                logger.error(f"YouTube search render failed: {e}")
        
        # Fallback to HTTP
        resp = self.client.fetch(url, country=country)
        return {
            "query": query,
            "url": url,
            "status": resp.status_code,
            "html": resp.text,
            "size_kb": len(resp.text) // 1024,
            "source": "http"
        }

    def _extract_search_results(self, html: str) -> list:
        """Extract search results from YouTube HTML."""
        results = []
        
        # YouTube search results are in JSON scripts
        script_matches = re.finditer(r'var ytInitialData = ({.+?});', html)
        
        for script_match in script_matches:
            try:
                data = json.loads(script_match.group(1))
                
                # Navigate to contents
                contents = (data.get('contents', {})
                           .get('twoColumnSearchResultsRenderer', {})
                           .get('primaryContents', {})
                           .get('sectionListRenderer', {})
                           .get('contents', []))
                
                for section in contents:
                    items = section.get('itemSectionRenderer', {}).get('contents', [])
                    
                    for item in items:
                        if 'videoRenderer' in item:
                            video = item['videoRenderer']
                            
                            title = ""
                            title_runs = video.get('title', {}).get('runs', [])
                            if title_runs:
                                title = ''.join(run.get('text', '') for run in title_runs)
                            
                            video_id = video.get('videoId', '')
                            
                            # Get view count
                            views = ""
                            view_text = video.get('viewCountText', {})
                            if isinstance(view_text, dict):
                                views = view_text.get('simpleText', '')
                            
                            # Get channel name
                            channel = ""
                            channel_data = video.get('ownerText', {}).get('runs', [])
                            if channel_data:
                                channel = channel_data[0].get('text', '')
                            
                            if title and video_id:
                                results.append({
                                    'title': title,
                                    'video_id': video_id,
                                    'url': f'https://www.youtube.com/watch?v={video_id}',
                                    'channel': channel,
                                    'views': views
                                })
                
                break  # Found results, no need to check other scripts
                
            except (json.JSONDecodeError, KeyError):
                continue
        
        return results

    def channel(self, channel_name, country="US"):
        """Fetch a YouTube channel page."""
        self._rate_limit()
        
        # Handle both @channel and channel/UCxxxxx formats
        if channel_name.startswith('@'):
            url = f"https://www.youtube.com/{channel_name}"
        elif channel_name.startswith('UC') and len(channel_name) == 24:
            url = f"https://www.youtube.com/channel/{channel_name}"
        else:
            url = f"https://www.youtube.com/@{channel_name}"
        
        # Try render for better content
        if hasattr(self.client, 'render_fetch'):
            try:
                html = self.client.render_fetch(url, country=country, wait_time=3)
                return {
                    "channel": channel_name,
                    "url": url,
                    "status": 200,
                    "html": html,
                    "source": "render"
                }
            except Exception as e:
                logger.error(f"YouTube channel render failed: {e}")
        
        # Fallback to HTTP
        resp = self.client.fetch(url, country=country)
        return {
            "channel": channel_name,
            "url": url,
            "status": resp.status_code,
            "html": resp.text,
            "source": "http"
        }
