#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
═══════════════════════════════════════════════════════════════════════════════
 ARGUS — Synchronisateur d'enums entre schemas et données YAML
 Détecte et corrige les désynchronisations entre les enums JSON Schema
 et les valeurs réellement utilisées dans les fichiers YAML
═══════════════════════════════════════════════════════════════════════════════

Usage:
  python sync_schemas.py              # Mode détection (affiche les écarts)
  python sync_schemas.py --fix        # Mode correction (met à jour les schemas)
  python sync_schemas.py --quiet      # Mode silencieux (exit code uniquement)
  python sync_schemas.py --json       # Sortie JSON (pour intégration CI)

Exit codes:
  0 = Enums synchronisés (aucun écart)
  1 = Écarts détectés (corrections nécessaires)
  2 = Erreur d'exécution du script

Implémente la recommandation R005 de l'analyse ANL-20260221-001.
"""

import json
import sys
import argparse
import os
from pathlib import Path
from typing import Any
from dataclasses import dataclass, field
from datetime import datetime

try:
    import yaml
except ImportError:
    print("Missing dependency. Install with: pip install pyyaml")
    sys.exit(2)


# ═══════════════════════════════════════════════════════════════════════════════
#  CONFIGURATION
# ═══════════════════════════════════════════════════════════════════════════════

ROOT = Path(__file__).resolve().parent.parent
SCHEMAS_DIR = ROOT / "schemas"
MEMORY_DIR = ROOT / "memory"


# ═══════════════════════════════════════════════════════════════════════════════
#  DATA CLASSES
# ═══════════════════════════════════════════════════════════════════════════════

@dataclass
class EnumDelta:
    """Représente un écart entre une enum schema et les données réelles."""
    group: str              # Nom du groupe d'enums (ex: "defect_pattern_categories")
    schema_file: str        # Fichier schema concerné
    json_path: str          # Chemin JSON dans le schema
    missing_in_schema: list[str]   # Valeurs dans les données mais pas dans le schema
    missing_in_data: list[str]     # Valeurs dans le schema mais pas dans les données
    current_enum: list[str]        # Enum actuelle dans le schema
    data_values: list[str]         # Valeurs trouvées dans les données
    severity: str = "medium"       # low, medium, high

    @property
    def is_desync(self) -> bool:
        return len(self.missing_in_schema) > 0

    def to_dict(self) -> dict:
        return {
            "group": self.group,
            "schema_file": self.schema_file,
            "json_path": self.json_path,
            "missing_in_schema": self.missing_in_schema,
            "missing_in_data": self.missing_in_data,
            "current_enum": self.current_enum,
            "data_values": self.data_values,
            "severity": self.severity,
            "desync": self.is_desync,
        }


@dataclass
class CrossSchemaDesync:
    """Représente une incohérence entre deux schemas pour le même concept."""
    group: str
    source_schema: str          # Schema de référence
    source_path: str
    target_schema: str          # Schema qui devrait être synchronisé
    target_path: str
    missing_in_target: list[str]
    extra_in_target: list[str]
    severity: str = "high"

    @property
    def is_desync(self) -> bool:
        return len(self.missing_in_target) > 0 or len(self.extra_in_target) > 0

    def to_dict(self) -> dict:
        return {
            "group": self.group,
            "source_schema": self.source_schema,
            "source_path": self.source_path,
            "target_schema": self.target_schema,
            "target_path": self.target_path,
            "missing_in_target": self.missing_in_target,
            "extra_in_target": self.extra_in_target,
            "severity": self.severity,
            "desync": self.is_desync,
        }


@dataclass
class SyncReport:
    """Rapport complet de synchronisation des enums."""
    timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
    enum_deltas: list[EnumDelta] = field(default_factory=list)
    cross_schema_desyncs: list[CrossSchemaDesync] = field(default_factory=list)
    errors: list[str] = field(default_factory=list)
    fixes_applied: list[str] = field(default_factory=list)

    @property
    def has_desyncs(self) -> bool:
        return (
            any(d.is_desync for d in self.enum_deltas)
            or any(d.is_desync for d in self.cross_schema_desyncs)
        )

    @property
    def desync_count(self) -> int:
        return (
            sum(1 for d in self.enum_deltas if d.is_desync)
            + sum(1 for d in self.cross_schema_desyncs if d.is_desync)
        )

    def to_dict(self) -> dict:
        return {
            "timestamp": self.timestamp,
            "summary": {
                "total_checks": len(self.enum_deltas) + len(self.cross_schema_desyncs),
                "desyncs": self.desync_count,
                "errors": len(self.errors),
                "fixes_applied": len(self.fixes_applied),
                "status": "DESYNC" if self.has_desyncs else "SYNC",
            },
            "enum_deltas": [d.to_dict() for d in self.enum_deltas if d.is_desync],
            "cross_schema_desyncs": [d.to_dict() for d in self.cross_schema_desyncs if d.is_desync],
            "fixes_applied": self.fixes_applied,
            "errors": self.errors,
        }


# ═══════════════════════════════════════════════════════════════════════════════
#  ENUM GROUP DEFINITIONS
#  Chaque groupe relie un ensemble de valeurs dynamiques (catégories, types)
#  à un ou plusieurs emplacements dans les schemas JSON.
#  Quand les données YAML évoluent (nouveau pattern, nouvelle catégorie),
#  ce script détecte les enums schema qui doivent être mises à jour.
# ═══════════════════════════════════════════════════════════════════════════════

class EnumGroup:
    """Définition d'un groupe d'enums liées à travers le projet."""

    def __init__(self, name: str, description: str, severity: str = "medium"):
        self.name = name
        self.description = description
        self.severity = severity
        self.schema_locations: list[dict] = []

    def add_location(self, schema_file: str, json_path: list[str]) -> "EnumGroup":
        """Ajoute un emplacement de schema pour ce groupe."""
        self.schema_locations.append({
            "schema_file": schema_file,
            "json_path": json_path,
        })
        return self


def build_enum_groups() -> list[EnumGroup]:
    """Construit la liste de tous les groupes d'enums dynamiques."""
    groups = []

    # ── Catégories de patterns de défauts ──────────────────────────────────
    g = EnumGroup(
        "defect_pattern_categories",
        "Catégories de patterns de défauts (null_safety, async, api...)",
        severity="high",
    )
    g.add_location(
        "defect_patterns.schema.json",
        ["properties", "patterns", "items", "properties", "category", "enum"],
    )
    g.add_location(
        "incident.schema.json",
        ["properties", "argus_improvements", "properties",
         "new_patterns_to_learn", "items", "properties", "category", "enum"],
    )
    groups.append(g)

    # ── Catégories d'heuristiques ──────────────────────────────────────────
    g = EnumGroup(
        "heuristic_categories",
        "Catégories d'heuristiques de test (selection, coverage, api...)",
        severity="high",
    )
    g.add_location(
        "testing_heuristics.schema.json",
        ["definitions", "heuristic_list", "items", "properties", "category", "enum"],
    )
    g.add_location(
        "incident.schema.json",
        ["properties", "argus_improvements", "properties",
         "heuristics_to_add", "items", "properties", "category", "enum"],
    )
    groups.append(g)

    # ── Catégories de findings d'analyse ───────────────────────────────────
    g = EnumGroup(
        "finding_categories",
        "Catégories de findings d'analyse (regression, bug, security...)",
        severity="medium",
    )
    g.add_location(
        "analysis.schema.json",
        ["properties", "findings", "items", "properties", "category", "enum"],
    )
    groups.append(g)

    # ── Catégories d'incidents ─────────────────────────────────────────────
    g = EnumGroup(
        "incident_categories",
        "Catégories d'incidents (regression, bug, security...)",
        severity="medium",
    )
    g.add_location(
        "incident.schema.json",
        ["properties", "category", "enum"],
    )
    groups.append(g)

    # ── Facteurs contributifs (root cause) ─────────────────────────────────
    g = EnumGroup(
        "root_cause_factor_categories",
        "Catégories de facteurs contributifs (code, config, infra...)",
        severity="medium",
    )
    g.add_location(
        "incident.schema.json",
        ["properties", "root_cause", "properties", "contributing_factors",
         "items", "properties", "category", "enum"],
    )
    groups.append(g)

    # ── Runners CI/CD ──────────────────────────────────────────────────────
    g = EnumGroup(
        "ci_runners",
        "Plateformes CI/CD (github-actions, gitlab-ci, jenkins...)",
        severity="low",
    )
    g.add_location(
        "execution.schema.json",
        ["properties", "environment", "properties", "runner", "enum"],
    )
    groups.append(g)

    # ── Types d'architecture ───────────────────────────────────────────────
    g = EnumGroup(
        "architecture_types",
        "Types de structure projet (monolith, microservices...)",
        severity="low",
    )
    g.add_location(
        "project.schema.json",
        ["properties", "architecture", "properties", "structure",
         "properties", "type", "enum"],
    )
    groups.append(g)

    # ── Domaines de contexte applicatif ────────────────────────────────────
    g = EnumGroup(
        "context_profile_domains",
        "Domaines de contexte applicatif (fintech, ecommerce, health...)",
        severity="medium",
    )
    g.add_location(
        "context_profiles.schema.json",
        ["properties", "profiles", "items", "properties", "domain", "enum"],
    )
    groups.append(g)

    return groups


