"""``mgf-fed standards`` — print engineering-standards docs.

Closes the v0.9.0 deliverable: every project that depends on
``mgf-common`` should be able to read the canonical engineering
standards on demand without vendoring or path-hacking. The
standards corpus is the federation's source of truth and lives in
the ``mgf-standard`` sibling (a runtime dependency of
``mgf-common``); it ships as wheel data there and is resolved
through ``mgf.standard.standards_dir()``.

Two modes:

  - ``mgf-fed standards`` (no arg) — list available topics.
  - ``mgf-fed standards <topic>`` — print that doc to stdout.

Topic names are case-insensitive and accept either the full file
name ("DESIGN_PRINCIPLES") or a shortened alias ("design").
"""

from __future__ import annotations

import sys
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import argparse
    from pathlib import Path
    from typing import Any


# Aliases let consumers type ``mgf-fed standards logging`` instead
# of ``mgf-fed standards LOGGING``. The canonical file name (upper-
# case) is the value; the alias (lower-case) is the key.
_ALIASES: dict[str, str] = {
    # DESIGN_PRINCIPLES
    "design": "DESIGN_PRINCIPLES",
    "principles": "DESIGN_PRINCIPLES",
    "dp": "DESIGN_PRINCIPLES",
    # ERROR_HANDLING
    "errors": "ERROR_HANDLING",
    "error_handling": "ERROR_HANDLING",
    "eh": "ERROR_HANDLING",
    # LOGGING
    "logging": "LOGGING",
    "logs": "LOGGING",
    "lg": "LOGGING",
    # CONFIGURATION
    "config": "CONFIGURATION",
    "configuration": "CONFIGURATION",
    "cf": "CONFIGURATION",
    # SECURITY_STANDARD
    "security": "SECURITY_STANDARD",
    "security_standard": "SECURITY_STANDARD",
    "sec": "SECURITY_STANDARD",
    "sc": "SECURITY_STANDARD",
    # TESTING
    "testing": "TESTING",
    "tests": "TESTING",
    "ts": "TESTING",
    # API_DESIGN
    "api": "API_DESIGN",
    "api_design": "API_DESIGN",
    "ap": "API_DESIGN",
    # SCOPE
    "scope": "SCOPE",
    # COMMON_COMPONENTS
    "components": "COMMON_COMPONENTS",
    "common_components": "COMMON_COMPONENTS",
    # GUI_CONSUMERS
    "gui": "GUI_CONSUMERS",
    "gui_consumers": "GUI_CONSUMERS",
    "pyside6": "GUI_CONSUMERS",
    "qt": "GUI_CONSUMERS",
    "desktop": "GUI_CONSUMERS",
    # AGENTS
    "agents": "AGENTS",
    "agent": "AGENTS",
    "ag": "AGENTS",
    # DOCUMENTATION
    "docs": "DOCUMENTATION",
    "doc": "DOCUMENTATION",
    "documentation": "DOCUMENTATION",
    # NAMING
    "naming": "NAMING",
    "names": "NAMING",
    "nm": "NAMING",
    # OBSERVABILITY
    "observability": "OBSERVABILITY",
    "otel": "OBSERVABILITY",
    "tracing": "OBSERVABILITY",
    "ob": "OBSERVABILITY",
    # OPERABILITY
    "operability": "OPERABILITY",
    "operations": "OPERABILITY",
    "ops": "OPERABILITY",
    "op": "OPERABILITY",
    # PERFORMANCE
    "performance": "PERFORMANCE",
    "perf": "PERFORMANCE",
    "pf": "PERFORMANCE",
    # PRESENTATION_LAYER
    "presentation": "PRESENTATION_LAYER",
    "presentation_layer": "PRESENTATION_LAYER",
    "pr": "PRESENTATION_LAYER",
    # DEPLOYMENT
    "deployment": "DEPLOYMENT",
    "deploy": "DEPLOYMENT",
    # PACKAGE_DISTRIBUTION
    "package_distribution": "PACKAGE_DISTRIBUTION",
    "packaging": "PACKAGE_DISTRIBUTION",
    "distribution": "PACKAGE_DISTRIBUTION",
    "codeberg": "PACKAGE_DISTRIBUTION",
    "registry": "PACKAGE_DISTRIBUTION",
    "pkg": "PACKAGE_DISTRIBUTION",
    # RELEASING
    "releasing": "RELEASING",
    "release": "RELEASING",
    "rel": "RELEASING",
    # TIME
    "time": "TIME",
    "tm": "TIME",
    # WORKFLOWS / WORKFLOWS_PATTERNS
    "workflows": "WORKFLOWS",
    "ci": "WORKFLOWS",
    "woodpecker": "WORKFLOWS",
    "wf": "WORKFLOWS",
    "workflows_patterns": "WORKFLOWS_PATTERNS",
    "workflow_patterns": "WORKFLOWS_PATTERNS",
    # README (index)
    "readme": "README",
    "index": "README",
}


