# markdownconverter/utils/error_handler.py

"""
Unified error handling for conversion operations.
Provides consistent error classification, logging, and user-friendly messages.
"""

import tkinter as tk
from tkinter import messagebox
from typing import Optional, Callable
from ..core.core import ConverterNotFoundError, TemplatePlaceholderError
from .logger import get_logger

log = get_logger(__name__)


class ConversionError(Exception):
    """Base exception for conversion errors."""
    pass


class ErrorCategory:
    """Categories of conversion errors for better user feedback."""
    FILE_NOT_FOUND = "file_not_found"
    TEMPLATE_ERROR = "template_error"
    CONVERTER_MISSING = "converter_missing"
    CONVERSION_FAILED = "conversion_failed"
    PERMISSION_ERROR = "permission_error"
    TIMEOUT_ERROR = "timeout_error"
    UNKNOWN = "unknown"


def classify_error(exception: Exception) -> str:
    """
    Classify an exception into a specific error category.
    
    Args:
        exception: The exception to classify
        
    Returns:
        Error category string
    """
    if isinstance(exception, FileNotFoundError):
        return ErrorCategory.FILE_NOT_FOUND
    elif isinstance(exception, TemplatePlaceholderError):
        return ErrorCategory.TEMPLATE_ERROR
    elif isinstance(exception, ConverterNotFoundError):
        return ErrorCategory.CONVERTER_MISSING
    elif isinstance(exception, PermissionError):
        return ErrorCategory.PERMISSION_ERROR
    elif "timeout" in str(exception).lower() or "timed out" in str(exception).lower():
        return ErrorCategory.TIMEOUT_ERROR
    elif isinstance(exception, (RuntimeError, ValueError)):
        return ErrorCategory.CONVERSION_FAILED
    else:
        return ErrorCategory.UNKNOWN


def get_user_friendly_message(category: str, exception: Exception) -> tuple[str, str]:
    """
    Get user-friendly title and message for an error category.
    
    Args:
        category: Error category from ErrorCategory
        exception: The original exception
        
    Returns:
        Tuple of (title, message) for display
    """
    messages = {
        ErrorCategory.FILE_NOT_FOUND: (
            "File Non Trovato",
            f"Il file richiesto non è stato trovato:\n\n{str(exception)}\n\n"
            "Verifica che il file esista e sia accessibile."
        ),
        ErrorCategory.TEMPLATE_ERROR: (
            "Errore Template",
            f"Il template presenta un problema:\n\n{str(exception)}\n\n"
            "Verifica che il template contenga tutti i placeholder richiesti."
        ),
        ErrorCategory.CONVERTER_MISSING: (
            "Convertitore Mancante",
            f"{str(exception)}\n\n"
            "Per la conversione PDF è necessario installare:\n"
            "- Microsoft Word (consigliato), oppure\n"
            "- LibreOffice (alternativa gratuita)"
        ),
        ErrorCategory.CONVERSION_FAILED: (
            "Conversione Fallita",
            f"La conversione non è riuscita:\n\n{str(exception)}\n\n"
            "Verifica che:\n"
            "- Il file di input sia valido\n"
            "- Pandoc sia installato correttamente\n"
            "- Il formato di output sia supportato"
        ),
        ErrorCategory.PERMISSION_ERROR: (
            "Errore di Permessi",
            f"Non è possibile accedere al file:\n\n{str(exception)}\n\n"
            "Possibili cause:\n"
            "- Il file è aperto in un altro programma\n"
            "- Permessi insufficienti sulla cartella\n"
            "- Il file è in sola lettura"
        ),
        ErrorCategory.TIMEOUT_ERROR: (
            "Timeout Conversione",
            f"La conversione ha impiegato troppo tempo:\n\n{str(exception)}\n\n"
            "Il documento potrebbe essere troppo grande o complesso.\n"
            "Prova a chiudere altri programmi o dividere il documento."
        ),
        ErrorCategory.UNKNOWN: (
            "Errore Inaspettato",
            f"Si è verificato un errore imprevisto:\n\n{str(exception)}\n\n"
            "Consulta il log per maggiori dettagli."
        )
    }
    
    return messages.get(category, messages[ErrorCategory.UNKNOWN])


def handle_conversion_error(
    exception: Exception,
    log_callback: Optional[Callable[[str], None]] = None,
    show_dialog: bool = True
) -> None:
    """
    Handle a conversion error with consistent logging and user feedback.
    
    Args:
        exception: The exception to handle
        log_callback: Optional callback function for logging to GUI (e.g., self._log)
        show_dialog: If True, shows a messagebox to the user
    """
    # Classify the error
    category = classify_error(exception)
    
    # Log the error with full stack trace
    log.error(f"Conversion error ({category}): {exception}", exc_info=True)
    
    # Log to GUI if callback provided
    if log_callback:
        log_callback(f"❌ ERRORE ({category}): {str(exception)}")
    
    # Show user-friendly dialog if requested
    if show_dialog:
        title, message = get_user_friendly_message(category, exception)
        messagebox.showerror(title, message)


def safe_conversion(
    conversion_func: Callable,
    *args,
    log_callback: Optional[Callable[[str], None]] = None,
    success_callback: Optional[Callable] = None,
    error_callback: Optional[Callable[[Exception], None]] = None,
    **kwargs
) -> Optional[any]:
    """
    Safely execute a conversion function with automatic error handling.
    
    Args:
        conversion_func: The conversion function to call
        *args: Positional arguments for the conversion function
        log_callback: Optional callback for GUI logging
        success_callback: Optional callback on success
        error_callback: Optional callback on error (receives the exception)
        **kwargs: Keyword arguments for the conversion function
        
    Returns:
        The result of conversion_func if successful, None if failed
    """
    try:
        result = conversion_func(*args, **kwargs)
        
        if success_callback:
            success_callback(result)
        
        return result
        
    except Exception as e:
        handle_conversion_error(e, log_callback=log_callback, show_dialog=True)
        
        if error_callback:
            error_callback(e)
        
        return None