# ═══════════════════════════════════════════════════════════════════════════════
#  DATA EXTRACTION — Valeurs utilisées dans les fichiers YAML
# ═══════════════════════════════════════════════════════════════════════════════

def extract_values_from_yaml(
    yaml_path: Path, field_path: list[str]
) -> set[str]:
    """Extrait les valeurs uniques d'un champ dans un fichier YAML.

    field_path supporte des wildcards pour traverser les structures dynamiques :
      '*'  → itère sur les éléments d'une liste OU les valeurs d'un dict
      '**' → traverse toutes les valeurs au niveau courant (drill-down)
    
    Exemple: ["patterns", "*", "category"] → patterns[i].category
    """
    if not yaml_path.exists():
        return set()

    try:
        with open(yaml_path, "r", encoding="utf-8") as f:
            data = yaml.safe_load(f)
    except (yaml.YAMLError, OSError):
        return set()

    if data is None:
        return set()

    return _extract_recursive(data, field_path)


def _extract_recursive(data: Any, path: list[str]) -> set[str]:
    """Extraction récursive suivant un chemin avec wildcards."""
    if not path:
        if isinstance(data, str):
            return {data}
        return set()

    head, *tail = path

    if head == "*":
        values = set()
        if isinstance(data, list):
            for item in data:
                values |= _extract_recursive(item, tail)
        elif isinstance(data, dict):
            for v in data.values():
                values |= _extract_recursive(v, tail)
        return values

    if head == "**":
        # Traverse all values at current level, delegate list iteration to '*'
        values = set()
        if isinstance(data, dict):
            for val in data.values():
                values |= _extract_recursive(val, tail)
        elif isinstance(data, list):
            for item in data:
                values |= _extract_recursive(item, tail)
        return values

    if isinstance(data, dict) and head in data:
        return _extract_recursive(data[head], tail)

    return set()


