"""Per-file coverage threshold validation for quality-managed modules."""

from __future__ import annotations

import xml.etree.ElementTree as ET
from pathlib import Path
from typing import Any


def _load_percentages(xml_path: Path, root: Path) -> dict[str, float]:
    """Load per-file line-rate percentages from a Cobertura XML report."""
    tree = ET.parse(xml_path)
    xml_root = tree.getroot()
    source_roots = [
        Path(node.text)
        for node in xml_root.findall("./sources/source")
        if node.text
    ]
    percentages: dict[str, float] = {}
    for class_node in xml_root.findall(".//class"):
        filename = class_node.attrib.get("filename")
        line_rate = class_node.attrib.get("line-rate")
        if not filename or line_rate is None:
            continue
        normalized = filename.replace("\\", "/")
        if normalized.startswith("/"):
            key = Path(normalized).relative_to(root).as_posix()
        else:
            key = normalized
            for source_root in source_roots:
                candidate = source_root / filename
                if candidate.exists():
                    key = candidate.relative_to(root).as_posix()
                    break
        percentages[key] = float(line_rate) * 100.0
    return percentages


def run_check(contract: dict[str, Any], root: Path, xml_path: Path) -> list[str]:
    """Return human-readable issues for tracked files below the coverage floor.

    The report is intentionally per-file so a single weak module cannot hide
    behind aggregate suite coverage.
    """
    if not xml_path.exists():
        return [f"coverage xml missing: {xml_path.relative_to(root)}"]

    percentages = _load_percentages(xml_path, root)
    minimum = float(contract.get("coverage", {}).get("minimum_percent", 95.0))
    issues: list[str] = []

    for relative_path in contract.get("coverage", {}).get("tracked_files", []):
        normalized = relative_path.replace("\\", "/")
        percent = percentages.get(normalized)
        if percent is None:
            issues.append(f"coverage missing for tracked file: {relative_path}")
            continue
        if percent + 1e-9 < minimum:
            issues.append(
                f"coverage below {minimum:.1f}%: {relative_path} ({percent:.1f}%)"
            )

    return issues


def compute_workspace_line_coverage(
    contract: dict[str, Any],
    root: Path,
    xml_path: Path,
) -> float:
    """Compute mean line coverage across tracked files present in the XML."""

    if not xml_path.exists():
        return 0.0

    percentages = _load_percentages(xml_path, root)
    samples: list[float] = []
    for relative_path in contract.get("coverage", {}).get("tracked_files", []):
        normalized = relative_path.replace("\\", "/")
        percent = percentages.get(normalized)
        if percent is not None:
            samples.append(percent)
    if not samples:
        return 0.0
    return round(sum(samples) / len(samples), 3)
