#!/usr/bin/env python3
"""Map changed repository paths to suites using the CI impact manifest."""

from __future__ import annotations

import argparse
import json
from fnmatch import fnmatchcase
from pathlib import Path
from typing import Any

ROOT = Path(__file__).resolve().parents[2]
MANIFEST = ROOT / ".github" / "ci-impact.json"


def load_manifest() -> dict[str, Any]:
    with MANIFEST.open(encoding="utf-8") as handle:
        return json.load(handle)


def suites_for_path(path: str, manifest: dict[str, Any]) -> tuple[set[str], set[str]]:
    matching_rules: list[dict[str, Any]] = []
    for rule in manifest["rules"]:
        if any(fnmatchcase(path, pattern) for pattern in rule["patterns"]):
            matching_rules.append(rule)
    if matching_rules:
        return (
            {str(rule["name"]) for rule in matching_rules},
            {str(suite) for rule in matching_rules for suite in rule["suites"]},
        )
    fallback = manifest["fallback"]
    return {str(fallback["name"])}, set(fallback["suites"])


def classify(paths: list[str], *, exhaustive: bool, force_lean: bool) -> dict[str, str]:
    manifest = load_manifest()
    exhaustive = exhaustive or not paths
    all_suites = set(manifest["suites"])
    suites: set[str] = set()
    owners: set[str] = set()

    if exhaustive or not paths:
        suites = all_suites
    else:
        for path in paths:
            path_owners, owned_suites = suites_for_path(path, manifest)
            owners.update(path_owners)
            suites.update(owned_suites)

    if force_lean:
        suites.add("lean")

    if exhaustive:
        classification = "exhaustive"
    elif suites == all_suites:
        classification = "full"
    elif not suites:
        classification = "docs"
    elif suites == {"npm"}:
        classification = "npm"
    elif suites == {"lean"}:
        classification = "lean"
    elif suites <= {"core", "static"} and "core" in suites:
        classification = "python-core"
    elif suites <= {"integration", "static"} and "integration" in suites:
        classification = "python-integration"
    else:
        classification = "selective"

    run_core = "core" in suites
    run_integration = "integration" in suites
    plan: dict[str, str] = {
        "classification": classification,
        "run-python": str(run_core or run_integration).lower(),
        "run-core": str(run_core).lower(),
        "run-integration": str(run_integration).lower(),
        "run-coverage": str(exhaustive and run_core and run_integration).lower(),
        "run-compatibility": str(exhaustive and run_core and run_integration).lower(),
    }
    for suite in ("lean", "npm", "static", "build", "security", "duplicate"):
        plan[f"run-{suite}"] = str(suite in suites).lower()
    return plan


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--force-full",
        "--force-exhaustive",
        dest="exhaustive",
        action="store_true",
        help="Run every suite, including coverage and compatibility.",
    )
    parser.add_argument("--force-lean", action="store_true")
    parser.add_argument("paths", nargs="*")
    args = parser.parse_args()
    paths = args.paths[1:] if args.paths[:1] == ["--"] else args.paths
    for key, value in classify(
        paths,
        exhaustive=args.exhaustive,
        force_lean=args.force_lean,
    ).items():
        print(f"{key}={value}")


if __name__ == "__main__":
    main()
