"""ASV benchmarks for coverage collection pipeline runtime.

Measures the performance of operations that contribute to the
``nox -s coverage_report`` wall-clock time:
- Slipcover configuration extraction from noxfile / pyproject
- Coverage threshold validation and percentage parsing
- Coverage JSON report parsing and summary extraction
- XML report generation overhead (file I/O proxy)
"""

from __future__ import annotations

import ast
import json
import re
from pathlib import Path

_PROJECT_ROOT = Path(__file__).resolve().parents[1]
_NOXFILE_PATH = _PROJECT_ROOT / "noxfile.py"
_PYPROJECT_PATH = _PROJECT_ROOT / "pyproject.toml"
_COVERAGE_JSON = _PROJECT_ROOT / "build" / "coverage.json"
_COVERAGE_XML = _PROJECT_ROOT / "build" / "coverage.xml"

# Threshold used by the project for coverage enforcement.
_COVERAGE_THRESHOLD = 97


class CoverageConfigExtractionSuite:
    """Benchmark extracting coverage configuration from project files.

    The coverage report session reads noxfile.py and pyproject.toml to
    determine source paths, omit patterns, and the fail-under threshold.
    """

    timeout = 60

    def setup(self) -> None:
        """Read configuration files once for reuse."""
        if _NOXFILE_PATH.exists():
            self.noxfile_text: str = _NOXFILE_PATH.read_text()
        else:
            self.noxfile_text = ""
        if _PYPROJECT_PATH.exists():
            self.pyproject_text: str = _PYPROJECT_PATH.read_text()
        else:
            self.pyproject_text = ""

    def time_parse_noxfile_ast(self) -> None:
        """Benchmark full AST parse of noxfile.py."""
        if self.noxfile_text:
            ast.parse(self.noxfile_text)

    def time_extract_coverage_threshold(self) -> None:
        """Benchmark extracting COVERAGE_THRESHOLD from noxfile AST."""
        if self.noxfile_text:
            tree = ast.parse(self.noxfile_text)
            for node in ast.walk(tree):
                if isinstance(node, ast.Assign):
                    for target in node.targets:
                        if (
                            isinstance(target, ast.Name)
                            and target.id == "COVERAGE_THRESHOLD"
                            and isinstance(node.value, ast.Constant)
                        ):
                            _ = node.value.value

    def time_extract_omit_patterns(self) -> None:
        """Benchmark extracting omit patterns from noxfile text."""
        if self.noxfile_text:
            re.findall(r'"(\*[^"]+)"', self.noxfile_text)

    def time_parse_pyproject_coverage_section(self) -> None:
        """Benchmark extracting [tool.coverage] from pyproject.toml."""
        if self.pyproject_text:
            import tomllib

            data = tomllib.loads(self.pyproject_text)
            _ = data.get("tool", {}).get("coverage", {})

    def time_extract_source_paths(self) -> None:
        """Benchmark extracting coverage source paths from pyproject."""
        if self.pyproject_text:
            import tomllib

            data = tomllib.loads(self.pyproject_text)
            _ = (
                data.get("tool", {})
                .get("coverage", {})
                .get("run", {})
                .get("source", [])
            )

    def time_extract_fail_under_regex(self) -> None:
        """Benchmark regex extraction of --fail-under value from noxfile."""
        if self.noxfile_text:
            re.findall(r"--fail-under=(\d+)", self.noxfile_text)


class CoverageReportParsingSuite:
    """Benchmark parsing coverage report outputs.

    After slipcover runs, the coverage pipeline parses JSON and XML
    reports to extract totals and enforce thresholds.
    """

    timeout = 60

    def setup(self) -> None:
        """Load coverage report data if available."""
        self.json_text: str = ""
        self.xml_text: str = ""
        if _COVERAGE_JSON.exists():
            self.json_text = _COVERAGE_JSON.read_text()
        if _COVERAGE_XML.exists():
            self.xml_text = _COVERAGE_XML.read_text()

    def time_parse_coverage_json(self) -> None:
        """Benchmark parsing the slipcover JSON report."""
        if self.json_text:
            json.loads(self.json_text)

    def time_extract_coverage_percentage(self) -> None:
        """Benchmark extracting coverage percentage from JSON report."""
        if self.json_text:
            payload = json.loads(self.json_text)
            summary = payload.get("summary") or payload.get("totals") or {}
            for key in (
                "percent_covered",
                "percent_covered_display",
                "line_coverage_percent",
            ):
                value = summary.get(key)
                if value is not None:
                    break

    def time_threshold_comparison(self) -> None:
        """Benchmark the threshold enforcement logic."""
        if self.json_text:
            payload = json.loads(self.json_text)
            summary = payload.get("summary") or payload.get("totals") or {}
            percent_value = None
            for key in (
                "percent_covered",
                "percent_covered_display",
                "line_coverage_percent",
            ):
                percent_value = summary.get(key)
                if percent_value is not None:
                    break
            if percent_value is None:
                covered = summary.get("lines_covered") or summary.get("covered_lines")
                total = summary.get("lines") or summary.get("total_lines")
                if covered is not None and total:
                    percent_value = (float(covered) / float(total)) * 100.0
            if isinstance(percent_value, str):
                percent_value = percent_value.strip().rstrip("%")
            if percent_value is not None:
                pct = round(float(percent_value), 1)
                _ = pct >= _COVERAGE_THRESHOLD

    def time_parse_coverage_xml(self) -> None:
        """Benchmark parsing the coverage XML report."""
        if self.xml_text:
            import xml.etree.ElementTree as ET

            ET.fromstring(self.xml_text)


class CoverageFileSuite:
    """Benchmark coverage-related file I/O operations."""

    timeout = 60

    def setup(self) -> None:
        """Prepare paths for file I/O benchmarks."""
        self.build_dir: Path = _PROJECT_ROOT / "build"

    def time_discover_slipcover_files(self) -> None:
        """Benchmark discovering .slipcover.*.json files in build/."""
        if self.build_dir.exists():
            list(self.build_dir.glob(".slipcover.*.json"))

    def time_discover_coverage_artifacts(self) -> None:
        """Benchmark discovering all coverage-related artifacts."""
        if self.build_dir.exists():
            _json = list(self.build_dir.glob("coverage*.json"))
            _xml = list(self.build_dir.glob("coverage*.xml"))
            _txt = list(self.build_dir.glob("coverage*.txt"))

    def track_source_file_count(self) -> int:
        """Track number of Python source files under coverage."""
        src_dir = _PROJECT_ROOT / "src"
        if src_dir.exists():
            return len(list(src_dir.rglob("*.py")))
        return 0

    def track_source_total_bytes(self) -> int:
        """Track total byte size of Python source files."""
        src_dir = _PROJECT_ROOT / "src"
        if src_dir.exists():
            return sum(f.stat().st_size for f in src_dir.rglob("*.py"))
        return 0


CoverageFileSuite.track_source_file_count.unit = "files"
CoverageFileSuite.track_source_total_bytes.unit = "bytes"
