#!/usr/bin/env python3
"""
Script pour analyser l'historique Git des fichiers livre.org, intrigues.org et personnages.org
Génère un JSON détaillé des changements et un CSV récapitulatif
"""

import subprocess
import json
import csv
from datetime import datetime
from collections import defaultdict
import os
import sys
from typing import Dict, List, Tuple, Any
import argparse

def run_git_command(args: List[str]) -> str:
    """Exécute une commande git et retourne la sortie"""
    try:
        result = subprocess.run(
            ['git'] + args,
            capture_output=True,
            text=True,
            check=True
        )
        return result.stdout
    except subprocess.CalledProcessError as e:
        print(f"Erreur lors de l'exécution de la commande git: {e}", file=sys.stderr)
        sys.exit(1)

def get_file_history(filename: str) -> List[Dict[str, str]]:
    """Récupère l'historique Git d'un fichier"""
    cmd = ['log', '--follow', '--pretty=format:%H|%an|%ae|%ad|%s', '--date=iso-strict', '--', filename]
    output = run_git_command(cmd)
    
    history = []
    for line in output.strip().split('\n'):
        if not line:
            continue
        parts = line.split('|', 4)
        if len(parts) >= 5:
            history.append({
                'hash': parts[0],
                'author': parts[1],
                'email': parts[2],
                'date': parts[3],
                'message': parts[4]
            })
    
    return history

def get_file_content_at_commit(hash: str, filename: str) -> str:
    """Récupère le contenu d'un fichier à un commit donné"""
    try:
        result = subprocess.run(
            ['git', 'show', f'{hash}:{filename}'],
            capture_output=True,
            text=True,
            check=True
        )
        return result.stdout
    except subprocess.CalledProcessError:
        return ""

def parse_diff(diff_output: str) -> Dict[str, Any]:
    """Parse la sortie de git diff pour extraire les changements"""
    changes = {
        'added_lines': [],
        'removed_lines': [],
        'modified_sections': []
    }
    
    current_file = None
    current_block_start = None
    in_header = True
    
    for line in diff_output.split('\n'):
        # Sauter la première ligne de diff
        if line.startswith('diff --git'):
            in_header = True
            continue
        
        if line.startswith('+++'):
            in_header = False
            continue
            
        if line.startswith('---'):
            continue
        
        if in_header:
            continue
        
        # Nouveau bloc de changement
        if line.startswith('@@'):
            parts = line.split('@@')
            if len(parts) >= 3:
                # Extraire les numéros de ligne
                line_info = parts[1].strip()
                current_block_start = line_info
            continue
        
        # Ligne ajoutée
        if line.startswith('+') and not line.startswith('+++'):
            clean_line = line[1:]  # Enlever le + au début
            line_num = current_block_start if current_block_start else 'unknown'
            changes['added_lines'].append({
                'line': clean_line,
                'context': current_block_start
            })
        
        # Ligne supprimée
        elif line.startswith('-') and not line.startswith('---'):
            clean_line = line[1:]  # Enlever le - au début
            changes['removed_lines'].append({
                'line': clean_line,
                'context': current_block_start
            })
        
        # Ligne inchangée (context)
        elif line.startswith(' '):
            continue
    
    return changes

def get_changes_between_commits(commit_hash: str, prev_hash: str, filename: str) -> Dict[str, Any]:
    """Compare deux commits et retourne les changements"""
    # Récupérer le diff
    diff_cmd = ['diff', prev_hash, commit_hash, '--', filename]
    diff_output = run_git_command(diff_cmd)
    
    if not diff_output.strip():
        return None
    
    # Parser le diff
    changes = parse_diff(diff_output)
    
    # Ajouter des statistiques
    changes['stats'] = {
        'added_count': len(changes['added_lines']),
        'removed_count': len(changes['removed_lines']),
        'net_change': len(changes['added_lines']) - len(changes['removed_lines'])
    }
    
    return changes

def format_line_for_csv(line_content: str, max_length: int = 100) -> str:
    """Formate une ligne pour le CSV en limitant sa longueur"""
    if len(line_content) > max_length:
        return line_content[:max_length] + '...'
    return line_content