def extract_heuristic_section_names(yaml_path: Path) -> set[str]:
    """Extrait les noms de sections d'heuristiques (clés de premier niveau
    qui contiennent des listes d'heuristiques)."""
    if not yaml_path.exists():
        return set()

    try:
        with open(yaml_path, "r", encoding="utf-8") as f:
            data = yaml.safe_load(f)
    except (yaml.YAMLError, OSError):
        return set()

    if not isinstance(data, dict):
        return set()

    skip_keys = {"version", "last_updated", "metadata"}
    sections = set()
    for key, val in data.items():
        if key in skip_keys:
            continue
        if isinstance(val, list) and len(val) > 0:
            sections.add(key)
    return sections


def collect_data_values(group_name: str, base_dir: Path) -> set[str]:
    """Collecte les valeurs réelles depuis les fichiers YAML pour un groupe donné.
    
    Pour chaque groupe d'enums, définit quels fichiers YAML scanner et quel
    chemin extraire. Les fichiers préfixés '_' (templates) sont ignorés.
    """
    memory = base_dir / "memory"

    extractors: dict[str, tuple[list[Path], list[str]]] = {
        "defect_pattern_categories": (
            [memory / "semantic" / "defect_patterns.yaml"],
            ["patterns", "*", "category"],
        ),
        "finding_categories": (
            list((memory / "episodic" / "analyses").glob("*.yaml"))
            if (memory / "episodic" / "analyses").exists() else [],
            ["findings", "*", "category"],
        ),
        "incident_categories": (
            list((memory / "episodic" / "incidents").glob("*.yaml"))
            if (memory / "episodic" / "incidents").exists() else [],
            ["category"],
        ),
        "root_cause_factor_categories": (
            list((memory / "episodic" / "incidents").glob("*.yaml"))
            if (memory / "episodic" / "incidents").exists() else [],
            ["root_cause", "contributing_factors", "*", "category"],
        ),
        "ci_runners": (
            list((memory / "episodic" / "executions").glob("*.yaml"))
            if (memory / "episodic" / "executions").exists() else [],
            ["environment", "runner"],
        ),
        "architecture_types": (
            list((memory / "projects").glob("*.yaml"))
            if (memory / "projects").exists() else [],
            ["architecture", "structure", "type"],
        ),
    }

    if group_name == "heuristic_categories":
        heuristics_file = memory / "semantic" / "testing_heuristics.yaml"
        # Combiner les noms de sections ET les category des heuristiques
        sections = extract_heuristic_section_names(heuristics_file)
        categories = extract_values_from_yaml(
            heuristics_file, ["**", "*", "category"]
        )
        return sections | categories

    if group_name not in extractors:
        return set()

    files, field_path = extractors[group_name]
    values = set()
    for f in files:
        if f.name.startswith("_"):
            continue
        values |= extract_values_from_yaml(f, field_path)

    return values


