"""
Error handling utilities for reports system.

Provides centralized error handling, user-friendly error messages,
and logging for all report operations.
"""

from django.http import JsonResponse, HttpResponse
from django.contrib import messages
from django.shortcuts import render
from functools import wraps
import logging
from typing import Callable, Any

logger = logging.getLogger(__name__)


class ReportError(Exception):
    """Base exception for report-related errors"""
    pass


class FilterValidationError(ReportError):
    """Exception raised when filter validation fails"""
    pass


class DataExportError(ReportError):
    """Exception raised when data export fails"""
    pass


class DatabaseError(ReportError):
    """Exception raised when database operations fail"""
    pass


def handle_report_errors(view_func: Callable) -> Callable:
    """
    Decorator to handle errors in report views.
    
    Catches exceptions, logs them, and returns user-friendly error responses.
    
    Usage:
        @handle_report_errors
        def my_report_view(request):
            # view logic
    """
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        try:
            return view_func(request, *args, **kwargs)
            
        except FilterValidationError as e:
            logger.warning(f"Filter validation error in {view_func.__name__}: {str(e)}")
            messages.error(request, f"Invalid filter parameters: {str(e)}")
            
            # Return to the same page with error message
            return render(request, 'reports/error.html', {
                'error_title': 'Invalid Filters',
                'error_message': str(e),
                'suggestion': 'Please check your filter values and try again.'
            }, status=400)
            
        except DataExportError as e:
            logger.error(f"Export error in {view_func.__name__}: {str(e)}")
            messages.error(request, f"Export failed: {str(e)}")
            
            return HttpResponse(
                f"Error generating export: {str(e)}",
                content_type='text/plain',
                status=500
            )
            
        except DatabaseError as e:
            logger.error(f"Database error in {view_func.__name__}: {str(e)}")
            messages.error(request, "A database error occurred. Please try again later.")
            
            return render(request, 'reports/error.html', {
                'error_title': 'Database Error',
                'error_message': 'Unable to retrieve data from the database.',
                'suggestion': 'Please try again later or contact support if the problem persists.'
            }, status=500)
            
        except Exception as e:
            logger.exception(f"Unexpected error in {view_func.__name__}: {str(e)}")
            messages.error(request, "An unexpected error occurred. Please try again.")
            
            return render(request, 'reports/error.html', {
                'error_title': 'Unexpected Error',
                'error_message': 'An unexpected error occurred while processing your request.',
                'suggestion': 'Please try again or contact support if the problem persists.'
            }, status=500)
    
    return wrapper


def handle_export_errors(export_func: Callable) -> Callable:
    """
    Decorator specifically for export functions.
    
    Provides specialized error handling for PDF and Excel exports.
    
    Usage:
        @handle_export_errors
        def export_report_pdf(request):
            # export logic
    """
    @wraps(export_func)
    def wrapper(request, *args, **kwargs):
        try:
            return export_func(request, *args, **kwargs)
            
        except DataExportError as e:
            logger.error(f"Export error in {export_func.__name__}: {str(e)}")
            return HttpResponse(
                f"Error generating export: {str(e)}\n\nPlease try again or contact support.",
                content_type='text/plain',
                status=500
            )
            
        except Exception as e:
            logger.exception(f"Unexpected export error in {export_func.__name__}: {str(e)}")
            return HttpResponse(
                "An unexpected error occurred while generating the export.\n\n"
                "Please try again or contact support if the problem persists.",
                content_type='text/plain',
                status=500
            )
    
    return wrapper


def validate_date_range(start_date, end_date) -> tuple:
    """
    Validate date range parameters.
    
    Args:
        start_date: Start date
        end_date: End date
        
    Returns:
        Tuple of (is_valid, error_message)
        
    Raises:
        FilterValidationError: If validation fails
    """
    if start_date and end_date:
        if start_date > end_date:
            raise FilterValidationError(
                "Start date must be before or equal to end date"
            )
    
    return True, ""


