"""
TikTok scraper using yt-dlp.

What works:
- ✅ User/profile pages (@username) - full metadata
- ✅ Individual video URLs - full metadata
- ❌ Hashtag/tag pages - TikTok blocks automated access
- ❌ Search - requires authentication

Strategy: Focus on user pages for discovery, suggest popular creators.
"""

import re
from datetime import datetime

import yt_dlp


class TikTokScraper:
    """TikTok scraper using yt-dlp. Works best with user/profile pages."""

    # 100+ popular TikTok creators organized by niche for comprehensive discovery
    TRENDING_CREATORS = [
        # Mega creators (100M+ followers)
        "khaby.lame", "charlidamelio", "bellapoarch", "addisonre",
        "zachking", "mrbeast", "kimberly.loaiza", "willsmith",
        # Entertainment & Comedy
        "babyariel", "kallmekris", "drewafualo", "jasonderulo",
        "itsjojosiwa", "codyko", "emilyzugay", "comedydojo",
        # Dance & Music
        "avanigregg", "dixiedamelio", "justmaiko", "rikiofficial",
        "nickiminaj", "justinbieber", "dualipa", "badgalriri",
        # Lifestyle & Fashion
        "emilymariko", "matildadjerf", "wisdomkaye", "chifranco",
        # Food & Cooking
        "gordonramsayofficial", "saltbae", "cookingwithshereen",
        "emilymariko", "thecookingguy", "gaborbaacsi",
        # Tech & Education
        "tiktoktips", "howiexu", "techbrother", "mkbhd",
        # Sports & Fitness
        "cristiano", "neymarjr", "kingjames", "leomessi",
        "gymshark", "nike", "workout",
        # Beauty & Skincare
        "skincarebyhyram", "jamescharles", "mikaylanogueira",
        "nadiaokamoto", "patrickstarrr",
        # Brazilian creators
        "virginia", "tirfrancoo", "lufrancoo", "cafrancoo",
        "pequenalo", "manfrancoo", "gkay", "windfrancoo",
        "lufrancoo", "safrancoo", "rfrancoo",
        # Latin American creators
        "domelipa", "rodcontrerasc", "kunaguilera",
        "elrodcontreras", "danfrancoo",
        # Viral / Trending accounts
        "tiktok", "tiktokbrasil", "tiktokcreators",
        "viraltiktok", "dailytiktoks",
        # Memes & Humor
        "memezar", "pubity", "dailymemes",
        # Travel & Nature
        "natgeo", "discoverychannel",
        # News & Media
        "washingtonpost", "nbcnews", "espn",
    ]

    def __init__(self, cookies_file: str | None = None):
        self.cookies_file = cookies_file

    def _get_opts(self, max_results: int = 20) -> dict:
        opts = {
            "quiet": True,
            "no_warnings": True,
            "ignoreerrors": True,
            "no_color": True,
            "playlistend": max_results,
            "socket_timeout": 30,
        }
        if self.cookies_file:
            opts["cookiefile"] = self.cookies_file
        return opts

    def search_videos(
        self, query: str, max_results: int = 20
    ) -> list[dict]:
        """
        Search TikTok videos.
        Since TikTok blocks hashtag/search scraping,
        this tries the query as a username first.
        """
        query = query.strip().lstrip("@#")
        if not query:
            return []

        # Try as username first
        videos = self.get_user_videos(query, max_results)
        if videos:
            return videos

        # If that fails, try as hashtag (might work with cookies)
        return self.get_hashtag_videos(query, max_results)

    def get_hashtag_videos(
        self, hashtag: str, max_results: int = 20
    ) -> list[dict]:
        """
        Get videos for a hashtag.
        Note: This often fails due to TikTok's anti-bot measures.
        Consider using get_user_videos() instead.
        """
        tag = hashtag.strip().lstrip("#")
        if not tag:
            return []
        url = f"https://www.tiktok.com/tag/{tag}"
        videos = self._extract_videos(url, max_results)
        if not videos:
            print(
                f"[TikTok] Hashtag #{tag} retornou 0 vídeos. "
                "TikTok bloqueia scraping de hashtags. "
                "Use busca por perfil (@usuario) para melhores resultados."
            )
        return videos

    def get_user_videos(
        self, username: str, max_results: int = 20
    ) -> list[dict]:
        """
        Get videos from a TikTok user profile.
        This is the most reliable method - works without cookies!
        """
        username = username.strip().lstrip("@")
        if not username:
            return []
        url = f"https://www.tiktok.com/@{username}"
        return self._extract_videos(url, max_results)

    def get_trending_videos(self, max_results: int = 20) -> list[dict]:
        """
        Get trending videos by scraping popular creator profiles.
        Since TikTok doesn't expose trending via API,
        we collect recent videos from popular creators.
        """
        all_videos = []
        per_creator = max(3, max_results // len(self.TRENDING_CREATORS))

        for creator in self.TRENDING_CREATORS:
            videos = self.get_user_videos(creator, per_creator)
            all_videos.extend(videos)
            if len(all_videos) >= max_results:
                break

        # Sort by views (most viral first)
        all_videos.sort(key=lambda v: v.get("views", 0), reverse=True)
        return all_videos[:max_results]

    def get_video_info(self, video_url: str) -> dict | None:
        """Get details for a specific video URL. Works reliably."""
        opts = self._get_opts(1)
        opts["noplaylist"] = True
        try:
            with yt_dlp.YoutubeDL(opts) as ydl:
                info = ydl.extract_info(video_url, download=False)
                if info:
                    return self._parse_entry(info)
        except Exception as e:
            print(f"[TikTok] Error getting video info: {e}")
        return None

    def get_trending_hashtags(self) -> list[dict]:
        """Get trending hashtags (static fallback)."""
        return [
            {"platform": "tiktok", "trend_type": "hashtag", "name": "fyp"},
            {"platform": "tiktok", "trend_type": "hashtag", "name": "viral"},
            {"platform": "tiktok", "trend_type": "hashtag", "name": "trending"},
            {"platform": "tiktok", "trend_type": "hashtag", "name": "foryou"},
            {"platform": "tiktok", "trend_type": "hashtag", "name": "foryoupage"},
        ]

    def _extract_videos(self, url: str, max_results: int) -> list[dict]:
        """Extract videos from a TikTok URL using yt-dlp."""
        opts = self._get_opts(max_results)
        try:
            with yt_dlp.YoutubeDL(opts) as ydl:
                result = ydl.extract_info(url, download=False)
                if not result:
                    return []

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

                # Playlist (user page, tag page, etc.)
                videos = []
                for entry in result.get("entries") or []:
                    if entry is None:
                        continue
                    parsed = self._parse_entry(entry)
                    if parsed:
                        videos.append(parsed)
                    if len(videos) >= max_results:
                        break

                return videos
        except Exception as e:
            print(f"[TikTok] Extraction error for {url}: {e}")
            return []

    def _parse_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("creator", "")
                or ""
            )
            uploader_id = (
                entry.get("uploader_id", "")
                or entry.get("channel_id", "")
                or ""
            )

            description = entry.get("description", "") or ""
            title = entry.get("title", "") or description[:200]
            hashtags = re.findall(r"#(\w+)", description)

            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": "tiktok",
                "external_id": video_id,
                "url": entry.get("webpage_url", "")
                or f"https://www.tiktok.com/@{uploader_id}/video/{video_id}",
                "title": title[:200],
                "description": description[:500],
                "hashtags": hashtags[:20],
                "creator_username": uploader_id or uploader,
                "creator_display_name": uploader,
                "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": int(
                    entry.get("repost_count")
                    or entry.get("share_count")
                    or 0
                ),
                "duration": int(entry.get("duration") or 0),
                "thumbnail_url": entry.get("thumbnail", "") or "",
                "published_at": published_at,
            }
        except Exception as e:
            print(f"[TikTok] Parse error: {e}")
            return None

    def close(self):
        pass
