#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
API pour traiter les requêtes de correction depuis la page web.
Ce script:
1. Fournit des endpoints pour appliquer des corrections au fichier livre.org
2. Permet d'ajouter des mots au dictionnaire personnalisé
3. Permet de marquer des erreurs comme "à ne pas traiter" dans le CSV

Modifications (2025-08-30):
- Amélioration de la recherche des chapitres dans le fichier livre.org
- Le motif de recherche est maintenant plus flexible pour gérer les chapitres avec des tags ou du texte supplémentaire
- Ajout de logs de débogage pour aider à diagnostiquer les problèmes de correspondance de chapitres
- Amélioration des fonctions load_spelling_errors et load_grammar_errors pour utiliser une correspondance flexible
"""

import os
import re
import json
import csv
from flask import Flask, request, jsonify, Blueprint

# Créer un Blueprint pour l'API
corrections_api = Blueprint('corrections_api', __name__)

# Chemin vers les fichiers
LIVRE_PATH = 'livre.org'
DICT_PATH = 'dictionnaire_personnalise.txt'
CSV_PATH = 'resume_erreurs.csv'

@corrections_api.route('/api/corrections', methods=['POST'])
def handle_corrections():
    """Endpoint pour traiter les requêtes de correction."""
    data = request.json
    
    if not data or 'action' not in data:
        return jsonify({'success': False, 'message': 'Action non spécifiée'}), 400
    
    action = data['action']
    
    if action == 'apply_correction':
        return apply_correction(data)
    elif action == 'ignore_error':
        return ignore_error(data)
    elif action == 'add_to_dictionary':
        return add_to_dictionary(data)
    else:
        return jsonify({'success': False, 'message': 'Action non reconnue'}), 400

def apply_correction(data):
    """Applique une correction au fichier livre.org."""
    # Vérifier les paramètres requis
    required_params = ['error_type', 'chapter', 'error_index', 'correction']
    if not all(param in data for param in required_params):
        return jsonify({'success': False, 'message': 'Paramètres manquants'}), 400
    
    error_type = data['error_type']
    chapter = data['chapter']
    error_index = data['error_index']
    correction = data['correction']
    
    try:
        # Lire le contenu du fichier
        with open(LIVRE_PATH, 'r', encoding='utf-8') as file:
            content = file.read()
        
        # Trouver la section du chapitre
        # Motif plus flexible qui permet des textes supplémentaires après le nom du chapitre
        chapter_pattern = r'^\*\* ' + re.escape(chapter) + r'(?:\s+.*?)?$(.*?)(?=^\*\* |\Z)'
        chapter_match = re.search(chapter_pattern, content, re.MULTILINE | re.DOTALL)
        
        # Si le motif flexible ne trouve pas de correspondance, essayer avec le motif original
        if not chapter_match:
            # Ajouter des logs pour le débogage
            print(f"Recherche du chapitre: '{chapter}'")
            print(f"Motif utilisé: {chapter_pattern}")
            
            # Lister tous les titres de chapitres dans le fichier pour le débogage
            all_chapters = re.findall(r'^\*\* (.*?)$', content, re.MULTILINE)
            print(f"Chapitres trouvés dans le fichier: {all_chapters}")
            
            return jsonify({
                'success': False, 
                'message': f'Chapitre "{chapter}" non trouvé',
                'debug_info': {
                    'pattern_used': chapter_pattern,
                    'chapters_in_file': all_chapters
                }
            }), 404
        
        chapter_content = chapter_match.group(1)
        chapter_start = chapter_match.start()
        chapter_end = chapter_match.end()
        
        # Appliquer la correction en fonction du type d'erreur
        if error_type == 'spelling':
            # Charger les erreurs d'orthographe pour ce chapitre
            errors = load_spelling_errors(chapter)
            if error_index >= len(errors):
                return jsonify({'success': False, 'message': 'Index d\'erreur invalide'}), 400
            
            error = errors[error_index]
            word = error['word']
            
            # Remplacer le mot dans le contenu du chapitre
            modified_chapter = re.sub(r'\b' + re.escape(word) + r'\b', correction, chapter_content)
            
            # Mettre à jour le contenu complet
            new_content = content[:chapter_start] + '** ' + chapter + modified_chapter + content[chapter_end:]
            
        elif error_type == 'grammar':
            # Charger les erreurs grammaticales pour ce chapitre
            errors = load_grammar_errors(chapter)
            if error_index >= len(errors):
                return jsonify({'success': False, 'message': 'Index d\'erreur invalide'}), 400
            
            error = errors[error_index]
            error_text = error['error_text']
            context = error['context']
            
            # Trouver le contexte dans le chapitre
            # Créer un motif plus flexible pour la recherche de contexte
            # 1. Échapper les caractères spéciaux dans le contexte
            escaped_context = re.escape(context)
            # 2. Remplacer les espaces échappées par un motif qui accepte plusieurs espaces
            flexible_context = escaped_context.replace('\\ ', '\\s+')
            # 3. Créer un motif qui permet des variations au début et à la fin du contexte
            flexible_pattern = flexible_context
            
            # Essayer d'abord avec le motif flexible
            context_match = re.search(flexible_pattern, chapter_content)
            
            # Si ça ne fonctionne pas, essayer avec une approche encore plus flexible
            if not context_match:
                # Extraire les mots significatifs du contexte (ignorer les mots très courts)
                words = [word for word in re.findall(r'\b\w+\b', context) if len(word) > 3]
                if words:
                    # Créer un motif qui recherche une phrase contenant ces mots dans l'ordre
                    words_pattern = r'.*'.join([re.escape(word) for word in words])
                    context_match = re.search(words_pattern, chapter_content)
            
            # Si toujours pas de correspondance, essayer de trouver juste l'erreur et son contexte immédiat
            if not context_match and error_text:
                # Chercher l'erreur avec quelques mots avant et après
                error_vicinity = r'[^\n]{0,30}' + re.escape(error_text) + r'[^\n]{0,30}'
                context_match = re.search(error_vicinity, chapter_content)
            
            if not context_match:
                # Ajouter des logs de débogage
                print(f"Contexte non trouvé: '{context}'")
                print(f"Motif flexible utilisé: {flexible_pattern}")
                print(f"Erreur à corriger: '{error_text}'")
                print(f"Correction à appliquer: '{correction}'")
                print(f"Extrait du chapitre (100 premiers caractères): '{chapter_content[:100]}'")
                
                return jsonify({
                    'success': False, 
                    'message': 'Contexte non trouvé dans le chapitre',
                    'debug_info': {
                        'context': context,
                        'error_text': error_text,
                        'correction': correction,
                        'chapter_excerpt': chapter_content[:200]  # Premiers 200 caractères du chapitre
                    }
                }), 404
            
            # Remplacer l'erreur dans le contexte trouvé
            matched_text = context_match.group(0)
            
            # Déterminer quelle stratégie de remplacement utiliser
            if matched_text == context:
                # Si le texte correspond exactement au contexte, utiliser la méthode simple
                corrected_context = context.replace(error_text, correction)
                modified_chapter = chapter_content.replace(matched_text, corrected_context)
            else:
                # Si nous avons utilisé une correspondance flexible, nous devons être plus précis
                # Trouver la position de l'erreur dans le contexte original
                error_pos = context.find(error_text)
                if error_pos >= 0:
                    # Calculer les positions relatives dans le texte trouvé
                    error_length = len(error_text)
                    
                    # Essayer de trouver l'erreur exacte dans le texte trouvé
                    error_in_match = re.search(re.escape(error_text), matched_text)
                    if error_in_match:
                        # Remplacer directement l'erreur dans le texte trouvé
                        start, end = error_in_match.span()
                        corrected_matched_text = matched_text[:start] + correction + matched_text[end:]
                        modified_chapter = chapter_content.replace(matched_text, corrected_matched_text)
                    else:
                        # Fallback: remplacer tout le texte trouvé
                        corrected_context = context.replace(error_text, correction)
                        modified_chapter = chapter_content.replace(matched_text, corrected_context)
                else:
                    # Si nous ne pouvons pas localiser l'erreur, utiliser une approche plus simple
                    corrected_context = context.replace(error_text, correction)
                    modified_chapter = chapter_content.replace(matched_text, corrected_context)
                
            # Ajouter des logs pour le débogage
            print(f"Correction appliquée: '{error_text}' -> '{correction}'")
            print(f"Contexte trouvé: '{matched_text}'")
            print(f"Contexte après correction: '{corrected_context if 'corrected_context' in locals() else 'N/A'}'")
            
            # Si le chapitre n'a pas été modifié, c'est qu'il y a un problème
            if modified_chapter == chapter_content:
                print("AVERTISSEMENT: Le chapitre n'a pas été modifié après la correction!")
            
            # Mettre à jour le contenu complet
            new_content = content[:chapter_start] + '** ' + chapter + modified_chapter + content[chapter_end:]
        
        else:
            return jsonify({'success': False, 'message': 'Type d\'erreur non reconnu'}), 400
        
        # Écrire le contenu modifié dans le fichier
        with open(LIVRE_PATH, 'w', encoding='utf-8') as file:
            file.write(new_content)
        
        return jsonify({'success': True, 'message': 'Correction appliquée avec succès'})
    
    except Exception as e:
        return jsonify({'success': False, 'message': f'Erreur lors de l\'application de la correction: {str(e)}'}), 500

def ignore_error(data):
    """Marque une erreur comme 'à ne pas traiter' dans le CSV."""
    # Vérifier les paramètres requis
    required_params = ['error_type', 'chapter', 'error_index']
    if not all(param in data for param in required_params):
        return jsonify({'success': False, 'message': 'Paramètres manquants'}), 400
    
    error_type = data['error_type']
    chapter = data['chapter']
    error_index = data['error_index']
    
    try:
        # Lire le CSV des erreurs
        rows = []
        with open(CSV_PATH, 'r', newline='', encoding='utf-8') as csvfile:
            reader = csv.reader(csvfile)
            headers = next(reader)  # Lire les en-têtes
            
            # Ajouter les en-têtes à la liste des lignes
            rows.append(headers)
            
            # Parcourir les lignes et mettre à jour celle du chapitre concerné
            for row in reader:
                if len(row) > 0 and row[0] == chapter:
                    # Mettre à jour la ligne en fonction du type d'erreur
                    if error_type == 'spelling' and len(row) > 1:
                        # Décrémenter le nombre d'erreurs d'orthographe
                        spelling_errors = int(row[1]) - 1
                        row[1] = str(max(0, spelling_errors))
                        # Mettre à jour le total
                        if len(row) > 3:
                            row[3] = str(int(row[3]) - 1)
                    elif error_type == 'grammar' and len(row) > 2:
                        # Décrémenter le nombre d'erreurs grammaticales
                        grammar_errors = int(row[2]) - 1
                        row[2] = str(max(0, grammar_errors))
                        # Mettre à jour le total
                        if len(row) > 3:
                            row[3] = str(int(row[3]) - 1)
                
                rows.append(row)
        
        # Écrire les modifications dans le CSV
        with open(CSV_PATH, 'w', newline='', encoding='utf-8') as csvfile:
            writer = csv.writer(csvfile)
            writer.writerows(rows)
        
        return jsonify({'success': True, 'message': 'Erreur ignorée avec succès'})
    
    except Exception as e:
        return jsonify({'success': False, 'message': f'Erreur lors de l\'ignorance de l\'erreur: {str(e)}'}), 500

def add_to_dictionary(data):
    """Ajoute un mot au dictionnaire personnalisé."""
    # Vérifier les paramètres requis
    if 'word' not in data:
        return jsonify({'success': False, 'message': 'Mot non spécifié'}), 400
    
    word = data['word']
    
    try:
        # Vérifier si le mot est déjà dans le dictionnaire
        existing_words = set()
        if os.path.exists(DICT_PATH):
            with open(DICT_PATH, 'r', encoding='utf-8') as file:
                for line in file:
                    line = line.strip()
                    if line and not line.startswith('#'):
                        existing_words.add(line.lower())
        
        # Si le mot n'est pas déjà dans le dictionnaire, l'ajouter
        if word.lower() not in existing_words:
            with open(DICT_PATH, 'a', encoding='utf-8') as file:
                file.write(f"\n{word}")
        
        return jsonify({'success': True, 'message': 'Mot ajouté au dictionnaire avec succès'})
    
    except Exception as e:
        return jsonify({'success': False, 'message': f'Erreur lors de l\'ajout du mot au dictionnaire: {str(e)}'}), 500

def load_spelling_errors(chapter):
    """Charge les erreurs d'orthographe pour un chapitre donné depuis le rapport."""
    # Cette fonction devrait charger les erreurs d'orthographe depuis le rapport
    # Pour simplifier, nous utilisons une implémentation fictive
    # Dans une implémentation réelle, il faudrait parser le rapport d'erreurs
    
    # Exemple d'implémentation fictive
    from generate_corrections_page import parse_error_report
    
    errors_by_chapter = parse_error_report('rapport_orthographe_grammaire.md')
    
    # Recherche exacte d'abord
    if chapter in errors_by_chapter:
        return errors_by_chapter[chapter]['spelling']
    
    # Si pas trouvé, essayer une recherche plus flexible
    # Chercher un chapitre qui commence par le même nom (sans les tags ou autres textes)
    for chap_name in errors_by_chapter.keys():
        # Vérifier si le chapitre commence par le nom recherché
        if chap_name.startswith(chapter) or chapter.startswith(chap_name):
            print(f"Correspondance flexible trouvée: '{chapter}' -> '{chap_name}'")
            return errors_by_chapter[chap_name]['spelling']
    
    print(f"Aucune erreur d'orthographe trouvée pour le chapitre: '{chapter}'")
    print(f"Chapitres disponibles: {list(errors_by_chapter.keys())}")
    return []

