# scripts/run_all_plots.py
from __future__ import annotations

import argparse
import subprocess
import sys
from dataclasses import dataclass
from pathlib import Path
from typing import Iterable


PROJECT_ROOT = Path(__file__).resolve().parents[1]


@dataclass(frozen=True)
class PlotScript:
    key: str
    path: Path

    def display_path(self) -> str:
        try:
            return str(self.path.relative_to(PROJECT_ROOT))
        except ValueError:
            return str(self.path)


PLOT_SCRIPTS: tuple[PlotScript, ...] = (
    PlotScript(
        "plot_basic_variables",
        PROJECT_ROOT / "docs" / "03 - Premiers graphiques" / "scripts" / "plot_basic_variables.py",
    ),
    PlotScript(
        "plot_calendar_overview",
        PROJECT_ROOT / "docs" / "03 - Premiers graphiques" / "scripts" / "plot_calendar_overview.py",
    ),
    PlotScript(
        "plot_all_pairwise_scatter",
        PROJECT_ROOT / "docs" / "04 - Corrélations binaires" / "scripts" / "plot_all_pairwise_scatter.py",
    ),
    PlotScript(
        "plot_correlation_heatmap",
        PROJECT_ROOT / "docs" / "04 - Corrélations binaires" / "scripts" / "plot_correlation_heatmap.py",
    ),
    PlotScript("plot_diurnal_cycle", PROJECT_ROOT / "scripts" / "plot_diurnal_cycle.py"),
    PlotScript("plot_illuminance_focus", PROJECT_ROOT / "scripts" / "plot_illuminance_focus.py"),
    PlotScript(
        "plot_hexbin_explorations",
        PROJECT_ROOT / "docs" / "06 - Corrélations multiples" / "scripts" / "plot_hexbin_explorations.py",
    ),
    PlotScript("plot_monthly_patterns", PROJECT_ROOT / "scripts" / "plot_monthly_patterns.py"),
    PlotScript("plot_rain_event_composites", PROJECT_ROOT / "scripts" / "plot_rain_event_composites.py"),
    PlotScript("plot_rain_hyetograph", PROJECT_ROOT / "scripts" / "plot_rain_hyetograph.py"),
    PlotScript(
        "plot_lagged_correlations",
        PROJECT_ROOT / "docs" / "05 - Corrélations binaires avancées" / "scripts" / "plot_lagged_correlations.py",
    ),
    PlotScript(
        "plot_rolling_correlation_heatmap",
        PROJECT_ROOT / "docs" / "05 - Corrélations binaires avancées" / "scripts" / "plot_rolling_correlation_heatmap.py",
    ),
    PlotScript("plot_seasonal_overview", PROJECT_ROOT / "scripts" / "plot_seasonal_overview.py"),
    PlotScript(
        "plot_sun_elevation_relationships",
        PROJECT_ROOT / "scripts" / "plot_sun_elevation_relationships.py",
    ),
    PlotScript("plot_wind_conditionals", PROJECT_ROOT / "scripts" / "plot_wind_conditionals.py"),
    PlotScript("plot_wind_rose", PROJECT_ROOT / "scripts" / "plot_wind_rose.py"),
    PlotScript("plot_wind_rose_rain", PROJECT_ROOT / "scripts" / "plot_wind_rose_rain.py"),
)


def _normalize_key(name: str) -> str:
    normalized = name.strip()
    if not normalized:
        raise ValueError("Nom de script vide.")
    if normalized.startswith("scripts."):
        normalized = normalized.split(".", 1)[1]
    return normalized


def iter_scripts(selected: Iterable[str] | None) -> list[PlotScript]:
    if not selected:
        return list(PLOT_SCRIPTS)

    mapping = {script.key: script for script in PLOT_SCRIPTS}
    normalized = [_normalize_key(name) for name in selected]
    scripts: list[PlotScript] = []
    missing: list[str] = []
    for key in normalized:
        script = mapping.get(key)
        if script is None:
            missing.append(key)
        else:
            scripts.append(script)

    if missing:
        available = "\n  - ".join(("",) + tuple(script.key for script in PLOT_SCRIPTS))
        missing_list = "\n  - ".join(("",) + tuple(missing))
        raise SystemExit(
            "\n".join(
                [
                    "Les scripts suivants ne sont pas reconnus :",
                    missing_list,
                    "",
                    "Scripts disponibles :",
                    available,
                ]
            )
        )

    return scripts


def run_script(script: PlotScript) -> bool:
    if not script.path.exists():
        print(f"✘ Script introuvable : {script.display_path()}")
        return False

    cmd = [sys.executable, str(script.path)]
    print(f"\n=== {script.key} ({script.display_path()}) ===")
    result = subprocess.run(cmd, check=False)

    if result.returncode == 0:
        print(f"✔ {script.key} terminé avec succès.")
        return True

    print(f"✘ {script.key} a échoué (code {result.returncode}).")
    return False


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(description="Lance tous les scripts de tracé météorologiques.")
    parser.add_argument(
        "--only",
        nargs="*",
        help="Limiter l'exécution à certains scripts (ex: plot_temperature).",
    )
    args = parser.parse_args(argv)

    scripts = iter_scripts(args.only)
    print(f"Exécution de {len(scripts)} script(s) de tracé…")

    success_count = 0
    for script in scripts:
        if run_script(script):
            success_count += 1

    print()
    print(f"Scripts réussis : {success_count}/{len(scripts)}")
    failed = len(scripts) - success_count
    if failed:
        print(f"⚠ {failed} script(s) ont échoué. Consultez les messages ci-dessus.")
        return 1

    print("✔ Tous les scripts ont été exécutés avec succès.")
    return 0


if __name__ == "__main__":  # pragma: no cover
    raise SystemExit(main())
