"""Topic class for XMind 16."""

from __future__ import annotations

from typing import Iterator, List, Optional

from .utils import generate_id


class Topic:
    """Represents a topic (node) in an XMind mind map.
    
    A topic can have a title, notes, labels, markers, and child subtopics.
    """
    
    def __init__(self, title: str = "", id: Optional[str] = None):
        """Initialize a Topic.
        
        Args:
            title: The topic title.
            id: Optional unique identifier. If not provided, one will be generated.
        """
        self._id = id or generate_id()
        self._title = title
        self._parent: Optional[Topic] = None
        self._subtopics: List[Topic] = []
        self._notes: str = ""
        self._labels: List[str] = []
        self._markers: List[str] = []
    
    @property
    def id(self) -> str:
        """Get the topic's unique identifier."""
        return self._id
    
    @property
    def title(self) -> str:
        """Get the topic's title."""
        return self._title
    
    @title.setter
    def title(self, value: str) -> None:
        """Set the topic's title."""
        self._title = value
    
    @property
    def parent(self) -> Optional[Topic]:
        """Get the parent topic, or None if this is a root topic."""
        return self._parent
    
    @property
    def subtopics(self) -> List[Topic]:
        """Get a list of child subtopics."""
        return self._subtopics.copy()
    
    @property
    def notes(self) -> str:
        """Get the topic's notes."""
        return self._notes
    
    @notes.setter
    def notes(self, value: str) -> None:
        """Set the topic's notes."""
        self._notes = value
    
    @property
    def labels(self) -> List[str]:
        """Get the topic's labels."""
        return self._labels.copy()
    
    @property
    def markers(self) -> List[str]:
        """Get the topic's markers."""
        return self._markers.copy()
    
    def add_subtopic(self, title: str = "", id: Optional[str] = None) -> Topic:
        """Add a child subtopic.
        
        Args:
            title: The subtopic title.
            id: Optional unique identifier for the subtopic.
            
        Returns:
            The newly created subtopic.
        """
        subtopic = Topic(title=title, id=id)
        subtopic._parent = self
        self._subtopics.append(subtopic)
        return subtopic
    
    def remove_subtopic(self, topic: Topic) -> bool:
        """Remove a subtopic.
        
        Args:
            topic: The subtopic to remove.
            
        Returns:
            True if the subtopic was found and removed, False otherwise.
        """
        if topic in self._subtopics:
            self._subtopics.remove(topic)
            topic._parent = None
            return True
        return False
    
    def find_subtopic(self, title: str) -> Optional[Topic]:
        """Find a direct subtopic by title.
        
        Args:
            title: The title to search for.
            
        Returns:
            The first matching subtopic, or None if not found.
        """
        for subtopic in self._subtopics:
            if subtopic.title == title:
                return subtopic
        return None
    
    def find_subtopic_recursive(self, title: str) -> Optional[Topic]:
        """Find any subtopic (at any depth) by title.
        
        Args:
            title: The title to search for.
            
        Returns:
            The first matching subtopic, or None if not found.
        """
        for topic in self.walk():
            if topic.title == title:
                return topic
        return None
    
    def walk(self) -> Iterator[Topic]:
        """Iterate through all descendant topics (depth-first).
        
        Yields:
            Each descendant topic, including this topic itself.
        """
        yield self
        for subtopic in self._subtopics:
            yield from subtopic.walk()
    
    def add_label(self, label: str) -> None:
        """Add a label to the topic.
        
        Args:
            label: The label to add.
        """
        if label not in self._labels:
            self._labels.append(label)
    
    def remove_label(self, label: str) -> bool:
        """Remove a label from the topic.
        
        Args:
            label: The label to remove.
            
        Returns:
            True if the label was found and removed, False otherwise.
        """
        if label in self._labels:
            self._labels.remove(label)
            return True
        return False
    
    def add_marker(self, marker: str) -> None:
        """Add a marker to the topic.
        
        Args:
            marker: The marker to add.
        """
        if marker not in self._markers:
            self._markers.append(marker)
    
    def remove_marker(self, marker: str) -> bool:
        """Remove a marker from the topic.
        
        Args:
            marker: The marker to remove.
            
        Returns:
            True if the marker was found and removed, False otherwise.
        """
        if marker in self._markers:
            self._markers.remove(marker)
            return True
        return False
    
    def to_dict(self) -> dict:
        """Convert the topic to a dictionary representation (XMind 16 format).
        
        Returns:
            A dictionary representing the topic.
        """
        data = {
            "id": self._id,
            "class": "topic",
            "title": self._title,
        }
        
        if self._notes:
            data["notes"] = self._notes
        
        if self._labels:
            data["labels"] = self._labels.copy()
        
        if self._markers:
            data["markers"] = self._markers.copy()
        
        if self._subtopics:
            data["children"] = {
                "attached": [child.to_dict() for child in self._subtopics]
            }
        
        return data
    
    @classmethod
    def from_dict(cls, data: dict) -> Topic:
        """Create a Topic from a dictionary.
        
        Args:
            data: The dictionary containing topic data.
            
        Returns:
            A new Topic instance.
        """
        topic = cls(
            title=data.get("title", ""),
            id=data.get("id")
        )
        
        topic._notes = data.get("notes", "")
        topic._labels = data.get("labels", []).copy()
        topic._markers = data.get("markers", []).copy()
        
        # Handle XMind 16 format with "attached" children
        children_data = data.get("children", {})
        if isinstance(children_data, dict):
            attached_children = children_data.get("attached", [])
        else:
            attached_children = children_data  # Fallback for old format
        
        for child_data in attached_children:
            child = cls.from_dict(child_data)
            child._parent = topic
            topic._subtopics.append(child)
        
        return topic
    
    def __repr__(self) -> str:
        """Return a string representation of the topic."""
        return f"Topic(id={self._id[:8]}..., title='{self._title}')"
    
    def __eq__(self, other: object) -> bool:
        """Check if two topics are equal based on their IDs."""
        if not isinstance(other, Topic):
            return NotImplemented
        return self._id == other._id
