"""Instagram-specific CRDT document parser.

This module provides parsing for CRDT documents containing Instagram
post data with instagrampost nodes.
"""

from __future__ import annotations

from datetime import UTC, datetime
from typing import TYPE_CHECKING

from marqetive.utils.crdt.exceptions import CRDTEmptyDocumentError, CRDTParseError
from marqetive.utils.crdt.models import (
    ContentType,
    InstagramPostContent,
    ParsedInstagramDocument,
)
from marqetive.utils.crdt.platforms.base import BaseCRDTParser

if TYPE_CHECKING:
    from pycrdt import Doc, XmlElement

# Instagram platform limits
INSTAGRAM_MAX_CAPTION_CHARACTERS = 2200
INSTAGRAM_CAROUSEL_MIN_ITEMS = 2
INSTAGRAM_CAROUSEL_MAX_ITEMS = 10


class InstagramCRDTParser(BaseCRDTParser[ParsedInstagramDocument]):
    """Parser for Instagram CRDT documents.

    Handles parsing of instagrampost nodes. Supports feed posts, reels,
    stories, and carousels with location and music metadata.

    Validation rules:
        - Max 2200 characters for caption
        - Media is required (at least 1 item)
        - Carousel: 2-10 items
        - Reel: exactly 1 video
    """

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

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

    def parse_document(
        self, doc: Doc, fragment_name: str = "default"
    ) -> ParsedInstagramDocument:
        """Parse an Instagram CRDT document.

        Args:
            doc: The pycrdt Doc object to parse
            fragment_name: Name of the XML fragment to parse

        Returns:
            ParsedInstagramDocument with parsed post

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

        # Find the instagrampost node
        post_nodes = self._find_nodes_by_type(fragment, self.root_node_type)

        if not post_nodes:
            raise CRDTEmptyDocumentError(
                "No Instagram post found in document",
                fragment_name=fragment_name,
            )

        # Parse the post
        try:
            post = self._parse_post(post_nodes[0])
        except Exception as e:
            raise CRDTParseError(
                f"Failed to parse Instagram post: {e}",
                platform=self.platform_name,
                node_type=self.root_node_type,
            ) from e

        return ParsedInstagramDocument(
            post=post,
            raw_attributes=raw_attributes,
            parsed_at=datetime.now(UTC),
        )

    def validate_schema(self, doc: Doc, fragment_name: str = "default") -> list[str]:
        """Validate an Instagram CRDT document.

        Args:
            doc: The pycrdt Doc object to validate
            fragment_name: Name of the XML fragment to validate

        Returns:
            List of validation errors (empty if valid)
        """
        errors: list[str] = []

        try:
            result = self.parse_document(doc, fragment_name)
        except CRDTEmptyDocumentError as e:
            return [str(e)]
        except CRDTParseError as e:
            return [str(e)]

        post = result.post

        # Validate caption length
        caption_error = self._validate_text_length(
            post.caption, INSTAGRAM_MAX_CAPTION_CHARACTERS, "Caption"
        )
        if caption_error:
            errors.append(caption_error)

        # Validate media requirements
        if not post.media:
            errors.append("Instagram posts require at least 1 media item")
        else:
            match post.content_type:
                case ContentType.CAROUSEL:
                    if len(post.media) < INSTAGRAM_CAROUSEL_MIN_ITEMS:
                        errors.append(
                            f"Carousel requires at least {INSTAGRAM_CAROUSEL_MIN_ITEMS} "
                            f"items, got {len(post.media)}"
                        )
                    if len(post.media) > INSTAGRAM_CAROUSEL_MAX_ITEMS:
                        errors.append(
                            f"Carousel allows maximum {INSTAGRAM_CAROUSEL_MAX_ITEMS} "
                            f"items, got {len(post.media)}"
                        )
                case ContentType.REEL:
                    if len(post.media) != 1:
                        errors.append("Reel requires exactly 1 video")
                case ContentType.STORY:
                    if len(post.media) != 1:
                        errors.append("Story requires exactly 1 media item")

        return errors

    def _parse_post(self, node: XmlElement) -> InstagramPostContent:
        """Parse an instagrampost node.

        Matches production instagram_parser.py:93-129 behavior.

        Args:
            node: The instagrampost XML node

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

        # Read post ID from attrs
        post_id = attrs.get("id")
        if post_id is not None:
            post_id = str(post_id)

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

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

        # Parse content type from "content_type" attr (production uses this name)
        content_type_str = attrs.get("content_type", "POST")
        content_type = self._parse_content_type(str(content_type_str))

        # Read location as simple string
        location = attrs.get("location")
        if location is not None:
            location = str(location)

        # Read music as simple string
        music = attrs.get("music")
        if music is not None:
            music = str(music)

        return InstagramPostContent(
            id=post_id,
            caption=caption,
            media=media,
            content_type=content_type,
            location=location,
            music=music,
        )

    def _parse_content_type(self, content_type_str: str) -> ContentType:
        """Parse content type string to enum.

        Handles uppercase values matching production behavior.

        Args:
            content_type_str: Content type string

        Returns:
            ContentType enum value
        """
        content_type_map = {
            "POST": ContentType.POST,
            "REEL": ContentType.REEL,
            "STORY": ContentType.STORY,
            "CAROUSEL": ContentType.CAROUSEL,
            "FEED": ContentType.FEED,
        }

        return content_type_map.get(content_type_str.strip().upper(), ContentType.POST)