def load_grammar_errors(chapter):
    """Charge les erreurs grammaticales pour un chapitre donné depuis le rapport."""
    # Cette fonction devrait charger les erreurs grammaticales depuis le rapport
    # Pour simplifier, nous utilisons une implémentation fictive
    # Dans une implémentation réelle, il faudrait parser le rapport d'erreurs
    
    # Exemple d'implémentation fictive
    from generate_corrections_page import parse_error_report
    
    errors_by_chapter = parse_error_report('rapport_orthographe_grammaire.md')
    
    # Recherche exacte d'abord
    if chapter in errors_by_chapter:
        return errors_by_chapter[chapter]['grammar']
    
    # Si pas trouvé, essayer une recherche plus flexible
    # Chercher un chapitre qui commence par le même nom (sans les tags ou autres textes)
    for chap_name in errors_by_chapter.keys():
        # Vérifier si le chapitre commence par le nom recherché
        if chap_name.startswith(chapter) or chapter.startswith(chap_name):
            print(f"Correspondance flexible trouvée: '{chapter}' -> '{chap_name}'")
            return errors_by_chapter[chap_name]['grammar']
    
    print(f"Aucune erreur grammaticale trouvée pour le chapitre: '{chapter}'")
    print(f"Chapitres disponibles: {list(errors_by_chapter.keys())}")
    return []

# Pour tester l'API indépendamment
if __name__ == '__main__':
    app = Flask(__name__)
    app.register_blueprint(corrections_api)
    app.run(debug=True, port=5001)