# ═══════════════════════════════════════════════════════════════════════════════
#  SCHEMA OPERATIONS — Lecture/écriture des enums dans les schemas JSON
# ═══════════════════════════════════════════════════════════════════════════════

def get_enum_at_path(schema: dict, json_path: list[str]) -> list[str] | None:
    """Récupère les valeurs enum à un chemin donné dans un schema."""
    current = schema
    for key in json_path:
        if isinstance(current, dict) and key in current:
            current = current[key]
        else:
            return None

    if isinstance(current, list):
        return [v for v in current if isinstance(v, str)]
    return None


def set_enum_at_path(schema: dict, json_path: list[str], values: list[str]) -> bool:
    """Met à jour les valeurs enum à un chemin donné dans un schema.
    
    Préserve les valeurs null existantes dans l'enum (certains schemas
    utilisent null comme valeur valide pour les champs optionnels).
    Retourne True si modification effectuée.
    """
    current = schema
    for key in json_path[:-1]:
        if isinstance(current, dict) and key in current:
            current = current[key]
        else:
            return False

    last_key = json_path[-1]
    if isinstance(current, dict) and last_key in current:
        old = current[last_key]
        # Filtrer les valeurs null qui font partie de certaines enums
        nulls = [v for v in old if v is None]
        current[last_key] = values + nulls
        return True
    return False


def load_schema(schema_file: str, schemas_dir: Path | None = None) -> dict:
    """Charge un fichier JSON Schema."""
    if schemas_dir is None:
        schemas_dir = SCHEMAS_DIR
    path = schemas_dir / schema_file
    with open(path, "r", encoding="utf-8") as f:
        return json.load(f)


def save_schema(schema_file: str, schema: dict, schemas_dir: Path | None = None) -> None:
    """Sauvegarde un fichier JSON Schema avec formatage cohérent."""
    if schemas_dir is None:
        schemas_dir = SCHEMAS_DIR
    path = schemas_dir / schema_file
    with open(path, "w", encoding="utf-8", newline="\n") as f:
        json.dump(schema, f, indent=2, ensure_ascii=False)
        f.write("\n")


# ═══════════════════════════════════════════════════════════════════════════════
#  SYNCHRONIZER
# ═══════════════════════════════════════════════════════════════════════════════

