"""
TikTok Creative Center scraper.

Two modes:
1. API mode: Uses Creative Center internal API (needs session cookie)
2. HTML mode: Scrapes the public Creative Center webpage (free, no auth)

TikTok now requires auth for their API endpoints, so we provide both.
The HTML scraper extracts data from the server-rendered page.
"""

import json
import re
import httpx
from datetime import datetime
from bs4 import BeautifulSoup


class TikTokCreativeCenter:
    """Scrape trending data from TikTok Creative Center."""

    BASE_URL = "https://ads.tiktok.com/creative_radar_api/v1"

    HEADERS = {
        "User-Agent": (
            "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
            "AppleWebKit/537.36 (KHTML, like Gecko) "
            "Chrome/120.0.0.0 Safari/537.36"
        ),
        "Accept": "application/json, text/plain, */*",
        "Referer": "https://ads.tiktok.com/business/creativecenter/inspiration/popular/hashtag/pc/en",
        "Origin": "https://ads.tiktok.com",
    }

    # Public Creative Center pages (no auth needed)
    PAGES = {
        "hashtags": "https://ads.tiktok.com/business/creativecenter/inspiration/popular/hashtag/pc/en",
        "songs": "https://ads.tiktok.com/business/creativecenter/inspiration/popular/music/pc/en",
        "creators": "https://ads.tiktok.com/business/creativecenter/inspiration/popular/creator/pc/en",
        "videos": "https://ads.tiktok.com/business/creativecenter/inspiration/popular/video/pc/en",
    }

    def __init__(self, session_cookie: str = "", timeout: int = 30):
        self.session_cookie = session_cookie
        self.timeout = timeout

    def _get_headers(self) -> dict:
        headers = {**self.HEADERS}
        if self.session_cookie:
            headers["Cookie"] = self.session_cookie
        return headers

    def get_trending_hashtags(
        self,
        region: str = "BR",
        period: int = 7,
        limit: int = 50,
    ) -> list[dict]:
        """
        Get trending hashtags from TikTok Creative Center.
        Tries API first, falls back to HTML scraping.
        """
        # Try API (needs auth cookie)
        if self.session_cookie:
            result = self._api_hashtags(region, period, limit)
            if result:
                return result

        # Fallback: scrape the HTML page
        return self._scrape_page_data("hashtags", region)

    def get_trending_songs(
        self,
        region: str = "BR",
        period: int = 7,
        limit: int = 50,
    ) -> list[dict]:
        """Get trending songs/sounds."""
        if self.session_cookie:
            result = self._api_songs(region, period, limit)
            if result:
                return result
        return self._scrape_page_data("songs", region)

    def get_trending_creators(
        self,
        region: str = "BR",
        period: int = 7,
        limit: int = 50,
    ) -> list[dict]:
        """Get trending creators."""
        if self.session_cookie:
            result = self._api_creators(region, period, limit)
            if result:
                return result
        return self._scrape_page_data("creators", region)

    def get_top_videos(
        self,
        region: str = "BR",
        period: int = 7,
        limit: int = 50,
    ) -> list[dict]:
        """Get top-performing videos."""
        if self.session_cookie:
            result = self._api_videos(region, period, limit)
            if result:
                return result
        return self._scrape_page_data("videos", region)

    # ─── API methods (need session cookie) ─────────────────────

    def _api_hashtags(self, region, period, limit) -> list[dict]:
        url = f"{self.BASE_URL}/popular_trend/hashtag/list"
        params = {"page": 1, "limit": min(limit, 50), "period": period, "country_code": region}
        data = self._api_request(url, params)
        items = data.get("data", {}).get("list", []) if data else []
        results = []
        for item in items:
            results.append({
                "platform": "tiktok",
                "trend_type": "hashtag",
                "name": item.get("hashtag_name", ""),
                "video_count": item.get("video_count", 0),
                "view_count": item.get("publish_cnt", 0) or item.get("video_count", 0),
                "trend_value": item.get("trend", 0),
                "region": region,
                "collected_at": datetime.utcnow().isoformat(),
            })
        return results

    def _api_songs(self, region, period, limit) -> list[dict]:
        url = f"{self.BASE_URL}/popular_trend/sound/list"
        params = {"page": 1, "limit": min(limit, 50), "period": period, "country_code": region}
        data = self._api_request(url, params)
        items = data.get("data", {}).get("list", []) if data else []
        results = []
        for item in items:
            results.append({
                "platform": "tiktok",
                "trend_type": "music",
                "name": item.get("music_name", "") or item.get("title", ""),
                "artist": item.get("author", ""),
                "video_count": item.get("video_count", 0),
                "region": region,
                "collected_at": datetime.utcnow().isoformat(),
            })
        return results

    def _api_creators(self, region, period, limit) -> list[dict]:
        url = f"{self.BASE_URL}/popular_trend/creator/list"
        params = {"page": 1, "limit": min(limit, 50), "period": period, "country_code": region}
        data = self._api_request(url, params)
        items = data.get("data", {}).get("list", []) if data else []
        results = []
        for item in items:
            results.append({
                "platform": "tiktok",
                "trend_type": "creator",
                "username": item.get("nick_name", "") or item.get("unique_id", ""),
                "unique_id": item.get("unique_id", ""),
                "followers": item.get("follower_cnt", 0),
                "likes": item.get("like_cnt", 0),
                "region": region,
                "collected_at": datetime.utcnow().isoformat(),
            })
        return results

    def _api_videos(self, region, period, limit) -> list[dict]:
        url = f"{self.BASE_URL}/popular_trend/list"
        params = {"page": 1, "limit": min(limit, 50), "period": period, "country_code": region}
        data = self._api_request(url, params)
        items = data.get("data", {}).get("list", []) if data else []
        return self._parse_video_items(items)

    def _api_request(self, url: str, params: dict) -> dict | None:
        try:
            with httpx.Client(timeout=self.timeout) as client:
                resp = client.get(url, params=params, headers=self._get_headers())
                if resp.status_code == 200:
                    data = resp.json()
                    if data.get("code") == 0:
                        return data
                    print(f"[TikTok CC API] code={data.get('code')}, msg={data.get('msg')}")
                return None
        except Exception as e:
            print(f"[TikTok CC API] Error: {e}")
            return None

    # ─── HTML scraping (no auth needed) ────────────────────────

    def _scrape_page_data(self, page_type: str, region: str = "BR") -> list[dict]:
        """
        Scrape TikTok Creative Center pages for embedded JSON data.
        The pages contain __NEXT_DATA__ with server-rendered content.
        """
        url = self.PAGES.get(page_type, "")
        if not url:
            return []

        try:
            headers = {
                "User-Agent": self.HEADERS["User-Agent"],
                "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                "Accept-Language": "en-US,en;q=0.9",
            }
            with httpx.Client(timeout=self.timeout, follow_redirects=True) as client:
                resp = client.get(url, headers=headers)
                if resp.status_code != 200:
                    print(f"[TikTok CC HTML] HTTP {resp.status_code} for {page_type}")
                    return []

            # Try to extract __NEXT_DATA__ JSON
            soup = BeautifulSoup(resp.text, "html.parser")
            script_tag = soup.find("script", {"id": "__NEXT_DATA__"})
            if script_tag and script_tag.string:
                try:
                    next_data = json.loads(script_tag.string)
                    return self._extract_from_next_data(next_data, page_type, region)
                except json.JSONDecodeError:
                    pass

            # Try to find embedded JSON in any script tag
            for script in soup.find_all("script"):
                if script.string and "hashtag_name" in (script.string or ""):
                    try:
                        # Find JSON objects in the script
                        matches = re.findall(r'\{[^{}]*"hashtag_name"[^{}]*\}', script.string)
                        results = []
                        for m in matches:
                            item = json.loads(m)
                            results.append({
                                "platform": "tiktok",
                                "trend_type": "hashtag",
                                "name": item.get("hashtag_name", ""),
                                "video_count": item.get("video_count", 0),
                                "region": region,
                                "collected_at": datetime.utcnow().isoformat(),
                            })
                        if results:
                            return results
                    except (json.JSONDecodeError, Exception):
                        continue

            print(f"[TikTok CC HTML] No embedded data found for {page_type}")
            return []

        except Exception as e:
            print(f"[TikTok CC HTML] Error scraping {page_type}: {e}")
            return []

    def _extract_from_next_data(
        self, next_data: dict, page_type: str, region: str
    ) -> list[dict]:
        """Extract trending data from __NEXT_DATA__ JSON."""
        # Navigate through Next.js data structure
        props = next_data.get("props", {}).get("pageProps", {})
        if not props:
            return []

        # The data might be in different paths depending on page type
        data_sources = [
            props.get("dehydratedState", {}).get("queries", []),
            [props],
        ]

        for source in data_sources:
            if isinstance(source, list):
                for query in source:
                    state = query.get("state", {}) if isinstance(query, dict) else {}
                    data = state.get("data", query) if state else query
                    if not isinstance(data, dict):
                        continue

                    items = (
                        data.get("list", [])
                        or data.get("data", {}).get("list", [])
                        or data.get("itemList", [])
                    )
                    if items:
                        return self._parse_items_by_type(items, page_type, region)

        return []

    def _parse_items_by_type(
        self, items: list, page_type: str, region: str
    ) -> list[dict]:
        """Parse items based on the page type."""
        results = []
        now = datetime.utcnow().isoformat()

        for item in items:
            if not isinstance(item, dict):
                continue

            if page_type == "hashtags":
                name = item.get("hashtag_name", "") or item.get("name", "")
                if name:
                    results.append({
                        "platform": "tiktok",
                        "trend_type": "hashtag",
                        "name": name,
                        "video_count": item.get("video_count", 0),
                        "view_count": item.get("publish_cnt", 0),
                        "region": region,
                        "collected_at": now,
                    })
            elif page_type == "songs":
                name = item.get("music_name", "") or item.get("title", "")
                if name:
                    results.append({
                        "platform": "tiktok",
                        "trend_type": "music",
                        "name": name,
                        "artist": item.get("author", ""),
                        "video_count": item.get("video_count", 0),
                        "region": region,
                        "collected_at": now,
                    })
            elif page_type == "creators":
                uid = item.get("unique_id", "") or item.get("nick_name", "")
                if uid:
                    results.append({
                        "platform": "tiktok",
                        "trend_type": "creator",
                        "username": item.get("nick_name", uid),
                        "unique_id": item.get("unique_id", ""),
                        "followers": item.get("follower_cnt", 0),
                        "region": region,
                        "collected_at": now,
                    })
            elif page_type == "videos":
                results.extend(self._parse_video_items([item]))

        return results

    def _parse_video_items(self, items: list) -> list[dict]:
        """Parse video items into standard format."""
        results = []
        for item in items:
            if not isinstance(item, dict):
                continue
            video_url = item.get("video_link", "") or ""
            if not video_url and item.get("item_id"):
                video_url = f"https://www.tiktok.com/@/video/{item['item_id']}"

            results.append({
                "platform": "tiktok",
                "external_id": str(item.get("item_id", "") or item.get("id", "")),
                "url": video_url,
                "title": (item.get("text", "") or "")[:200],
                "description": (item.get("text", "") or "")[:500],
                "hashtags": [],
                "creator_username": item.get("author_name", "") or item.get("nickname", ""),
                "views": int(item.get("play_cnt", 0) or item.get("vv", 0) or 0),
                "likes": int(item.get("like_cnt", 0) or item.get("digg_count", 0) or 0),
                "comments": int(item.get("comment_cnt", 0) or 0),
                "shares": int(item.get("share_cnt", 0) or 0),
                "duration": int(item.get("duration", 0) or 0),
                "thumbnail_url": item.get("cover_url", "") or item.get("cover", ""),
                "published_at": None,
            })
        return results