def validate_filter_params(params) -> tuple:
    """
    Validate filter parameters.
    
    Args:
        params: FilterParams object
        
    Returns:
        Tuple of (is_valid, error_message)
        
    Raises:
        FilterValidationError: If validation fails
    """
    # Validate date range
    if params.start_date and params.end_date:
        validate_date_range(params.start_date, params.end_date)
    
    # Validate gender
    if params.gender:
        valid_genders = ['M', 'F', 'MALE', 'FEMALE', 'OTHER']
        if params.gender.upper() not in valid_genders:
            raise FilterValidationError(
                f"Invalid gender value: {params.gender}. "
                f"Must be one of: {', '.join(valid_genders)}"
            )
    
    # Validate period
    if params.period:
        valid_periods = ['daily', 'weekly', 'monthly', 'custom', 'all_time', 
                        'today', 'week', 'month', 'quarter']
        if params.period.lower() not in valid_periods:
            raise FilterValidationError(
                f"Invalid period value: {params.period}. "
                f"Must be one of: {', '.join(valid_periods)}"
            )
    
    return True, ""


def get_user_friendly_error_message(error: Exception) -> str:
    """
    Convert technical error messages to user-friendly messages.
    
    Args:
        error: Exception object
        
    Returns:
        User-friendly error message string
    """
    error_str = str(error).lower()
    
    # Database errors
    if 'database' in error_str or 'connection' in error_str:
        return "Unable to connect to the database. Please try again later."
    
    # Validation errors
    if 'validation' in error_str or 'invalid' in error_str:
        return f"Invalid input: {str(error)}"
    
    # Export errors
    if 'pdf' in error_str or 'excel' in error_str or 'export' in error_str:
        return "Unable to generate the export file. Please try again."
    
    # Permission errors
    if 'permission' in error_str or 'forbidden' in error_str:
        return "You don't have permission to access this resource."
    
    # Generic error
    return "An error occurred while processing your request. Please try again."


def log_report_access(user, report_type: str, filters: dict = None):
    """
    Log report access for auditing purposes.
    
    Args:
        user: User accessing the report
        report_type: Type of report being accessed
        filters: Applied filters (optional)
    """
    logger.info(
        f"Report access: user={user.username if user else 'anonymous'}, "
        f"report={report_type}, filters={filters}"
    )


def log_export_request(user, report_type: str, export_format: str, record_count: int):
    """
    Log export requests for auditing purposes.
    
    Args:
        user: User requesting the export
        report_type: Type of report being exported
        export_format: Format of export (PDF, Excel)
        record_count: Number of records being exported
    """
    logger.info(
        f"Export request: user={user.username if user else 'anonymous'}, "
        f"report={report_type}, format={export_format}, records={record_count}"
    )


class ErrorMessageBuilder:
    """
    Builder class for constructing detailed error messages.
    """
    
    def __init__(self, error_type: str):
        self.error_type = error_type
        self.details = []
        self.suggestions = []
    
    def add_detail(self, detail: str):
        """Add error detail"""
        self.details.append(detail)
        return self
    
    def add_suggestion(self, suggestion: str):
        """Add suggestion for resolving the error"""
        self.suggestions.append(suggestion)
        return self
    
    def build(self) -> dict:
        """Build the error message dictionary"""
        return {
            'error_type': self.error_type,
            'details': self.details,
            'suggestions': self.suggestions,
            'message': self._format_message()
        }
    
    def _format_message(self) -> str:
        """Format the complete error message"""
        message = f"{self.error_type}\n\n"
        
        if self.details:
            message += "Details:\n"
            for detail in self.details:
                message += f"  • {detail}\n"
            message += "\n"
        
        if self.suggestions:
            message += "Suggestions:\n"
            for suggestion in self.suggestions:
                message += f"  • {suggestion}\n"
        
        return message
