"""
Global Flask error handling middleware
Provides centralized error handling for the Flask API with Laravel-style responses
"""

import traceback
import json
from flask import Flask, request, jsonify, g
from werkzeug.exceptions import HTTPException
from app.exceptions.validation_exception import ValidationException
from app.utils.response_helper import ApiResponse
from app.utils.helpers import get_logger


def register_error_handlers(app: Flask):
    """
    Register global error handlers for the Flask application

    Args:
        app: Flask application instance
    """
    logger = get_logger('error_handler')

    @app.errorhandler(ValidationException)
    def handle_validation_exception(e: ValidationException):
        """Handle ValidationException and return Laravel-style response"""
        logger.warning(f"Validation error: {e.message} - Errors: {e.errors}")
        return ApiResponse.validation_error(e.errors, e.message)

    @app.errorhandler(400)
    def handle_bad_request(e):
        """Handle 400 Bad Request errors"""
        logger.warning(f"Bad request: {str(e)}")
        return ApiResponse.error("Bad request. Please check your request format.", status_code=400)

    @app.errorhandler(401)
    def handle_unauthorized(e):
        """Handle 401 Unauthorized errors"""
        logger.warning(f"Unauthorized access attempt: {str(e)}")
        return ApiResponse.error("Unauthorized. Please provide valid authentication credentials.", status_code=401)

    @app.errorhandler(403)
    def handle_forbidden(e):
        """Handle 403 Forbidden errors"""
        logger.warning(f"Forbidden access attempt: {str(e)}")
        return ApiResponse.error("Forbidden. You don't have permission to access this resource.", status_code=403)

    @app.errorhandler(404)
    def handle_not_found(e):
        """Handle 404 Not Found errors"""
        logger.warning(f"Resource not found: {request.url}")
        return ApiResponse.error("The requested resource was not found.", status_code=404)

    @app.errorhandler(405)
    def handle_method_not_allowed(e):
        """Handle 405 Method Not Allowed errors"""
        logger.warning(f"Method not allowed: {request.method} {request.url}")
        return ApiResponse.error("Method not allowed for this endpoint.", status_code=405)

    @app.errorhandler(429)
    def handle_too_many_requests(e):
        """Handle 429 Too Many Requests errors"""
        logger.warning(f"Rate limit exceeded: {request.remote_addr}")
        return ApiResponse.error("Too many requests. Please try again later.", status_code=429)

    @app.errorhandler(500)
    def handle_internal_server_error(e):
        """Handle 500 Internal Server Error - Always return JSON"""
        logger.error(f"Internal server error: {str(e)}")
        logger.error(traceback.format_exc())

        # Always return JSON, even in debug mode
        if app.config.get('DEBUG'):
            return ApiResponse.error(
                f"Internal server error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}",
                status_code=500
            )
        else:
            return ApiResponse.error("An internal server error occurred.", status_code=500)

    @app.errorhandler(HTTPException)
    def handle_http_exception(e: HTTPException):
        """Handle generic HTTP exceptions - Always return JSON"""
        logger.warning(f"HTTP exception: {e.code} - {e.description}")
        return ApiResponse.error(e.description or "An error occurred.", status_code=e.code)

    @app.errorhandler(Exception)
    def handle_generic_exception(e: Exception):
        """Handle any unhandled exceptions - Always return JSON"""
        logger.error(f"Unhandled exception: {str(e)}")
        logger.error(traceback.format_exc())

        # Always return JSON format, even in debug mode
        if app.config.get('DEBUG'):
            return ApiResponse.error(
                f"Unhandled error: {str(e)}\n\nTraceback:\n{traceback.format_exc()}",
                status_code=500
            )
        else:
            return ApiResponse.error("An unexpected error occurred.", status_code=500)


def register_request_handlers(app: Flask):
    """
    Register request lifecycle handlers for the Flask application

    Args:
        app: Flask application instance
    """
    logger = get_logger('request_handler')

    @app.before_request
    def before_request_validation():
        """Run before each request for additional validation and setup"""
        # Log request details for debugging
        if app.config.get('DEBUG'):
            logger.debug(f"Request: {request.method} {request.url}")
            # silent=True: never raise on empty/invalid body (e.g. GET/DELETE sent with
            # Content-Type: application/json and no body) — logging must not 400 the request.
            json_body = request.get_json(silent=True) if request.is_json else None
            if json_body:
                logger.debug(f"JSON Body: {json_body}")
            if request.args:
                logger.debug(f"Query Params: {dict(request.args)}")

    @app.after_request
    def ensure_json_response(response):
        """Ensure all responses are JSON formatted"""
        # Add security headers to all responses
        response.headers['X-Content-Type-Options'] = 'nosniff'
        response.headers['X-Frame-Options'] = 'DENY'
        response.headers['X-XSS-Protection'] = '1; mode=block'

        # Add CORS headers if needed
        if app.config.get('ENABLE_CORS', True):
            response.headers['Access-Control-Allow-Origin'] = '*'
            response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
            response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'

        # Ensure error responses are JSON formatted
        if response.status_code >= 400:
            # Check if response is already JSON
            if not response.is_json and not response.content_type.startswith('application/json'):
                try:
                    # Convert non-JSON error responses to JSON
                    error_data = {
                        "error": "An error occurred",
                        "message": response.get_data(as_text=True) if response.get_data() else "Unknown error",
                        "status_code": response.status_code
                    }
                    response.data = json.dumps(error_data)
                    response.content_type = 'application/json'
                    logger.warning(f"Converted non-JSON error response to JSON: {response.status_code}")
                except Exception as e:
                    logger.error(f"Failed to convert response to JSON: {e}")

        return response

    @app.after_request
    def after_request_cleanup(response):
        """Run after each request for cleanup"""
        # Log response status for monitoring
        logger.debug(f"Response: {response.status_code} for {request.method} {request.url}")

        return response


def setup_error_handling(app: Flask):
    """
    Complete error handling setup for Flask application

    Args:
        app: Flask application instance
    """
    register_error_handlers(app)
    register_request_handlers(app)
