"""
YouTube scraper with dual approach:
- Primary: YouTube Data API v3 (requires YOUTUBE_API_KEY)
- Fallback: yt-dlp search (works without API key)
"""

import re
from pathlib import Path
from datetime import datetime

import yt_dlp

from backend.core.config import settings

# Only import Google API if available
try:
    from googleapiclient.discovery import build

    HAS_GOOGLE_API = True
except ImportError:
    HAS_GOOGLE_API = False


class YouTubeScraper:
    def __init__(self):
        self.api_key = settings.YOUTUBE_API_KEY
        self.service = None

    @property
    def has_api_key(self) -> bool:
        return bool(self.api_key)

    def _get_service(self):
        if not HAS_GOOGLE_API:
            raise ValueError(
                "google-api-python-client não instalado. "
                "Usando yt-dlp como alternativa."
            )
        if not self.api_key:
            raise ValueError(
                "YouTube API Key não configurada. "
                "Defina YOUTUBE_API_KEY no arquivo .env"
            )
        if self.service is None:
            self.service = build("youtube", "v3", developerKey=self.api_key)
        return self.service

    def search_trending(
        self,
        query: str = "",
        category_id: str = "",
        region: str = "BR",
        max_results: int = 25,
        order: str = "viewCount",
        published_after: str | None = None,
    ) -> list[dict]:
        """Search YouTube videos. Uses API if available, yt-dlp as fallback."""
        if self.has_api_key and HAS_GOOGLE_API:
            return self._search_api(
                query, category_id, region, max_results, order, published_after
            )
        return self._search_ytdlp(query or "trending", max_results)

    def get_trending_videos(
        self, region: str = "BR", category_id: str = "", max_results: int = 25
    ) -> list[dict]:
        """Get trending videos. Uses API if available, yt-dlp as fallback."""
        if self.has_api_key and HAS_GOOGLE_API:
            return self._trending_api(region, category_id, max_results)
        return self._trending_ytdlp(region, max_results)

    # ─── YouTube Data API methods ─────────────────────────────────

    def _search_api(
        self, query, category_id, region, max_results, order, published_after
    ) -> list[dict]:
        service = self._get_service()
        params = {
            "part": "snippet",
            "type": "video",
            "regionCode": region,
            "maxResults": min(max_results, 50),
            "order": order,
        }
        if query:
            params["q"] = query
        if category_id:
            params["videoCategoryId"] = category_id
        if published_after:
            params["publishedAfter"] = published_after

        response = service.search().list(**params).execute()
        video_ids = [
            item["id"]["videoId"]
            for item in response.get("items", [])
        ]
        if not video_ids:
            return []
        return self._get_video_details(video_ids)

    def _trending_api(self, region, category_id, max_results) -> list[dict]:
        service = self._get_service()
        params = {
            "part": "snippet,statistics,contentDetails",
            "chart": "mostPopular",
            "regionCode": region,
            "maxResults": min(max_results, 50),
        }
        if category_id:
            params["videoCategoryId"] = category_id
        response = service.videos().list(**params).execute()
        return [
            self._parse_api_video(item)
            for item in response.get("items", [])
        ]

    def _get_video_details(self, video_ids: list[str]) -> list[dict]:
        service = self._get_service()
        response = (
            service.videos()
            .list(
                part="snippet,statistics,contentDetails",
                id=",".join(video_ids),
            )
            .execute()
        )
        return [
            self._parse_api_video(item)
            for item in response.get("items", [])
        ]

    def _parse_api_video(self, item: dict) -> dict:
        snippet = item.get("snippet", {})
        stats = item.get("statistics", {})
        content = item.get("contentDetails", {})

        video_id = item["id"]
        if isinstance(video_id, dict):
            video_id = video_id.get("videoId", "")

        duration = self._parse_duration(content.get("duration", "PT0S"))

        tags = snippet.get("tags", [])
        description = snippet.get("description", "")
        hashtags = re.findall(r"#(\w+)", description)
        all_tags = list(set(tags + hashtags))

        published_at = None
        if snippet.get("publishedAt"):
            try:
                published_at = datetime.fromisoformat(
                    snippet["publishedAt"].replace("Z", "+00:00")
                )
            except (ValueError, TypeError):
                pass

        return {
            "platform": "youtube",
            "external_id": video_id,
            "url": f"https://www.youtube.com/watch?v={video_id}",
            "title": snippet.get("title", ""),
            "description": description[:500],
            "hashtags": all_tags[:20],
            "creator_username": snippet.get("channelTitle", ""),
            "creator_id_external": snippet.get("channelId", ""),
            "views": int(stats.get("viewCount", 0)),
            "likes": int(stats.get("likeCount", 0)),
            "comments": int(stats.get("commentCount", 0)),
            "shares": 0,
            "duration": duration,
            "thumbnail_url": snippet.get("thumbnails", {})
            .get("high", {})
            .get("url", ""),
            "published_at": published_at,
        }

    # ─── yt-dlp fallback methods ──────────────────────────────────

    def _search_ytdlp(
        self, query: str, max_results: int = 25
    ) -> list[dict]:
        """Search YouTube using yt-dlp (no API key needed)."""
        search_query = f"ytsearch{max_results}:{query}"
        return self._extract_ytdlp(search_query, max_results)

    def _trending_ytdlp(
        self, region: str = "BR", max_results: int = 25
    ) -> list[dict]:
        """Get trending YouTube videos using yt-dlp."""
        # yt-dlp can extract from YouTube trending page
        url = f"https://www.youtube.com/feed/trending?gl={region}"
        videos = self._extract_ytdlp(url, max_results)
        if not videos:
            # Fallback: search for trending content
            videos = self._search_ytdlp("trending", max_results)
        return videos

    def _extract_ytdlp(self, url: str, max_results: int) -> list[dict]:
        """Extract videos using yt-dlp."""
        opts = {
            "quiet": True,
            "no_warnings": True,
            "ignoreerrors": True,
            "no_color": True,
            "playlistend": max_results,
            "socket_timeout": 30,
            "extract_flat": False,
        }
        cookies_path = Path(settings.data_path) / "youtube_cookies.txt"
        if cookies_path.exists():
            opts["cookiefile"] = str(cookies_path)
        try:
            with yt_dlp.YoutubeDL(opts) as ydl:
                result = ydl.extract_info(url, download=False)
                if not result:
                    return []

                # Single video
                if "entries" not in result:
                    parsed = self._parse_ytdlp_entry(result)
                    return [parsed] if parsed else []

                # Playlist / search results
                videos = []
                for entry in result.get("entries") or []:
                    if entry is None:
                        continue
                    parsed = self._parse_ytdlp_entry(entry)
                    if parsed:
                        videos.append(parsed)
                    if len(videos) >= max_results:
                        break

                return videos
        except Exception as e:
            print(f"[YouTube] yt-dlp error: {e}")
            return []

    def _parse_ytdlp_entry(self, entry: dict) -> dict | None:
        """Parse a yt-dlp entry into our standard format."""
        try:
            video_id = str(entry.get("id", ""))
            if not video_id:
                return None

            uploader = entry.get("uploader", "") or entry.get("channel", "") or ""
            uploader_id = entry.get("uploader_id", "") or entry.get("channel_id", "") or ""

            description = entry.get("description", "") or ""
            title = entry.get("title", "") or ""
            hashtags = re.findall(r"#(\w+)", description)
            # Also get tags from yt-dlp
            tags = entry.get("tags") or []
            all_tags = list(set(hashtags + [str(t) for t in tags]))

            published_at = None
            ts = entry.get("timestamp")
            if ts:
                try:
                    published_at = datetime.fromtimestamp(int(ts))
                except (ValueError, TypeError, OSError):
                    pass
            elif entry.get("upload_date"):
                try:
                    published_at = datetime.strptime(
                        entry["upload_date"], "%Y%m%d"
                    )
                except ValueError:
                    pass

            return {
                "platform": "youtube",
                "external_id": video_id,
                "url": entry.get("webpage_url", "")
                or f"https://www.youtube.com/watch?v={video_id}",
                "title": title[:200],
                "description": description[:500],
                "hashtags": all_tags[:20],
                "creator_username": uploader,
                "creator_id_external": uploader_id,
                "views": int(entry.get("view_count") or 0),
                "likes": int(entry.get("like_count") or 0),
                "comments": int(entry.get("comment_count") or 0),
                "shares": 0,
                "duration": int(entry.get("duration") or 0),
                "thumbnail_url": entry.get("thumbnail", "") or "",
                "published_at": published_at,
            }
        except Exception as e:
            print(f"[YouTube] Parse error: {e}")
            return None

    # ─── Utilities ────────────────────────────────────────────────

    @staticmethod
    def _parse_duration(duration_str: str) -> int:
        match = re.match(
            r"PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?", duration_str
        )
        if not match:
            return 0
        hours = int(match.group(1) or 0)
        minutes = int(match.group(2) or 0)
        seconds = int(match.group(3) or 0)
        return hours * 3600 + minutes * 60 + seconds

    def get_categories(self, region: str = "BR") -> list[dict]:
        if not self.has_api_key or not HAS_GOOGLE_API:
            return []
        service = self._get_service()
        response = (
            service.videoCategories()
            .list(part="snippet", regionCode=region)
            .execute()
        )
        return [
            {"id": item["id"], "title": item["snippet"]["title"]}
            for item in response.get("items", [])
            if item["snippet"].get("assignable")
        ]
