"""Generate formatted release notes / changelog from a DocumentModel.

Produces a Markdown file styled as a changelog entry with categorized
changes: Features, Improvements, Installation, API Changes, etc.
Extracts structure from the README sections and KG entities.
"""

from __future__ import annotations

from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING

from ..core.knowledge_graph import EntityType, KnowledgeGraph
from ..core.models import (
    CodeBlock,
    ContentBlock,
    DocumentModel,
    GenerationResult,
    ListBlock,
    OutputFormat,
    ParagraphBlock,
    Section,
)
from .base import BaseGenerator

if TYPE_CHECKING:
    from .diagram_extractor import ImageCache


class ChangelogGenerator(BaseGenerator):
    """DocumentModel → Release Notes / Changelog Markdown."""

    format = OutputFormat.CHANGELOG

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def generate(self, doc: DocumentModel, output_dir: Path) -> GenerationResult:
        filename = self._safe_filename(doc.metadata.repo_name or "project", "md")
        filename = f"changelog_{filename}"
        output_path = self._ensure_dir(output_dir) / filename

        try:
            content = self._build_changelog(doc)
            output_path.write_text(content, encoding="utf-8")
            return GenerationResult(
                format=OutputFormat.CHANGELOG,
                output_path=output_path,
            )
        except Exception as exc:
            return GenerationResult(
                format=OutputFormat.CHANGELOG,
                output_path=output_path,
                success=False,
                error=str(exc),
            )

    # ------------------------------------------------------------------
    # Internal
    # ------------------------------------------------------------------

    def _build_changelog(self, doc: DocumentModel) -> str:
        lines: list[str] = []
        name = doc.metadata.repo_name or "Project"
        today = datetime.now().strftime("%Y-%m-%d")
        url = doc.metadata.repo_url or ""

        # -- Header ------------------------------------------------------
        lines.append(f"# Changelog — {name}")
        lines.append("")
        lines.append(
            f"All notable changes to **{name}** are documented below. "
            f"This file is auto-generated by "
            f"[opendocs](https://pypi.org/project/opendocs/)."
        )
        lines.append("")

        # -- Unreleased / current release --------------------------------
        lines.append(f"## [Unreleased] — {today}")
        lines.append("")

        # Categorize content from README sections
        categories = self._categorize_sections(doc)

        category_order = [
            ("Features", "features"),
            ("Installation & Setup", "setup"),
            ("API & Endpoints", "api"),
            ("Configuration", "configuration"),
            ("Architecture", "architecture"),
            ("Testing", "testing"),
            ("Deployment", "devops"),
            ("Documentation", "documentation"),
            ("Roadmap & Planned", "roadmap"),
            ("Security", "security"),
            ("Community", "community"),
        ]

        for label, key in category_order:
            items = categories.get(key, [])
            if items:
                lines.append(f"### {label}")
                lines.append("")
                for item in items:
                    lines.append(f"- {item}")
                lines.append("")

        # -- Tech Stack summary from KG ----------------------------------
        if self.kg:
            techs = self._extract_tech_entries()
            if techs:
                lines.append("### Tech Stack")
                lines.append("")
                for tech in techs:
                    lines.append(f"- {tech}")
                lines.append("")

        # -- Dependencies from KG ----------------------------------------
        if self.kg:
            deps = self._extract_dependencies()
            if deps:
                lines.append("### Dependencies")
                lines.append("")
                for dep in deps:
                    lines.append(f"- {dep}")
                lines.append("")

        # -- Stats -------------------------------------------------------
        lines.append("### Stats")
        lines.append("")
        lines.append(f"- **Sections:** {len(doc.sections)}")
        lines.append(f"- **Content Blocks:** {len(doc.all_blocks)}")
        n_code = sum(1 for b in doc.all_blocks if isinstance(b, CodeBlock))
        lines.append(f"- **Code Examples:** {n_code}")
        lines.append(f"- **Source Length:** {len(doc.raw_markdown):,} characters")
        if self.kg:
            stats = self.kg.compute_stats()
            lines.append(f"- **Entities Extracted:** {stats.get('total_entities', 0)}")
            lines.append(f"- **Relations Mapped:** {stats.get('total_relations', 0)}")
        lines.append("")

        # -- Footer -------------------------------------------------------
        if url:
            lines.append(f"[Unreleased]: {url}")
            lines.append("")

        lines.append("---")
        lines.append(
            f"*Generated by [opendocs](https://pypi.org/project/opendocs/) "
            f"on {today}*"
        )
        lines.append("")

        return "\n".join(lines)

    # ------------------------------------------------------------------
    # Categorization
    # ------------------------------------------------------------------

    def _categorize_sections(self, doc: DocumentModel) -> dict[str, list[str]]:
        """Walk sections and categorize their content into changelog buckets."""
        categories: dict[str, list[str]] = {}

        for section in doc.sections:
            self._walk_section(section, categories)

        return categories

    def _walk_section(
        self, section: Section, categories: dict[str, list[str]],
    ) -> None:
        """Recursively walk a section tree and populate categories."""
        if not section.title:
            return

        cat = self._classify(section.title)
        items = categories.setdefault(cat, [])

        # Extract list items as changelog entries
        for block in section.blocks:
            if isinstance(block, ListBlock):
                for item in block.items:
                    entry = item.strip()
                    if entry and len(entry) > 3:
                        items.append(entry)
            elif isinstance(block, ParagraphBlock) and len(block.text) > 20:
                # Summarize paragraph as a changelog entry
                text = block.text.strip()
                if len(text) > 120:
                    text = text[:117] + "..."
                items.append(text)

        # If section has content but no extracted items, add section title
        if section.blocks and not any(
            isinstance(b, (ListBlock, ParagraphBlock)) for b in section.blocks
        ):
            items.append(f"Added: {section.title}")

        for sub in section.subsections:
            self._walk_section(sub, categories)

    @staticmethod
    def _classify(title: str) -> str:
        """Classify a section into a changelog category."""
        lower = title.lower()
        if any(w in lower for w in ("feature", "highlight", "capability", "what")):
            return "features"
        if any(w in lower for w in ("install", "setup", "getting started", "quickstart")):
            return "setup"
        if any(w in lower for w in ("api", "endpoint", "route", "usage")):
            return "api"
        if any(w in lower for w in ("config", "environment", "setting", "option")):
            return "configuration"
        if any(w in lower for w in ("architect", "design", "diagram", "overview")):
            return "architecture"
        if any(w in lower for w in ("test", "qa", "quality")):
            return "testing"
        if any(w in lower for w in ("deploy", "ci", "cd", "docker", "kubernetes")):
            return "devops"
        if any(w in lower for w in ("roadmap", "todo", "planned", "future")):
            return "roadmap"
        if any(w in lower for w in ("security", "auth")):
            return "security"
        if any(w in lower for w in ("contribut", "community")):
            return "community"
        return "documentation"

    # ------------------------------------------------------------------
    # KG-based entries
    # ------------------------------------------------------------------

    def _extract_tech_entries(self) -> list[str]:
        """Extract tech-stack entries from KG."""
        if not self.kg:
            return []
        entries: list[str] = []
        for e in self.kg.entities:
            if e.entity_type in (
                EntityType.TECHNOLOGY, EntityType.FRAMEWORK,
                EntityType.LANGUAGE, EntityType.DATABASE,
                EntityType.CLOUD_SERVICE, EntityType.PLATFORM,
            ):
                cat = e.entity_type.value.replace("_", " ").title()
                entries.append(f"**{e.name}** ({cat})")
        return entries[:20]

    def _extract_dependencies(self) -> list[str]:
        """Extract dependency/prerequisite entries from KG."""
        if not self.kg:
            return []
        entries: list[str] = []
        for e in self.kg.entities_of_type(EntityType.PREREQUISITE):
            entries.append(f"Requires: {e.name}")
        return entries[:15]
