"""Bluesky-specific CRDT document parser.

This module provides parsing for CRDT documents containing Bluesky
post data with blueskycard nodes.

Intentionally skipped frontend-only attributes:
    - ``og`` on blueskycard: frontend-only link card preview (parsed but
      stored on the model; the *display* rendering is frontend-only)
    - ``autoNumber`` on blueskycard: computed at render time from AutoNumberConfig
    - Link marks (blueskyLink): stripped during text extraction, raw text preserved
"""

from __future__ import annotations

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,
    BlueSkyCardContent,
    ParsedBlueSkyDocument,
)
from marqetive.utils.crdt.platforms.base import BaseCRDTParser

if TYPE_CHECKING:
    from pycrdt import Doc, XmlElement


class BlueskyCRDTParser(BaseCRDTParser[ParsedBlueSkyDocument]):
    """Parser for Bluesky CRDT documents.

    Handles parsing of blueskycard nodes which represent individual posts
    in a thread. Supports media attachments and link card embeds.

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

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

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

    def parse_document(
        self,
        doc: Doc[Any],
        fragment_name: str = "default",
        *,
        include_auto_number: bool = False,
    ) -> ParsedBlueSkyDocument:
        """Parse a Bluesky 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:
            ParsedBlueSkyDocument 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 blueskythread node if present
        auto_number_config: AutoNumberConfig | None = None
        thread_nodes = self._find_nodes_by_type(fragment, "blueskythread")
        if thread_nodes:
            auto_number_config = self._parse_auto_number_config(thread_nodes[0])

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

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

        # Parse each card
        cards: list[BlueSkyCardContent] = []
        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 Bluesky 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 ParsedBlueSkyDocument(
            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) -> BlueSkyCardContent:
        """Parse a single blueskycard node.

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

        Returns:
            BlueSkyCardContent 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 og link card URL
        og: str | None = None
        og_value = attrs.get("og")
        if og_value is not None:
            og = str(og_value)

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

        return BlueSkyCardContent(
            id=card_id,
            text=text,
            media=media,
            og=og,
        )
