#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
═══════════════════════════════════════════════════════════════════════════════
 ARGUS — Validateur de fichiers YAML
 Utilisable en standalone ou comme hook pre-commit
═══════════════════════════════════════════════════════════════════════════════

Usage:
  python validate_yaml.py                    # Valide tous les YAML du projet
  python validate_yaml.py --staged           # Valide uniquement les fichiers staged (pour pre-commit)
  python validate_yaml.py --quiet            # Mode silencieux (exit code uniquement)
  python validate_yaml.py --file path.yaml   # Valide un fichier spécifique

Exit codes:
  0 = Tous les fichiers sont valides
  1 = Au moins un fichier contient une erreur
  2 = Erreur d'exécution du script
"""

import yaml
import os
import sys
import argparse
import subprocess
from pathlib import Path
from typing import List, Tuple, Optional


class YAMLValidator:
    """Validateur de fichiers YAML avec support pour hooks Git."""
    
    def __init__(self, base_dir: str, verbose: bool = True):
        self.base_dir = Path(base_dir)
        self.verbose = verbose
        self.errors: List[Tuple[str, str]] = []
        self.valid_count = 0
        
    def log(self, message: str, force: bool = False):
        """Affiche un message si le mode verbose est activé."""
        if self.verbose or force:
            print(message)
    
    def validate_file(self, filepath: Path) -> bool:
        """
        Valide un fichier YAML individuel.
        
        Returns:
            True si le fichier est valide, False sinon.
        """
        try:
            with open(filepath, 'r', encoding='utf-8') as f:
                content = f.read()
                
            # Un fichier vide est YAML-valide (retourne None) — on le signale
            # mais on ne le considère pas comme une erreur
            if not content.strip():
                self.log(f"  ⚠ EMPTY: {filepath.relative_to(self.base_dir)}")
                return True
            
            # safe_load détecte les erreurs de syntaxe, de structure et d'encodage
            yaml.safe_load(content)
            
            self.valid_count += 1
            self.log(f"  ✓ OK: {filepath.relative_to(self.base_dir)}")
            return True
            
        except yaml.YAMLError as e:
            error_msg = self._format_yaml_error(e)
            self.errors.append((str(filepath), error_msg))
            self.log(f"  ✗ ERROR: {filepath.relative_to(self.base_dir)}")
            return False
            
        except UnicodeDecodeError as e:
            error_msg = f"Encoding error: {e}"
            self.errors.append((str(filepath), error_msg))
            self.log(f"  ✗ ENCODING: {filepath.relative_to(self.base_dir)}")
            return False
            
        except Exception as e:
            error_msg = f"Unexpected error: {e}"
            self.errors.append((str(filepath), error_msg))
            self.log(f"  ✗ ERROR: {filepath.relative_to(self.base_dir)}")
            return False
    
    def _format_yaml_error(self, error: yaml.YAMLError) -> str:
        """Formate une erreur YAML de manière lisible."""
        if hasattr(error, 'problem_mark'):
            mark = error.problem_mark
            return f"Line {mark.line + 1}, Column {mark.column + 1}: {error.problem}"
        return str(error)
    
    def validate_directory(self, directory: Optional[Path] = None) -> bool:
        """
        Valide tous les fichiers YAML dans un répertoire récursivement.
        
        Returns:
            True si tous les fichiers sont valides, False sinon.
        """
        target_dir = directory or self.base_dir
        self.log(f"\n📁 Scanning {target_dir.relative_to(self.base_dir) if directory else 'project root'}...\n")
        
        yaml_files = list(target_dir.rglob("*.yaml")) + list(target_dir.rglob("*.yml"))
        
        # Exclure le dossier .git qui contient des fichiers YAML internes à Git
        yaml_files = [f for f in yaml_files if '.git' not in str(f)]
        
        if not yaml_files:
            self.log("  No YAML files found.")
            return True
        
        for filepath in sorted(yaml_files):
            self.validate_file(filepath)
        
        return len(self.errors) == 0
    
    def validate_staged_files(self) -> bool:
        """
        Valide uniquement les fichiers YAML qui sont staged pour le commit.
        Utilisé par le hook pre-commit.
        
        Returns:
            True si tous les fichiers staged sont valides, False sinon.
        """
        try:
            # --cached = fichiers dans l'index (staged), --diff-filter=ACM = ajoutés,
            # copiés ou modifiés uniquement (pas les suppressions)
            result = subprocess.run(
                ['git', 'diff', '--cached', '--name-only', '--diff-filter=ACM'],
                capture_output=True,
                text=True,
                cwd=self.base_dir
            )
            
            if result.returncode != 0:
                self.log("⚠ Could not get staged files from Git", force=True)
                return False
            
            staged_files = result.stdout.strip().split('\n')
            yaml_files = [f for f in staged_files if f.endswith(('.yaml', '.yml')) and f]
            
            if not yaml_files:
                self.log("ℹ No YAML files staged for commit.")
                return True
            
            self.log(f"\n🔍 Validating {len(yaml_files)} staged YAML file(s)...\n")
            
            for relative_path in yaml_files:
                filepath = self.base_dir / relative_path
                if filepath.exists():
                    self.validate_file(filepath)
                else:
                    self.log(f"  ⚠ MISSING: {relative_path}")
            
            return len(self.errors) == 0
            
        except FileNotFoundError:
            self.log("⚠ Git not found. Running full validation instead.", force=True)
            return self.validate_directory()
    
    def print_summary(self):
        """Affiche le résumé de la validation."""
        total = self.valid_count + len(self.errors)
        
        self.log(f"\n{'═' * 60}")
        self.log(f"📊 VALIDATION SUMMARY")
        self.log(f"{'═' * 60}")
        self.log(f"  Total files checked: {total}")
        self.log(f"  Valid:  {self.valid_count} ✓")
        self.log(f"  Errors: {len(self.errors)} {'✗' if self.errors else '✓'}")
        
        if self.errors:
            self.log(f"\n{'─' * 60}")
            self.log("❌ ERROR DETAILS:\n")
            for filepath, error in self.errors:
                rel_path = Path(filepath).relative_to(self.base_dir)
                self.log(f"  📄 {rel_path}")
                self.log(f"     └─ {error}\n")
            self.log(f"{'─' * 60}")
            self.log("💡 Fix the errors above before committing.", force=True)
        else:
            self.log(f"\n✅ All YAML files are valid!", force=True)
        
        self.log(f"{'═' * 60}\n")


def find_project_root() -> Path:
    """
    Trouve la racine du projet (le dossier contenant argus.persona.yaml et memory/).
    """
    current = Path(__file__).resolve().parent
    
    while current != current.parent:
        if (current / 'argus.persona.yaml').exists() and (current / 'memory').is_dir():
            return current
        current = current.parent
    
    # Fallback: répertoire parent du script
    return Path(__file__).resolve().parent.parent


def main():
    parser = argparse.ArgumentParser(
        description='Validate YAML files for Argus memory system',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__
    )
    
    parser.add_argument(
        '--staged', '-s',
        action='store_true',
        help='Only validate staged files (for pre-commit hook)'
    )
    
    parser.add_argument(
        '--quiet', '-q',
        action='store_true',
        help='Quiet mode - only show errors and exit code'
    )
    
    parser.add_argument(
        '--file', '-f',
        type=str,
        help='Validate a specific file'
    )
    
    parser.add_argument(
        '--dir', '-d',
        type=str,
        help='Validate files in a specific directory'
    )
    
    args = parser.parse_args()
    
    try:
        project_root = find_project_root()
        validator = YAMLValidator(str(project_root), verbose=not args.quiet)
        
        if not args.quiet:
            print(f"\n{'═' * 60}")
            print(f"🔎 ARGUS YAML VALIDATOR")
            print(f"{'═' * 60}")
            print(f"  Project root: {project_root}")
        
        # Mode de validation selon les arguments
        if args.file:
            filepath = Path(args.file)
            if not filepath.is_absolute():
                filepath = project_root / filepath
            success = validator.validate_file(filepath)
            
        elif args.staged:
            success = validator.validate_staged_files()
            
        elif args.dir:
            target_dir = Path(args.dir)
            if not target_dir.is_absolute():
                target_dir = project_root / target_dir
            success = validator.validate_directory(target_dir)
            
        else:
            success = validator.validate_directory()
        
        validator.print_summary()
        
        return 0 if success else 1
        
    except Exception as e:
        print(f"\n❌ Script error: {e}", file=sys.stderr)
        return 2


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