def analyze_git_history(folder_path: str):
    """Fonction principale pour analyser l'historique Git dans un dossier donné"""
    
    files_to_track = ['livre.org', 'intrigues.org', 'personnages.org']

    if not os.path.isdir(folder_path):
        print(f"Erreur: le dossier {folder_path} n'existe pas.", file=sys.stderr)
        sys.exit(1)

    # Sauver le cwd courant et se placer dans le bon dossier pour les commandes git
    original_cwd = os.getcwd()
    os.chdir(folder_path)
    try:
        # Vérifier que nous sommes dans un dépôt Git
        try:
            run_git_command(['rev-parse', '--git-dir'])
        except subprocess.CalledProcessError:
            print("Erreur: Ce répertoire n'est pas un dépôt Git", file=sys.stderr)
            sys.exit(1)

        all_changes = []

        for filename in files_to_track:
            if not os.path.exists(filename):
                print(f"Attention: Le fichier {filename} n'existe pas dans {folder_path}, il sera ignoré", file=sys.stderr)
                continue
            print(f"Analyse de {filename}...")
            history = get_file_history(filename)
            if not history:
                print(f"Aucun historique trouvé pour {filename}")
                continue
            for i, commit in enumerate(history):
                commit_hash = commit['hash']
                commit_date = commit['date']
                commit_author = commit['author']
                commit_message = commit['message']
                if i < len(history) - 1:
                    prev_hash = history[i + 1]['hash']
                    changes = get_changes_between_commits(commit_hash, prev_hash, filename)
                else:
                    content = get_file_content_at_commit(commit_hash, filename)
                    changes = {
                        'added_lines': [{'line': line, 'context': 'initial'} for line in content.split('\n') if line.strip()],
                        'removed_lines': [],
                        'modified_sections': [],
                        'stats': {
                            'added_count': len(content.split('\n')),
                            'removed_count': 0,
                            'net_change': len(content.split('\n'))
                        }
                    }
                if changes and changes['stats']['added_count'] + changes['stats']['removed_count'] > 0:
                    change_entry = {
                        'filename': filename,
                        'date': commit_date,
                        'commit_hash': commit_hash,
                        'author': commit_author,
                        'message': commit_message,
                        'changes': changes
                    }
                    all_changes.append(change_entry)
        all_changes.sort(key=lambda x: x['date'])
        # Générer le JSON
        print("Génération du fichier JSON...")
        with open('git_changes_history.json', 'w', encoding='utf-8') as f:
            json.dump(all_changes, f, ensure_ascii=False, indent=2)
        # Générer le CSV
        print("Génération du fichier CSV...")
        with open('git_changes_history.csv', 'w', newline='', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerow([
                'Date', 'Fichier', 'Commit Hash', 'Auteur', 'Message',
                'Lignes Ajoutées', 'Lignes Supprimées', 'Changement Net',
                'Détails Ajouts', 'Détails Suppressions'
            ])
            for change in all_changes:
                added_details = "; ".join([
                    format_line_for_csv(item['line'], 50)
                    for item in change['changes']['added_lines'][:10]
                ])
                if len(change['changes']['added_lines']) > 10:
                    added_details += f"... (+{len(change['changes']['added_lines']) - 10} autres)"
                removed_details = "; ".join([
                    format_line_for_csv(item['line'], 50)
                    for item in change['changes']['removed_lines'][:10]
                ])
                if len(change['changes']['removed_lines']) > 10:
                    removed_details += f"... (+{len(change['changes']['removed_lines']) - 10} autres)"
                stats = change['changes']['stats']
                writer.writerow([
                    change['date'],
                    change['filename'],
                    change['commit_hash'],
                    change['author'],
                    change['message'],
                    stats['added_count'],
                    stats['removed_count'],
                    stats['net_change'],
                    added_details,
                    removed_details
                ])
        print(f"Analyse terminée: {len(all_changes)} changements enregistrés")
        print("Fichiers générés: git_changes_history.json et git_changes_history.csv")
    finally:
        os.chdir(original_cwd)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="Analyse historique git pour livre.org, intrigues.org et personnages.org")
    parser.add_argument('folder', nargs='?', default='.', help="Chemin du dossier contenant les fichiers org (défaut: dossier courant)")
    args = parser.parse_args()
    analyze_git_history(args.folder)