class SchemaSynchronizer:
    """Synchronise les enums dynamiques entre schemas et données YAML."""

    def __init__(self, base_dir: Path | None = None, schemas_dir: Path | None = None,
                 verbose: bool = True):
        self.base_dir = base_dir or ROOT
        self.schemas_dir = schemas_dir or SCHEMAS_DIR
        self.verbose = verbose
        self.report = SyncReport()
        self.groups = build_enum_groups()

    def check(self) -> SyncReport:
        """Vérifie la synchronisation de toutes les enums dynamiques."""
        self._check_data_vs_schemas()
        self._check_cross_schema_consistency()
        return self.report

    def fix(self) -> SyncReport:
        """Corrige les désynchronisations détectées."""
        self.check()

        if not self.report.has_desyncs:
            return self.report

        self._apply_fixes()
        return self.report

    # ── Vérification données → schemas ─────────────────────────────────────

    def _check_data_vs_schemas(self) -> None:
        """Compare les valeurs dans les données avec les enums des schemas."""
        for group in self.groups:
            data_values = collect_data_values(group.name, self.base_dir)

            for loc in group.schema_locations:
                try:
                    schema = load_schema(loc["schema_file"], self.schemas_dir)
                except (FileNotFoundError, json.JSONDecodeError) as e:
                    self.report.errors.append(
                        f"Cannot load {loc['schema_file']}: {e}"
                    )
                    continue

                current_enum = get_enum_at_path(schema, loc["json_path"])
                if current_enum is None:
                    self.report.errors.append(
                        f"Enum not found at {'.'.join(loc['json_path'])} "
                        f"in {loc['schema_file']}"
                    )
                    continue

                current_set = set(current_enum)
                data_set = data_values

                missing_in_schema = sorted(data_set - current_set)
                missing_in_data = sorted(current_set - data_set)

                delta = EnumDelta(
                    group=group.name,
                    schema_file=loc["schema_file"],
                    json_path=".".join(loc["json_path"]),
                    missing_in_schema=missing_in_schema,
                    missing_in_data=missing_in_data,
                    current_enum=current_enum,
                    data_values=sorted(data_set),
                    severity=group.severity,
                )
                self.report.enum_deltas.append(delta)

    # ── Vérification cohérence inter-schemas ───────────────────────────────

    def _check_cross_schema_consistency(self) -> None:
        """Vérifie que les schemas partagent les mêmes enums pour un concept."""
        for group in self.groups:
            if len(group.schema_locations) < 2:
                continue

            # Le premier emplacement est la source de vérité
            source = group.schema_locations[0]
            try:
                source_schema = load_schema(source["schema_file"], self.schemas_dir)
            except (FileNotFoundError, json.JSONDecodeError):
                continue

            source_enum = get_enum_at_path(source_schema, source["json_path"])
            if source_enum is None:
                continue

            source_set = set(source_enum)

            for target in group.schema_locations[1:]:
                try:
                    target_schema = load_schema(target["schema_file"], self.schemas_dir)
                except (FileNotFoundError, json.JSONDecodeError):
                    continue

                target_enum = get_enum_at_path(target_schema, target["json_path"])
                if target_enum is None:
                    continue

                target_set = set(target_enum)
                missing = sorted(source_set - target_set)
                extra = sorted(target_set - source_set)

                desync = CrossSchemaDesync(
                    group=group.name,
                    source_schema=source["schema_file"],
                    source_path=".".join(source["json_path"]),
                    target_schema=target["schema_file"],
                    target_path=".".join(target["json_path"]),
                    missing_in_target=missing,
                    extra_in_target=extra,
                    severity="high",
                )
                self.report.cross_schema_desyncs.append(desync)

    # ── Application des corrections ────────────────────────────────────────

    def _apply_fixes(self) -> None:
        """Applique les corrections : ajoute les valeurs manquantes aux schemas."""
        # Charger tous les schemas concernés
        schemas_cache: dict[str, dict] = {}
        modified_schemas: set[str] = set()

        # 1. Corriger les données manquantes dans les schemas
        for delta in self.report.enum_deltas:
            if not delta.is_desync:
                continue

            schema_file = delta.schema_file
            if schema_file not in schemas_cache:
                try:
                    schemas_cache[schema_file] = load_schema(
                        schema_file, self.schemas_dir
                    )
                except (FileNotFoundError, json.JSONDecodeError):
                    continue

            schema = schemas_cache[schema_file]
            json_path = delta.json_path.split(".")
            current_enum = get_enum_at_path(schema, json_path)

            if current_enum is None:
                continue

            new_enum = sorted(set(current_enum) | set(delta.missing_in_schema))
            if set_enum_at_path(schema, json_path, new_enum):
                modified_schemas.add(schema_file)
                self.report.fixes_applied.append(
                    f"{schema_file}: ajouté {delta.missing_in_schema} "
                    f"à {delta.json_path}"
                )

        # 2. Corriger les incohérences inter-schemas
        for desync in self.report.cross_schema_desyncs:
            if not desync.is_desync:
                continue

            # Charger le schema cible
            target_file = desync.target_schema
            if target_file not in schemas_cache:
                try:
                    schemas_cache[target_file] = load_schema(
                        target_file, self.schemas_dir
                    )
                except (FileNotFoundError, json.JSONDecodeError):
                    continue

            # Charger le schema source pour avoir la référence
            source_file = desync.source_schema
            if source_file not in schemas_cache:
                try:
                    schemas_cache[source_file] = load_schema(
                        source_file, self.schemas_dir
                    )
                except (FileNotFoundError, json.JSONDecodeError):
                    continue

            source_schema = schemas_cache[source_file]
            target_schema = schemas_cache[target_file]

            source_enum = get_enum_at_path(
                source_schema, desync.source_path.split(".")
            )
            target_path = desync.target_path.split(".")
            target_enum = get_enum_at_path(target_schema, target_path)

            if source_enum is None or target_enum is None:
                continue

            # Aligner la cible sur la source
            new_enum = sorted(set(source_enum))
            if set_enum_at_path(target_schema, target_path, new_enum):
                modified_schemas.add(target_file)
                if desync.missing_in_target:
                    self.report.fixes_applied.append(
                        f"{target_file}: synchronisé avec {source_file}, "
                        f"ajouté {desync.missing_in_target}"
                    )
                if desync.extra_in_target:
                    self.report.fixes_applied.append(
                        f"{target_file}: synchronisé avec {source_file}, "
                        f"retiré {desync.extra_in_target}"
                    )

        # 3. Sauvegarder les schemas modifiés
        for schema_file in modified_schemas:
            save_schema(schema_file, schemas_cache[schema_file], self.schemas_dir)


