"""Twitter-specific CRDT document parser.

This module provides parsing for CRDT documents containing Twitter
(X) post data with twittercard nodes.

Intentionally skipped frontend-only attributes:
    - ``autoNumber`` on twittercard: computed at render time from AutoNumberConfig
    - Link marks (twitterLink): stripped during text extraction, raw text preserved
"""

from __future__ import annotations

import re
from datetime import UTC, datetime
from typing import TYPE_CHECKING, Any

from marqetive.utils.crdt.exceptions import CRDTEmptyDocumentError, CRDTParseError
from marqetive.utils.crdt.models import (
    AutoNumberConfig,
    ParsedTwitterDocument,
    TwitterCardContent,
)
from marqetive.utils.crdt.platforms.base import BaseCRDTParser

if TYPE_CHECKING:
    from pycrdt import Doc, XmlElement

# Pattern to extract tweet ID from OG URLs (twitter.com or x.com)
QUOTED_TWEET_PATTERN = re.compile(r"(?:twitter\.com|x\.com)/[^/]+/status/(\d+)")


class TwitterCRDTParser(BaseCRDTParser[ParsedTwitterDocument]):
    """Parser for Twitter CRDT documents.

    Handles parsing of twittercard nodes which represent individual tweets
    in a thread. Supports media attachments and quoted tweet extraction.

    Validation rules:
        - Max 280 characters per card
        - Max 4 media attachments per card
        - Threads supported (multiple cards)
    """

    @property
    def platform_name(self) -> str:
        """Return the platform name."""
        return "twitter"

    @property
    def root_node_type(self) -> str:
        """Return the root node type for Twitter."""
        return "twittercard"

    def parse_document(
        self,
        doc: Doc[Any],
        fragment_name: str = "default",
        *,
        include_auto_number: bool = False,
    ) -> ParsedTwitterDocument:
        """Parse a Twitter CRDT document.

        Args:
            doc: The pycrdt Doc object to parse
            fragment_name: Name of the XML fragment to parse
            include_auto_number: If True, materialize auto-numbering into card text

        Returns:
            ParsedTwitterDocument with parsed cards

        Raises:
            CRDTEmptyDocumentError: If no cards found
            CRDTParseError: If parsing fails
        """
        fragment = self._get_fragment(doc, fragment_name)
        raw_attributes = self._get_node_attributes(fragment)

        # Parse auto-number config from twitterthread node if present
        auto_number_config: AutoNumberConfig | None = None
        thread_nodes = self._find_nodes_by_type(fragment, "twitterthread")
        if thread_nodes:
            auto_number_config = self._parse_auto_number_config(thread_nodes[0])

        # Find all twittercard nodes
        card_nodes = self._find_nodes_by_type(fragment, self.root_node_type)

        if not card_nodes:
            raise CRDTEmptyDocumentError(
                "No Twitter cards found in document",
                fragment_name=fragment_name,
            )

        # Parse each card
        cards: list[TwitterCardContent] = []
        for i, node in enumerate(card_nodes):
            try:
                card = self._parse_card(node, i)
                cards.append(card)
            except Exception as e:
                raise CRDTParseError(
                    f"Failed to parse Twitter card at position {i}: {e}",
                    platform=self.platform_name,
                    node_type=self.root_node_type,
                    position=i,
                ) from e

        if include_auto_number:
            cards = self._apply_auto_numbering(cards, auto_number_config)

        return ParsedTwitterDocument(
            cards=cards,
            auto_number_config=auto_number_config,
            raw_attributes=raw_attributes,
            parsed_at=datetime.now(UTC),
        )

    def _parse_card(self, node: XmlElement, index: int) -> TwitterCardContent:
        """Parse a single twittercard node.

        Matches production twitter_parser.py behavior for attribute names
        and data formats.

        Args:
            node: The twittercard XML node
            index: Position of the card in the thread

        Returns:
            TwitterCardContent with parsed data
        """
        attrs = self._get_node_attributes(node)

        # Extract text content
        text = self._extract_text(node)

        # Parse media via space-separated URL strings
        media = self._parse_media_urls(node, "media")

        # Parse isQuoted with string conversion (production lines 97-100)
        is_quoted = False
        is_quoted_value = attrs.get("isQuoted")
        if is_quoted_value is not None:
            is_quoted = str(is_quoted_value).lower() == "true"

        # Parse quotedCardId with int validation (production lines 103-111)
        quoted_card_id: str | None = None
        quoted_card_id_value = attrs.get("quotedCardId")
        if quoted_card_id_value is not None:
            try:
                quoted_card_id = str(int(quoted_card_id_value))
            except (ValueError, TypeError):
                quoted_card_id = None

        # Extract quoted tweet ID from og URL when quoteOg is true (production lines 80-90)
        quoted_tweet_id: str | None = None
        quote_og_value = attrs.get("quoteOg")
        if quote_og_value is not None and str(quote_og_value).lower() == "true":
            og_value = attrs.get("og")
            if og_value:
                quoted_tweet_id = self._extract_quoted_tweet_id(og_value)

        # Get card ID if available
        raw_id = attrs.get("id") or attrs.get("cardId")
        try:
            card_id = str(int(raw_id)) if raw_id is not None else f"card_{index}"
        except (ValueError, TypeError):
            card_id = str(raw_id) if raw_id is not None else f"card_{index}"

        return TwitterCardContent(
            id=card_id,
            text=text,
            media=media,
            is_quoted=is_quoted,
            quoted_card_id=quoted_card_id,
            quoted_tweet_id=quoted_tweet_id,
        )

    def _extract_quoted_tweet_id(self, url: str | dict[str, Any]) -> str | None:
        """Extract tweet ID from an OG URL.

        Handles both string URLs and dict with "url" key.
        Matches production twitter_parser.py:60-69.

        Args:
            url: OG URL string or dict containing url key

        Returns:
            Tweet ID string or None
        """
        if isinstance(url, dict):
            url = url.get("url", "")
        if not isinstance(url, str):
            return None
        match = QUOTED_TWEET_PATTERN.search(url)
        return match.group(1) if match else None