def add_standards_args(parser: argparse.ArgumentParser) -> None:
    """Register the ``standards`` subcommand's arguments."""
    parser.add_argument(
        "topic",
        nargs="?",
        default=None,
        help=(
            "Standards-doc topic to print (e.g., LOGGING, ERROR_HANDLING, "
            "SECURITY, AGENTS, RELEASING). The full doc name always works "
            "(case-insensitive); short aliases are accepted too (logging / "
            "errors / config / api / security / testing / agents / "
            "releasing / observability / performance / naming / deploy / "
            "time / workflows / docs / …). Omit to list all available "
            "topics."
        ),
    )
    parser.add_argument(
        "--paths",
        action="store_true",
        help=(
            "List the resolved file paths instead of printing the docs. "
            "Useful for piping into other tools or copying the canon."
        ),
    )


def run_standards(args: argparse.Namespace) -> int:
    """Print the requested standards doc, or list topics."""
    standards_dir = _resolve_standards_dir()
    if standards_dir is None:
        print(
            "error: the engineering-standards corpus is not installed. It "
            "ships in the `mgf-standard` extra — install it:\n"
            "    pip install -U 'mgf-common[standards]'   "
            "(or: uv add 'mgf-common[standards]')\n"
            "The corpus is an optional, Codeberg-only extra; a plain "
            "mgf-common install does not carry it. Editable-sibling "
            "alternative: ensure ../mgf-standard/docs/standards/ exists.",
            file=sys.stderr,
        )
        return 1

    topics = _list_topics(standards_dir)

    if args.topic is None:
        if args.paths:
            for name in topics:
                print(standards_dir / f"{name}.md")
        else:
            _print_topic_index(topics)
        return 0

    canonical = _resolve_topic(args.topic, topics)
    if canonical is None:
        print(
            f"error: unknown standards topic {args.topic!r}. Available: {', '.join(topics)}.",
            file=sys.stderr,
        )
        print(
            "(Aliases also accepted; run `mgf-fed standards` with no arg for the topic list.)",
            file=sys.stderr,
        )
        return 1

    target = standards_dir / f"{canonical}.md"
    if args.paths:
        print(target)
        return 0

    print(target.read_text(encoding="utf-8"))
    return 0


# ---------------------------------------------------------------------------
# Path resolution. The corpus lives in the ``mgf-standard`` sibling (the
# federation's standards source of truth, a runtime dependency of
# mgf-common). We resolve it through that package's PUBLIC
# ``standards_dir()`` seam — never by reaching into its private
# ``_standards`` data dir — so the federation's closed-box rule stays
# intact. ``standards_dir`` prefers the wheel-shipped data and falls back
# to a sibling repo checkout for editable installs.
# ---------------------------------------------------------------------------


def _resolve_standards_dir() -> Path | None:
    """Find the directory holding ``DESIGN_PRINCIPLES.md`` etc.

    Delegates to :func:`mgf.standard.standards_dir`. Returns ``None``
    if ``mgf-standard`` is not importable or the corpus cannot be
    located, so the caller can print an actionable error.
    """
    try:
        import mgf.standard
    except (ModuleNotFoundError, ImportError):
        return None
    try:
        return mgf.standard.standards_dir()
    except FileNotFoundError:
        return None


def _resolve_libraries() -> list[dict[str, Any]] | None:
    """Return the federation library registry, or ``None`` if unresolvable.

    Delegates to :func:`mgf.standard.libraries` — the public seam over
    the registry shipped as ``mgf-standard`` wheel data. Returns
    ``None`` if ``mgf-standard`` is missing, too old to carry the
    registry (no ``libraries`` attribute), or the data can't be located,
    so callers degrade gracefully (the federation map is additive — it
    must never break the briefing).
    """
    try:
        import mgf.standard
    except (ModuleNotFoundError, ImportError):
        return None
    try:
        return mgf.standard.libraries()
    except (AttributeError, FileNotFoundError, ValueError, OSError):
        return None


def _list_topics(standards_dir: Path) -> list[str]:
    """Return canonical topic names (file stems, no extension)."""
    return sorted(p.stem for p in standards_dir.glob("*.md"))


def _resolve_topic(name: str, topics: list[str]) -> str | None:
    """Resolve a user-supplied name to the canonical file stem."""
    # Case-insensitive direct match.
    upper = name.upper()
    for canonical in topics:
        if canonical.upper() == upper:
            return canonical

    # Alias.
    aliased = _ALIASES.get(name.lower())
    if aliased and aliased in topics:
        return aliased

    return None


def _print_topic_index(topics: list[str]) -> None:
    """Print a human-readable index of available topics."""
    from mgf.common import __version__

    print(
        f"mgf-common {__version__} — engineering-standards docs",
    )
    print()
    print("Topics:")
    width = max(len(c) for c in topics) + 2
    for canonical in topics:
        print(f"  {canonical:<{width}}")
    print()
    print(
        "Print a topic:  mgf-fed standards <topic>",
    )
    print(
        "List paths:     mgf-fed standards --paths",
    )
    print(
        "Any topic above works by its full name (case-insensitive). "
        "Short aliases are also accepted, e.g.: logging, errors, config, "
        "api, security, testing, agents, releasing, observability, "
        "performance, naming, deploy, operability, time, workflows, "
        "presentation, docs, scope, components, design, gui.",
    )


__all__ = ["add_standards_args", "run_standards"]