# ═══════════════════════════════════════════════════════════════════════════════
#  AFFICHAGE
# ═══════════════════════════════════════════════════════════════════════════════

def print_report(report: SyncReport) -> None:
    """Affiche le rapport de synchronisation."""
    print(f"{'='*70}")
    print(f"  ARGUS — Schema Enum Synchronization Report")
    print(f"  Schemas: {SCHEMAS_DIR}")
    print(f"{'='*70}\n")

    # Écarts données → schemas
    data_desyncs = [d for d in report.enum_deltas if d.is_desync]
    if data_desyncs:
        print("  📊 DONNÉES → SCHEMAS (nouvelles valeurs dans les données)\n")
        for delta in data_desyncs:
            print(f"  ⚠  [{delta.severity.upper()}] {delta.group}")
            print(f"     Schema: {delta.schema_file}")
            print(f"     Path:   {delta.json_path}")
            print(f"     Manquant dans schema: {delta.missing_in_schema}")
            if delta.missing_in_data:
                print(f"     Non utilisé (données): {delta.missing_in_data}")
            print()

    # Incohérences inter-schemas
    cross_desyncs = [d for d in report.cross_schema_desyncs if d.is_desync]
    if cross_desyncs:
        print("  🔗 INCOHÉRENCES INTER-SCHEMAS\n")
        for desync in cross_desyncs:
            print(f"  ⚠  [{desync.severity.upper()}] {desync.group}")
            print(f"     Source: {desync.source_schema} ({desync.source_path})")
            print(f"     Cible:  {desync.target_schema} ({desync.target_path})")
            if desync.missing_in_target:
                print(f"     Manquant dans cible: {desync.missing_in_target}")
            if desync.extra_in_target:
                print(f"     Extra dans cible:    {desync.extra_in_target}")
            print()

    # Corrections appliquées
    if report.fixes_applied:
        print("  ✅ CORRECTIONS APPLIQUÉES\n")
        for fix in report.fixes_applied:
            print(f"     • {fix}")
        print()

    # Erreurs
    if report.errors:
        print("  ❌ ERREURS\n")
        for err in report.errors:
            print(f"     • {err}")
        print()

    # Résumé
    status = report.to_dict()["summary"]
    print(f"{'='*70}")
    emoji = "✅" if status["status"] == "SYNC" else "⚠ "
    print(f"  {emoji} Status: {status['status']} | "
          f"Checks: {status['total_checks']} | "
          f"Desyncs: {status['desyncs']} | "
          f"Fixes: {status['fixes_applied']}")
    print(f"{'='*70}")


# ═══════════════════════════════════════════════════════════════════════════════
#  MAIN
# ═══════════════════════════════════════════════════════════════════════════════

def main() -> int:
    parser = argparse.ArgumentParser(
        description="Synchronise les enums dynamiques des JSON Schemas avec les données YAML"
    )
    parser.add_argument("--fix", action="store_true",
                        help="Appliquer les corrections automatiquement")
    parser.add_argument("--quiet", "-q", action="store_true",
                        help="Mode silencieux (exit code uniquement)")
    parser.add_argument("--json", action="store_true",
                        help="Sortie JSON")

    args = parser.parse_args()

    try:
        sync = SchemaSynchronizer(verbose=not args.quiet)

        if args.fix:
            report = sync.fix()
        else:
            report = sync.check()

        if args.json:
            print(json.dumps(report.to_dict(), indent=2, ensure_ascii=False))
        elif not args.quiet:
            print_report(report)

        return 1 if report.has_desyncs and not args.fix else 0

    except Exception as e:
        if not args.quiet:
            print(f"Error: {e}", file=sys.stderr)
        return 2


if __name__ == "__main__":
    sys.exit(main())
