#!/usr/bin/env python3
"""Generate a markdown coverage report for PR comments.

Reads coverage JSON reports and outputs markdown to stdout.
Uses only the Python standard library.

Usage:
    python scripts/coverage_report.py \
        --unit coverage-unit.json \
        --integration coverage-integration.json \
        --combined coverage-combined.json \
        [--commit SHA]
"""

from __future__ import annotations

import argparse
import json
import os
import sys
from datetime import UTC, datetime
from pathlib import Path

SENTINEL = "<!-- coverage-report -->"


def load_json(path: str | None) -> dict | None:
    """Load a coverage JSON file, returning None if missing or invalid."""
    if not path:
        return None
    p = Path(path)
    if not p.exists():
        print(f"Warning: {path} not found, skipping", file=sys.stderr)
        return None
    try:
        with p.open() as f:
            return json.load(f)
    except (json.JSONDecodeError, OSError) as exc:
        print(f"Warning: failed to read {path}: {exc}", file=sys.stderr)
        return None


def pct(covered: int, total: int) -> str:
    """Format a percentage string."""
    if total == 0:
        return "N/A"
    return f"{covered / total * 100:.1f}%"


def extract_summary(data: dict) -> dict:
    """Extract summary metrics from a coverage JSON report."""
    totals = data.get("totals", {})
    return {
        "lines_covered": totals.get("covered_lines", 0),
        "lines_total": totals.get("num_statements", 0),
        "line_pct": totals.get("percent_covered", 0.0),
        "branches_covered": totals.get("covered_branches", 0),
        "branches_total": totals.get("num_branches", 0),
    }


def format_summary_row(name: str, summary: dict) -> str:
    """Format a single summary table row."""
    line_pct = f"{summary['line_pct']:.1f}%"
    lines = f"{summary['lines_covered']}/{summary['lines_total']}"
    branch_pct = pct(summary["branches_covered"], summary["branches_total"])
    return f"| {name} | {line_pct} | {lines} | {branch_pct} |"


def build_module_breakdown(data: dict) -> list[tuple[str, dict]]:
    """Aggregate coverage at the specwright.{subpackage} level."""
    modules: dict[str, dict] = {}
    for filepath, file_data in data.get("files", {}).items():
        # Convert path like "src/specwright/agent/client.py" to "specwright.agent"
        parts = filepath.replace("/", ".").replace(".py", "").split(".")
        # Find 'specwright' in the parts
        try:
            sw_idx = parts.index("specwright")
        except ValueError:
            continue
        # Group at specwright.{next_part} level, or just "specwright" for top-level files
        module = f"specwright.{parts[sw_idx + 1]}" if sw_idx + 1 < len(parts) - 1 else "specwright"

        summary = file_data.get("summary", {})
        if module not in modules:
            modules[module] = {
                "lines_covered": 0,
                "lines_total": 0,
                "branches_covered": 0,
                "branches_total": 0,
            }
        m = modules[module]
        m["lines_covered"] += summary.get("covered_lines", 0)
        m["lines_total"] += summary.get("num_statements", 0)
        m["branches_covered"] += summary.get("covered_branches", 0)
        m["branches_total"] += summary.get("num_branches", 0)

    result = []
    for name, m in sorted(modules.items()):
        line_pct = (m["lines_covered"] / m["lines_total"] * 100) if m["lines_total"] else 0.0
        m["line_pct"] = line_pct
        result.append((name, m))
    return result


def main() -> None:
    parser = argparse.ArgumentParser(description="Generate coverage PR comment")
    parser.add_argument("--unit", help="Path to unit coverage JSON")
    parser.add_argument("--integration", help="Path to integration coverage JSON")
    parser.add_argument("--combined", help="Path to combined coverage JSON")
    parser.add_argument("--commit", default=os.environ.get("GITHUB_SHA", "unknown"))
    args = parser.parse_args()

    unit_data = load_json(args.unit)
    integration_data = load_json(args.integration)
    combined_data = load_json(args.combined)

    if not (unit_data or integration_data or combined_data):
        print(SENTINEL)
        print("## Coverage Report")
        print()
        print("No coverage data available.")
        return

    lines: list[str] = []
    lines.append(SENTINEL)
    lines.append("## Coverage Report")
    lines.append("")
    lines.append("| Suite | Line Coverage | Lines (covered/total) | Branch Coverage |")
    lines.append("|-------|-------------:|----------------------:|----------------:|")

    if unit_data:
        lines.append(format_summary_row("Unit", extract_summary(unit_data)))
    if integration_data:
        lines.append(format_summary_row("Integration", extract_summary(integration_data)))
    if combined_data:
        lines.append(format_summary_row("**Combined**", extract_summary(combined_data)))

    # Module breakdown from combined (or best available) report
    breakdown_source = combined_data or unit_data
    if breakdown_source:
        modules = build_module_breakdown(breakdown_source)
        if modules:
            lines.append("")
            lines.append("<details>")
            lines.append("<summary>Per-module breakdown</summary>")
            lines.append("")
            lines.append("| Module | Line Coverage | Lines | Branch Coverage |")
            lines.append("|--------|-------------:|------:|----------------:|")
            for name, m in modules:
                lines.append(format_summary_row(f"`{name}`", m))
            lines.append("")
            lines.append("</details>")

    # Footer
    short_sha = args.commit[:8] if len(args.commit) >= 8 else args.commit
    timestamp = datetime.now(UTC).strftime("%Y-%m-%d %H:%M UTC")
    lines.append("")
    lines.append(f"*Updated at {timestamp} for commit `{short_sha}`*")

    print("\n".join(lines))


if __name__ == "__main__":
    main()
