#!/usr/bin/env python

"""
HTML/JSON parsing utilities for crawl4weibo
"""

import re
from datetime import datetime
from typing import Any, Dict, List, Optional

from ..exceptions.base import ParseError
from .logger import get_logger


class WeiboParser:
    """Parser for Weibo API responses and HTML content"""

    def __init__(self):
        self.logger = get_logger()

    def parse_user_info(self, response_data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Parse user information from API response

        Args:
            response_data: Raw API response data

        Returns:
            Dict containing parsed user information
        """
        try:
            if "data" not in response_data or "userInfo" not in response_data["data"]:
                raise ParseError("Invalid user info response format")

            user_info = response_data["data"]["userInfo"]

            return {
                "id": str(user_info.get("id", "")),
                "screen_name": user_info.get("screen_name", ""),
                "gender": user_info.get("gender", ""),
                "description": user_info.get("description", ""),
                "followers_count": user_info.get("followers_count", 0),
                "following_count": user_info.get("follow_count", 0),
                "posts_count": user_info.get("statuses_count", 0),
                "verified": user_info.get("verified", False),
                "verified_reason": user_info.get("verified_reason", ""),
                "avatar_url": user_info.get("profile_image_url", ""),
                "cover_image_url": user_info.get("cover_image_phone", ""),
            }
        except Exception as e:
            self.logger.error(f"Failed to parse user info: {e}")
            raise ParseError(f"Failed to parse user info: {e}")

    def parse_posts(self, response_data: Dict[str, Any]) -> List[Dict[str, Any]]:
        """
        Parse posts from API response

        Args:
            response_data: Raw API response data

        Returns:
            List of parsed post dictionaries
        """
        try:
            if "data" not in response_data or "cards" not in response_data["data"]:
                return []

            posts = []
            cards = response_data["data"]["cards"]

            for card in cards:
                if card.get("card_type") == 9 and "mblog" in card:
                    post_data = self._parse_single_post(card["mblog"])
                    if post_data:
                        posts.append(post_data)
                elif card.get("card_type") == 11 and "card_group" in card:
                    card_group = card.get("card_group", [])
                    for group_card in card_group:
                        if group_card.get("card_type") == 9 and "mblog" in group_card:
                            post_data = self._parse_single_post(group_card["mblog"])
                            if post_data:
                                posts.append(post_data)

            return posts
        except Exception as e:
            self.logger.error(f"Failed to parse posts: {e}")
            raise ParseError(f"Failed to parse posts: {e}")

    def _parse_single_post(self, mblog: Dict[str, Any]) -> Optional[Dict[str, Any]]:
        try:
            post = {
                "id": str(mblog.get("id", "")),
                "bid": str(mblog.get("bid", "")),
                "user_id": str(mblog.get("user", {}).get("id", "")),
                "text": self._clean_text(mblog.get("text", "")),
                "created_at": self._parse_time(mblog.get("created_at", "")),
                "source": mblog.get("source", ""),
                "reposts_count": mblog.get("reposts_count", 0),
                "comments_count": mblog.get("comments_count", 0),
                "attitudes_count": mblog.get("attitudes_count", 0),
                "pic_urls": self._extract_pic_urls(mblog),
                "video_url": self._extract_video_url(mblog),
                "is_original": not mblog.get("retweeted_status"),
                "location": mblog.get("geo", {}).get("name", ""),
                "topic_ids": self._extract_topics(mblog.get("text", "")),
                "at_users": self._extract_at_users(mblog.get("text", "")),
                "is_long_text": mblog.get("isLongText", False),
            }

            if mblog.get("retweeted_status"):
                post["retweeted_status"] = self._parse_single_post(
                    mblog["retweeted_status"]
                )

            return post
        except Exception as e:
            self.logger.error(f"Failed to parse single post: {e}")
            return None

    def _clean_text(self, text: str) -> str:
        if not text:
            return ""

        text = re.sub(r"<[^>]+>", "", text)
        text = re.sub(r"\s+", " ", text).strip()
        return text

    def _parse_time(self, time_str: str) -> Optional[datetime]:
        if not time_str:
            return None

        try:
            return datetime.strptime(time_str, "%a %b %d %H:%M:%S %z %Y")
        except ValueError:
            try:
                return datetime.strptime(time_str, "%Y-%m-%d %H:%M:%S")
            except ValueError:
                self.logger.warning(f"Failed to parse time: {time_str}")
                return None

    def _extract_pic_urls(self, mblog: Dict[str, Any]) -> List[str]:
        pic_urls = []

        if "pics" in mblog:
            for pic in mblog["pics"]:
                if "large" in pic and "url" in pic["large"]:
                    pic_urls.append(pic["large"]["url"])

        return pic_urls

    def _extract_video_url(self, mblog: Dict[str, Any]) -> str:
        if "page_info" in mblog and mblog["page_info"].get("type") == "video":
            media_info = mblog["page_info"].get("media_info", {})
            return media_info.get("stream_url", "")

        return ""

    def _extract_topics(self, text: str) -> List[str]:
        if not text:
            return []

        topics = re.findall(r"#([^#]+)#", text)
        return [topic.strip() for topic in topics if topic.strip()]

    def _extract_at_users(self, text: str) -> List[str]:
        if not text:
            return []

        mentions = re.findall(r"@([^\s@]+)", text)
        return [mention.strip() for mention in mentions if mention.strip()]
