"""Rich terminal formatter and JSON serializer for ConflictReport.

Provides two public functions:
- ``format_report``: renders a ``ConflictReport`` to the terminal using Rich,
  following the canonical output format defined in Section 8 of APPLICATION_BIBLE.md.
- ``format_json``: serializes a ``ConflictReport`` to a JSON string matching the
  schema defined in Section 8 of APPLICATION_BIBLE.md.
"""

from __future__ import annotations

import json
from typing import Any

from rich.console import Console

from depwhy.graph.models import ConflictReport, FixCandidate


def format_report(report: ConflictReport, no_color: bool = False) -> None:
    """Render a ConflictReport to the terminal using Rich styled output.

    The canonical output format (Section 8 of APPLICATION_BIBLE.md)::

        {N} conflict(s) found          ← red if N > 0, green if 0

        {package_name}                 ← bold, no color
          Problem: {explanation}       ← "Problem:" bold red
          Solution: {description}      ← "Solution:" bold green
            → {pip_command}            ← dim/grey, 4-space indent
          Alternative: {description}   ← "Alternative:" bold yellow
            → {pip_command}            ← dim/grey, 4-space indent

    One blank line separates consecutive conflict blocks.  If there are zero
    conflicts, ``✓ No conflicts found`` is printed in green and the function
    returns immediately.

    Args:
        report: The ``ConflictReport`` produced by ``analyze()``.
        no_color: When ``True``, create the ``Console`` with ``no_color=True``
                  so that all Rich markup is stripped from the output.
    """
    console = Console(no_color=no_color)

    n = len(report.conflicts)
    if n == 0:
        console.print("[green]✓ No conflicts found[/green]")
        return

    header_color = "red" if n > 0 else "green"
    console.print(f"[{header_color}]{n} conflict(s) found[/{header_color}]")

    for i, conflict in enumerate(report.conflicts):
        if i > 0:
            console.print()

        # Package name: bold, no color
        console.print(f"\n[bold]{conflict.package}[/bold]")

        # Problem line
        console.print(f"  [bold red]Problem:[/bold red] {conflict.problem}")

        # Solution
        _print_fix(console, conflict.solution, label="Solution", label_style="bold green")

        # Alternative (may be None)
        if conflict.alternative is not None:
            _print_fix(
                console, conflict.alternative, label="Alternative", label_style="bold yellow"
            )


def _print_fix(console: Console, fix: FixCandidate, label: str, label_style: str) -> None:
    """Print a single FixCandidate block (Solution or Alternative).

    Args:
        console: The Rich ``Console`` to write to.
        fix: The ``FixCandidate`` to render.
        label: The label text to prefix (e.g. ``"Solution"`` or ``"Alternative"``).
        label_style: A Rich markup style string applied to the label (e.g. ``"bold green"``).
    """
    console.print(f"  [{label_style}]{label}:[/{label_style}] {fix.description}")
    console.print(f"    [dim]→ {fix.pip_command}[/dim]")


def format_json(report: ConflictReport) -> str:
    """Serialize a ConflictReport to a JSON string.

    The output matches the schema from Section 8 of APPLICATION_BIBLE.md::

        {
          "has_conflicts": true,
          "analyzed_packages": 42,
          "conflicts": [
            {
              "package": "urllib3",
              "problem": "...",
              "solution": {
                "description": "...",
                "pip_command": "pip install urllib3==1.26.18",
                "is_alternative": false
              },
              "alternative": null
            }
          ],
          "warnings": []
        }

    The ``alternative`` key is ``null`` when ``Conflict.alternative`` is ``None``.

    Args:
        report: The ``ConflictReport`` to serialize.

    Returns:
        A JSON string with two-space indentation, suitable for ``--json`` output.
    """

    def _fix_candidate_to_dict(fix: FixCandidate) -> dict[str, Any]:
        return {
            "description": fix.description,
            "pip_command": fix.pip_command,
            "is_alternative": fix.is_alternative,
        }

    conflicts_list: list[dict[str, Any]] = []
    for conflict in report.conflicts:
        alternative_dict = (
            _fix_candidate_to_dict(conflict.alternative)
            if conflict.alternative is not None
            else None
        )
        conflicts_list.append(
            {
                "package": conflict.package,
                "problem": conflict.problem,
                "solution": _fix_candidate_to_dict(conflict.solution),
                "alternative": alternative_dict,
            }
        )

    payload: dict[str, Any] = {
        "has_conflicts": report.has_conflicts,
        "analyzed_packages": report.analyzed_packages,
        "conflicts": conflicts_list,
        "warnings": list(report.warnings),
    }
    return json.dumps(payload, indent=2)
