"""
Mindmap Generator - Erstellt Mindmaps aus Quelltexten.

Generiert:
- Hierarchische Struktur aus Text
- Hauptthema mit Unterthemen
- Verbindungen und Beziehungen
"""

import logging
import json
import re
from typing import List, Dict, Any, Optional
from dataclasses import dataclass, field

logger = logging.getLogger(__name__)


@dataclass
class MindmapNode:
    """Ein Knoten in der Mindmap."""
    id: str
    label: str
    level: int = 0
    children: List['MindmapNode'] = field(default_factory=list)
    color: Optional[str] = None
    icon: Optional[str] = None
    notes: Optional[str] = None


@dataclass
class Mindmap:
    """Eine komplette Mindmap."""
    root: MindmapNode
    title: str
    topic: Optional[str] = None
    total_nodes: int = 0


class MindmapGenerator:
    """
    Generiert Mindmaps aus Quelltexten.

    Extrahiert:
    - Hauptthema als Zentrum
    - Unterthemen als Äste
    - Details als Blätter
    """

    def __init__(self, llm_client=None):
        """
        Initialisiert den Generator.

        Args:
            llm_client: Optional - LLM-Client für intelligente Generierung
        """
        self.llm_client = llm_client
        logger.info("MindmapGenerator initialized")

        # Farben für verschiedene Ebenen
        self.level_colors = [
            "#6C1B1B",  # Weinrot (Zentrum)
            "#3b82f6",  # Blau
            "#22c55e",  # Grün
            "#f59e0b",  # Orange
            "#8b5cf6",  # Violett
        ]

    def generate(
        self,
        source_text: str,
        title: Optional[str] = None,
        max_depth: int = 3,
        topic: Optional[str] = None
    ) -> Mindmap:
        """
        Generiert eine Mindmap aus einem Quelltext.

        Args:
            source_text: Der Ausgangstext
            title: Optionaler Titel (sonst automatisch ermittelt)
            max_depth: Maximale Tiefe der Hierarchie
            topic: Optionales Thema

        Returns:
            Mindmap-Objekt
        """
        logger.info(f"Generating mindmap (max_depth: {max_depth})")

        if not source_text or len(source_text.strip()) < 50:
            logger.warning("Source text too short")
            return self._empty_mindmap(title or "Mindmap")

        if self.llm_client:
            return self._generate_with_llm(source_text, title, max_depth, topic)
        else:
            return self._generate_automatic(source_text, title, max_depth, topic)

    def _generate_with_llm(
        self,
        source_text: str,
        title: Optional[str],
        max_depth: int,
        topic: Optional[str]
    ) -> Mindmap:
        """Generiert Mindmap mit LLM."""
        prompt = f"""
Erstelle eine Mindmap-Struktur auf Deutsch basierend auf folgendem Text.
{f'Titel: {title}' if title else 'Ermittle einen passenden Titel.'}
Maximale Tiefe: {max_depth} Ebenen
{f'Thema: {topic}' if topic else ''}

Text:
{source_text}

Erstelle eine hierarchische Struktur mit:
- Einem zentralen Hauptthema
- 3-5 Hauptästen (Unterthemen)
- Jeweils 2-4 Details pro Ast

Antworte im JSON-Format:
{{
  "title": "Hauptthema",
  "branches": [
    {{
      "label": "Unterthema 1",
      "children": [
        {{"label": "Detail 1.1"}},
        {{"label": "Detail 1.2"}}
      ]
    }},
    {{
      "label": "Unterthema 2",
      "children": [
        {{"label": "Detail 2.1"}}
      ]
    }}
  ]
}}
"""

        try:
            response = self.llm_client.generate(prompt)
            data = json.loads(response)
            return self._create_mindmap_from_llm(data, topic)
        except Exception as e:
            logger.error(f"Error generating with LLM: {e}")
            return self._generate_automatic(source_text, title, max_depth, topic)

    def _generate_automatic(
        self,
        source_text: str,
        title: Optional[str],
        max_depth: int,
        topic: Optional[str]
    ) -> Mindmap:
        """Generiert Mindmap automatisch ohne LLM."""
        # Extrahiere Struktur aus Text
        sections = self._extract_sections(source_text)

        # Bestimme Titel
        if not title:
            # Erste Zeile oder erstes Substantiv
            first_line = source_text.split('\n')[0].strip()
            title = first_line[:50] if first_line else "Mindmap"

        # Erstelle Root-Knoten
        node_counter = [0]
        root = self._create_node(title, 0, node_counter)

        # Füge Hauptäste hinzu
        for section_title, section_content in sections[:5]:  # Max 5 Hauptäste
            branch = self._create_node(section_title, 1, node_counter)
            branch.color = self.level_colors[1 % len(self.level_colors)]

            # Füge Details hinzu
            details = self._extract_details(section_content)
            for detail in details[:4]:  # Max 4 Details pro Ast
                if max_depth >= 2:
                    leaf = self._create_node(detail, 2, node_counter)
                    leaf.color = self.level_colors[2 % len(self.level_colors)]
                    branch.children.append(leaf)

            root.children.append(branch)

        return Mindmap(
            root=root,
            title=title,
            topic=topic,
            total_nodes=node_counter[0]
        )

    def _extract_sections(self, text: str) -> List[tuple]:
        """Extrahiert Abschnitte aus dem Text."""
        sections = []

        # Versuche Überschriften zu finden
        lines = text.split('\n')
        current_section = None
        current_content = []

        for line in lines:
            line = line.strip()
            if not line:
                continue

            # Erkenne potenzielle Überschriften
            if (line.endswith(':') or
                line.isupper() or
                len(line) < 50 and line[0].isupper() and '.' not in line):
                # Speichere vorherige Section
                if current_section:
                    sections.append((current_section, '\n'.join(current_content)))
                current_section = line.rstrip(':')
                current_content = []
            else:
                current_content.append(line)

        # Letzte Section
        if current_section:
            sections.append((current_section, '\n'.join(current_content)))

        # Falls keine Sections gefunden, erstelle aus Sätzen
        if not sections:
            sentences = re.split(r'[.!?]+', text)
            for i, sentence in enumerate(sentences[:5]):
                sentence = sentence.strip()
                if len(sentence) > 10:
                    # Kürze auf max 30 Zeichen für Label
                    label = sentence[:30] + '...' if len(sentence) > 30 else sentence
                    sections.append((label, sentence))

        return sections

    def _extract_details(self, content: str) -> List[str]:
        """Extrahiert Details aus Abschnittsinhalt."""
        details = []

        # Aufzählungen
        bullet_pattern = r'[-•*]\s*(.+)'
        bullets = re.findall(bullet_pattern, content)
        details.extend(bullets)

        # Nummerierte Listen
        num_pattern = r'\d+[.)]\s*(.+)'
        numbered = re.findall(num_pattern, content)
        details.extend(numbered)

        # Falls keine Listen, nehme Sätze
        if not details:
            sentences = re.split(r'[.!?]+', content)
            for sentence in sentences:
                sentence = sentence.strip()
                if len(sentence) > 5:
                    label = sentence[:40] + '...' if len(sentence) > 40 else sentence
                    details.append(label)

        return details

    def _create_node(
        self,
        label: str,
        level: int,
        counter: List[int]
    ) -> MindmapNode:
        """Erstellt einen neuen Knoten."""
        counter[0] += 1
        return MindmapNode(
            id=f"node_{counter[0]}",
            label=label,
            level=level,
            children=[],
            color=self.level_colors[level % len(self.level_colors)]
        )

    def _create_mindmap_from_llm(
        self,
        data: Dict[str, Any],
        topic: Optional[str]
    ) -> Mindmap:
        """Erstellt Mindmap aus LLM-Antwort."""
        node_counter = [0]
        title = data.get("title", "Mindmap")

        root = self._create_node(title, 0, node_counter)

        for branch_data in data.get("branches", []):
            branch = self._create_node(branch_data.get("label", ""), 1, node_counter)
            branch.color = self.level_colors[1 % len(self.level_colors)]

            for child_data in branch_data.get("children", []):
                child = self._create_node(child_data.get("label", ""), 2, node_counter)
                child.color = self.level_colors[2 % len(self.level_colors)]
                branch.children.append(child)

            root.children.append(branch)

        return Mindmap(
            root=root,
            title=title,
            topic=topic,
            total_nodes=node_counter[0]
        )

    def _empty_mindmap(self, title: str) -> Mindmap:
        """Erstellt leere Mindmap bei Fehler."""
        root = MindmapNode(
            id="root",
            label=title,
            level=0,
            color=self.level_colors[0]
        )
        return Mindmap(root=root, title=title, total_nodes=1)

    def to_dict(self, mindmap: Mindmap) -> Dict[str, Any]:
        """Konvertiert Mindmap zu Dictionary-Format."""
        def node_to_dict(node: MindmapNode) -> Dict[str, Any]:
            return {
                "id": node.id,
                "label": node.label,
                "level": node.level,
                "color": node.color,
                "icon": node.icon,
                "notes": node.notes,
                "children": [node_to_dict(child) for child in node.children]
            }

        return {
            "title": mindmap.title,
            "topic": mindmap.topic,
            "total_nodes": mindmap.total_nodes,
            "root": node_to_dict(mindmap.root)
        }

    def to_mermaid(self, mindmap: Mindmap) -> str:
        """
        Konvertiert Mindmap zu Mermaid-Format für Visualisierung.

        Args:
            mindmap: Mindmap-Objekt

        Returns:
            Mermaid-Diagramm als String
        """
        lines = ["mindmap"]
        lines.append(f"  root(({mindmap.title}))")

        def add_node(node: MindmapNode, indent: int):
            for child in node.children:
                prefix = "  " * (indent + 1)
                if child.children:
                    lines.append(f"{prefix}{child.label}")
                else:
                    lines.append(f"{prefix}){child.label}(")
                add_node(child, indent + 1)

        add_node(mindmap.root, 1)
        return "\n".join(lines)

    def to_json_tree(self, mindmap: Mindmap) -> Dict[str, Any]:
        """
        Konvertiert Mindmap zu JSON-Tree-Format für JS-Bibliotheken.

        Args:
            mindmap: Mindmap-Objekt

        Returns:
            JSON-Tree-Format für d3.js, vis.js etc.
        """
        def node_to_tree(node: MindmapNode) -> Dict[str, Any]:
            result = {
                "name": node.label,
                "id": node.id,
                "color": node.color
            }
            if node.children:
                result["children"] = [node_to_tree(c) for c in node.children]
            return result

        return node_to_tree(mindmap.root)
