# Implements: REQ-d00224-A+B+C+D
# Implements: REQ-d00225-A+B
"""Glossary and term index generators for defined terms.

Produces glossary, term index, and collection manifest files in
markdown or JSON format from a TermDictionary.
"""

from __future__ import annotations

import argparse
import json
from collections import defaultdict
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from elspais.graph.terms import TermDictionary, TermEntry

_HEADER = (
    "<!-- Auto-generated by: elspais fix -->\n"
    "<!-- Do not edit manually; changes will be overwritten. -->\n"
)


# Implements: REQ-d00224-A
def generate_glossary(td: TermDictionary, format: str = "markdown") -> str:
    """Generate a glossary from all defined terms.

    Args:
        td: The term dictionary to generate from.
        format: "markdown" or "json".

    Returns:
        Formatted glossary content.
    """
    entries = sorted(td.iter_all(), key=lambda e: e.term.lower())

    if format == "json":
        data = []
        for entry in entries:
            item = {
                "term": entry.term,
                "definition": entry.definition,
                "defined_in": entry.defined_in,
                "namespace": entry.namespace,
                "collection": entry.collection,
                "indexed": entry.indexed,
            }
            data.append(item)
        return json.dumps(data, indent=2)

    # Markdown format
    lines = [_HEADER, "# Glossary\n"]
    current_letter = ""

    for entry in entries:
        first_letter = entry.term[0].upper()
        if first_letter != current_letter:
            current_letter = first_letter
            lines.append(f"\n## {current_letter}\n")

        # Term with annotation
        annotation = ""
        if entry.collection:
            annotation = " *(collection)*"
        elif not entry.indexed:
            annotation = " *(not indexed)*"

        lines.append(f"**{entry.term}**{annotation}")
        lines.append(f": {entry.definition}")
        lines.append(f"*Defined in: {entry.defined_in} ({entry.namespace})*\n")

    return "\n".join(lines)


# Implements: REQ-d00224-B
def generate_term_index(td: TermDictionary, format: str = "markdown") -> str:
    """Generate a term index from indexed terms only.

    Args:
        td: The term dictionary to generate from.
        format: "markdown" or "json".

    Returns:
        Formatted term index content.
    """
    entries = sorted(td.iter_indexed(), key=lambda e: e.term.lower())

    if format == "json":
        data = []
        for entry in entries:
            refs_by_ns: dict[str, list[str]] = defaultdict(list)
            for ref in entry.references:
                refs_by_ns[ref.namespace].append(ref.node_id)
            data.append(
                {
                    "term": entry.term,
                    "references": dict(refs_by_ns),
                }
            )
        return json.dumps(data, indent=2)

    # Markdown format
    lines = [_HEADER, "# Term Index\n"]

    for entry in entries:
        lines.append(f"\n## {entry.term}\n")

        # Group references by namespace
        refs_by_ns: dict[str, list[str]] = defaultdict(list)
        for ref in entry.references:
            if ref.node_id not in refs_by_ns[ref.namespace]:
                refs_by_ns[ref.namespace].append(ref.node_id)

        for ns in sorted(refs_by_ns):
            lines.append(f"**{ns}:**")
            for node_id in refs_by_ns[ns]:
                lines.append(f"- {node_id}")
            lines.append("")

    return "\n".join(lines)


# Implements: REQ-d00224-C
def generate_collection_manifest(entry: TermEntry, format: str = "markdown") -> str:
    """Generate a standalone collection manifest for one collection term.

    Args:
        entry: The TermEntry (must have collection=True).
        format: "markdown" or "json".

    Returns:
        Formatted manifest content.
    """
    if format == "json":
        refs_by_ns: dict[str, list[str]] = defaultdict(list)
        for ref in entry.references:
            refs_by_ns[ref.namespace].append(ref.node_id)
        data = {
            "term": entry.term,
            "definition": entry.definition,
            "references": dict(refs_by_ns),
        }
        return json.dumps(data, indent=2)

    # Markdown format
    lines = [_HEADER, f"# {entry.term}\n"]
    lines.append(f": {entry.definition}\n")

    refs_by_ns: dict[str, list[str]] = defaultdict(list)
    for ref in entry.references:
        if ref.node_id not in refs_by_ns[ref.namespace]:
            refs_by_ns[ref.namespace].append(ref.node_id)

    for ns in sorted(refs_by_ns):
        lines.append(f"**{ns}:**")
        for node_id in refs_by_ns[ns]:
            lines.append(f"- {node_id}")
        lines.append("")

    return "\n".join(lines)


# Implements: REQ-d00225-B
def write_term_outputs(
    td: TermDictionary,
    output_dir: str | Path,
    format: str = "markdown",
) -> list[str]:
    """Write glossary, term index, and collection manifests to output_dir.

    Returns:
        List of generated file paths.
    """
    out = Path(output_dir)
    out.mkdir(parents=True, exist_ok=True)
    generated: list[str] = []

    ext = ".json" if format == "json" else ".md"

    # Glossary
    glossary_path = out / f"glossary{ext}"
    glossary_path.write_text(generate_glossary(td, format=format))
    generated.append(str(glossary_path))

    # Term index
    index_path = out / f"term-index{ext}"
    index_path.write_text(generate_term_index(td, format=format))
    generated.append(str(index_path))

    # Collection manifests
    collections_dir = out / "collections"
    for entry in td.iter_collections():
        collections_dir.mkdir(parents=True, exist_ok=True)
        slug = entry.term.lower().replace(" ", "-")
        manifest_path = collections_dir / f"{slug}{ext}"
        manifest_path.write_text(generate_collection_manifest(entry, format=format))
        generated.append(str(manifest_path))

    return generated


# Implements: REQ-d00225-A
def run(args: argparse.Namespace) -> int:
    """Run glossary or term-index CLI command."""
    from elspais.commands import _engine

    def _compute(graph, config, params):  # type: ignore
        td = graph._terms if hasattr(graph, "_terms") else None
        if not td:
            # Try root repo graph
            for entry in graph._repos.values():
                if entry.graph and hasattr(entry.graph, "_terms"):
                    td = entry.graph._terms
                    break
        if td is None:
            return {"error": "No terms found in graph"}

        fmt = params.get("format", "markdown")

        if params.get("command") == "glossary":
            content = generate_glossary(td, format=fmt)
            print(content)
        else:
            content = generate_term_index(td, format=fmt)
            print(content)

        return {"success": True}

    params: dict[str, str] = {
        "command": getattr(args, "command", "glossary"),
        "format": getattr(args, "format", "markdown"),
    }
    output_dir = getattr(args, "output_dir", None)
    if output_dir:
        params["output_dir"] = output_dir

    _engine.call(
        f"/api/run/{params['command']}",
        params,
        _compute,
        config_path=getattr(args, "config", None),
    )
    return 0
