"""
Markdown document builder for cheat sheet generation.

Creates structured Markdown documents from CheatSheetContent.
"""

from dataclasses import dataclass
from pathlib import Path
from typing import Any, Dict, List, Optional, Union

from ..infra.logging import get_logger

logger = get_logger(__name__)


@dataclass
class MarkdownCheatSheet:
    """Result of Markdown generation."""
    title: str
    content: Dict[str, Any]
    markdown_source: str
    output_path: Optional[Path] = None


class MarkdownBuilder:
    """
    Builder for generating Markdown cheat sheets.
    
    Converts CheatSheetContent into well-formatted Markdown.
    """
    
    def __init__(
        self,
        include_toc: bool = True,
        math_format: str = "latex",  # "latex" for $...$ or "unicode" for symbols
        code_style: str = "fenced",  # "fenced" for ```...``` or "indented"
    ):
        """
        Initialize the Markdown builder.
        
        Args:
            include_toc: Whether to include table of contents
            math_format: How to format math ("latex" or "unicode")
            code_style: How to format code blocks
        """
        self.include_toc = include_toc
        self.math_format = math_format
        self.code_style = code_style
    
    def build(self, content: "CheatSheetContent") -> str:
        """
        Build Markdown document from CheatSheetContent.
        
        Args:
            content: CheatSheetContent object
            
        Returns:
            Markdown source string
        """
        sections = []
        
        # Title
        sections.append(f"# {content.title}\n")
        
        # Table of Contents
        if self.include_toc:
            sections.append(self._build_toc(content))
        
        # Overview
        if content.overview:
            sections.append("## Overview\n")
            sections.append(f"{self._clean_text(content.overview)}\n")
        
        # Core Concepts
        if content.concepts:
            sections.append("## Core Concepts\n")
            sections.append(self._build_concepts(content.concepts))
        
        # Key Formulas
        if content.formulas:
            sections.append("## Key Formulas\n")
            sections.append(self._build_formulas(content.formulas))
        
        # Models & Theory
        if content.models:
            sections.append("## Models & Theory\n")
            sections.append(self._build_models(content.models))
        
        # Algorithms
        if content.algorithms:
            sections.append("## Algorithms\n")
            sections.append(self._build_algorithms(content.algorithms))
        
        # Examples
        if content.examples:
            sections.append("## Examples\n")
            sections.append(self._build_examples(content.examples))
        
        # Connections
        if content.connections:
            sections.append("## Connections & Related Topics\n")
            sections.append(self._build_connections(content.connections))
        
        # Quick Reference / Recap
        if content.recap:
            sections.append("## Quick Reference\n")
            sections.append(f"{self._clean_text(content.recap)}\n")
        
        # References
        if content.references:
            sections.append("## References\n")
            for i, ref in enumerate(content.references, 1):
                sections.append(f"{i}. {ref}\n")
        
        return "\n".join(sections)
    
    def _build_toc(self, content: "CheatSheetContent") -> str:
        """Build table of contents."""
        toc_items = ["## Table of Contents\n"]
        
        if content.overview:
            toc_items.append("- [Overview](#overview)")
        if content.concepts:
            toc_items.append("- [Core Concepts](#core-concepts)")
        if content.formulas:
            toc_items.append("- [Key Formulas](#key-formulas)")
        if content.models:
            toc_items.append("- [Models & Theory](#models--theory)")
        if content.algorithms:
            toc_items.append("- [Algorithms](#algorithms)")
        if content.examples:
            toc_items.append("- [Examples](#examples)")
        if content.connections:
            toc_items.append("- [Connections & Related Topics](#connections--related-topics)")
        if content.recap:
            toc_items.append("- [Quick Reference](#quick-reference)")
        if content.references:
            toc_items.append("- [References](#references)")
        
        toc_items.append("")
        return "\n".join(toc_items)
    
    def _build_concepts(self, concepts: List[Dict[str, Any]]) -> str:
        """Build concepts section."""
        lines = []
        
        for concept in concepts:
            name = concept.get("name", "Concept")
            definition = concept.get("definition", "")
            intuition = concept.get("intuition", "")
            related = concept.get("related", [])
            
            lines.append(f"### {name}\n")
            
            if definition:
                lines.append(f"**Definition:** {self._clean_text(definition)}\n")
            
            if intuition:
                lines.append(f"**Intuition:** {self._clean_text(intuition)}\n")
            
            if related:
                if isinstance(related, list):
                    related_str = ", ".join(related)
                else:
                    related_str = str(related)
                lines.append(f"**Related:** {related_str}\n")
            
            lines.append("")
        
        return "\n".join(lines)
    
    def _build_formulas(self, formulas: List[Dict[str, Any]]) -> str:
        """Build formulas section."""
        lines = []
        
        # Group by category if available
        grouped = {}
        for formula in formulas:
            category = formula.get("category", "General")
            if category not in grouped:
                grouped[category] = []
            grouped[category].append(formula)
        
        for category, category_formulas in grouped.items():
            if len(grouped) > 1:
                lines.append(f"### {category}\n")
            
            for formula in category_formulas:
                name = formula.get("name", "")
                latex = formula.get("latex", formula.get("formula", ""))
                variables = formula.get("variables", [])
                context = formula.get("context", "")
                
                if name:
                    lines.append(f"**{name}**\n")
                
                if latex:
                    # Format as display math
                    latex_clean = latex.strip()
                    if not latex_clean.startswith("$$") and not latex_clean.startswith("$"):
                        lines.append(f"$$\n{latex_clean}\n$$\n")
                    else:
                        lines.append(f"{latex_clean}\n")
                
                if variables:
                    lines.append("*Variables:*")
                    if isinstance(variables, list):
                        for var in variables:
                            if isinstance(var, dict):
                                var_name = var.get("name", var.get("symbol", ""))
                                var_desc = var.get("description", var.get("meaning", ""))
                                lines.append(f"- ${var_name}$: {var_desc}")
                            else:
                                lines.append(f"- {var}")
                    elif isinstance(variables, dict):
                        for var_name, var_desc in variables.items():
                            lines.append(f"- ${var_name}$: {var_desc}")
                    lines.append("")
                
                if context:
                    lines.append(f"*Context:* {self._clean_text(context)}\n")
                
                lines.append("")
        
        return "\n".join(lines)
    
    def _build_models(self, models: List[Dict[str, Any]]) -> str:
        """Build models section."""
        lines = []
        
        for model in models:
            name = model.get("name", "Model")
            description = model.get("description", "")
            assumptions = model.get("assumptions", [])
            formulation = model.get("formulation", "")
            properties = model.get("properties", [])
            
            lines.append(f"### {name}\n")
            
            if description:
                lines.append(f"{self._clean_text(description)}\n")
            
            if assumptions:
                lines.append("**Assumptions:**")
                if isinstance(assumptions, list):
                    for assumption in assumptions:
                        lines.append(f"- {assumption}")
                else:
                    lines.append(f"- {assumptions}")
                lines.append("")
            
            if formulation:
                lines.append("**Formulation:**")
                lines.append(f"$$\n{formulation}\n$$\n")
            
            if properties:
                lines.append("**Key Properties:**")
                if isinstance(properties, list):
                    for prop in properties:
                        lines.append(f"- {prop}")
                else:
                    lines.append(f"- {properties}")
                lines.append("")
            
            lines.append("")
        
        return "\n".join(lines)
    
    def _build_algorithms(self, algorithms: List[Dict[str, Any]]) -> str:
        """Build algorithms section."""
        lines = []
        
        for algo in algorithms:
            name = algo.get("name", "Algorithm")
            purpose = algo.get("purpose", "")
            inputs = algo.get("input", algo.get("inputs", ""))
            outputs = algo.get("output", algo.get("outputs", ""))
            pseudocode = algo.get("pseudocode", algo.get("steps", ""))
            complexity = algo.get("complexity", {})
            
            lines.append(f"### {name}\n")
            
            if purpose:
                lines.append(f"**Purpose:** {self._clean_text(purpose)}\n")
            
            if inputs:
                lines.append(f"**Input:** {inputs}")
            if outputs:
                lines.append(f"**Output:** {outputs}")
            if inputs or outputs:
                lines.append("")
            
            if pseudocode:
                lines.append("**Pseudocode:**")
                lines.append("```")
                lines.append(str(pseudocode).strip())
                lines.append("```\n")
            
            if complexity:
                lines.append("**Complexity:**")
                if isinstance(complexity, dict):
                    time_c = complexity.get("time", "")
                    space_c = complexity.get("space", "")
                    if time_c:
                        lines.append(f"- Time: {time_c}")
                    if space_c:
                        lines.append(f"- Space: {space_c}")
                else:
                    lines.append(f"- {complexity}")
                lines.append("")
            
            lines.append("")
        
        return "\n".join(lines)
    
    def _build_examples(self, examples: List[Dict[str, Any]]) -> str:
        """Build examples section."""
        lines = []
        
        for i, example in enumerate(examples, 1):
            title = example.get("title", f"Example {i}")
            problem = example.get("problem", "")
            solution = example.get("solution", "")
            
            lines.append(f"### {title}\n")
            
            if problem:
                lines.append(f"**Problem:** {self._clean_text(problem)}\n")
            
            if solution:
                lines.append(f"**Solution:** {self._clean_text(solution)}\n")
            
            lines.append("")
        
        return "\n".join(lines)
    
    def _build_connections(self, connections: List[Dict[str, Any]]) -> str:
        """Build connections section."""
        lines = []
        
        for conn in connections:
            if isinstance(conn, dict):
                topic = conn.get("topic", conn.get("name", ""))
                description = conn.get("description", conn.get("connection", ""))
                if topic:
                    lines.append(f"- **{topic}**: {description}")
                else:
                    lines.append(f"- {description}")
            else:
                lines.append(f"- {conn}")
        
        lines.append("")
        return "\n".join(lines)
    
    def _clean_text(self, text: str) -> str:
        """Clean and normalize text content."""
        if not text:
            return ""
        
        # Remove excessive whitespace
        text = " ".join(text.split())
        
        return text
    
    def save(self, markdown_source: str, output_path: Union[str, Path]) -> Path:
        """
        Save Markdown source to file.
        
        Args:
            markdown_source: Markdown source string
            output_path: Output file path
            
        Returns:
            Path to saved file
        """
        output_path = Path(output_path)
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        output_path.write_text(markdown_source, encoding='utf-8')
        logger.info(f"Saved Markdown: {output_path}")
        
        return output_path
    
    def build_and_save(
        self,
        content: "CheatSheetContent",
        output_path: Union[str, Path]
    ) -> MarkdownCheatSheet:
        """
        Build Markdown and save to file.
        
        Args:
            content: CheatSheetContent
            output_path: Output file path
            
        Returns:
            MarkdownCheatSheet with source and path
        """
        markdown_source = self.build(content)
        saved_path = self.save(markdown_source, output_path)
        
        return MarkdownCheatSheet(
            title=content.title,
            content=content.to_dict(),
            markdown_source=markdown_source,
            output_path=saved_path
        )
