from flask import jsonify, Response, g, request, current_app
from typing import Dict, Any, Optional, Union, List
import json
import logging
from datetime import datetime
from functools import wraps

logger = logging.getLogger(__name__)

class ApiResponse:
    """
    High-performance API response handler for Flask applications.
    Mimics Laravel's ApiResponse trait functionality.
    """

    # Response caching for repeated identical responses
    _response_cache = {}
    _cache_max_size = 1000

    @staticmethod
    def _create_response(data: Any, status_code: int, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Create optimized Flask response with caching support.
        """
        # Don't cache error responses (4xx and 5xx status codes)
        should_cache = 200 <= status_code < 400

        # Get DEBUG configuration value
        is_debug = current_app.config.get('DEBUG', False)

        cache_key = None
        if should_cache:
            # Generate cache key for identical responses
            cache_key = hash((str(data), status_code, str(headers or {})))

            # Check cache for repeated responses (performance optimization)
            if cache_key in ApiResponse._response_cache and len(ApiResponse._response_cache) < ApiResponse._cache_max_size:
                cached_response = ApiResponse._response_cache[cache_key]
                logger.debug(f"Response cache hit for status {status_code}")
                return cached_response

        # Log error responses for debugging
        if status_code >= 400:
            logger.error(f"Error response {status_code}: {data}")
            if status_code >= 500:
                logger.error(f"Server error details: {data}", exc_info=True)

        # Create new response
        response_data = data if isinstance(data, dict) else {"data": data}
        response_data['success'] = 200 <= status_code < 400

        # Create Flask response with status code
        response = jsonify(response_data)
        response.status_code = status_code

        # Add custom headers
        if headers:
            for key, value in headers.items():
                response.headers[key] = value

        # Add security headers
        response.headers['X-Content-Type-Options'] = 'nosniff'
        response.headers['X-Frame-Options'] = 'DENY'

        # Add metadata for debugging and tracking
        if hasattr(g, 'request_id'):
            #response_data['request_id'] = g.request_id
            response.headers['X-Request-ID'] = g.request_id

        # Add timestamp for API versioning and debugging
        #response_data['timestamp'] = datetime.now().isoformat()
        response.headers['X-Timestamp'] = datetime.now().isoformat()


        # Cache the response for performance
        if should_cache and cache_key and len(ApiResponse._response_cache) < ApiResponse._cache_max_size:
            ApiResponse._response_cache[cache_key] = response

        return response

    @staticmethod
    def _body(data: Any, message: Optional[str] = None) -> Union[Dict[str, Any], Any]:
        """
        Generate response body.
        When message is provided produces {"message": ..., "data": ...}.
        When omitted returns data as-is.
        """
        if message is None:
            return data
        return {"message": message, "data": data}

    @staticmethod
    def ok(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send successful response (HTTP 200).
        Equivalent to PHP's ok() method.
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 200, headers)

    @staticmethod
    def created(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send resource created response (HTTP 201).
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 201, headers)

    @staticmethod
    def accepted(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send accepted response (HTTP 202).
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 202, headers)

    @staticmethod
    def no_content(headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send no content response (HTTP 204).
        """
        return ApiResponse._create_response({}, 204, headers)

    @staticmethod
    def bad_request(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send bad request error (HTTP 400).
        Equivalent to PHP's badRequest() method.
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 400, headers)

    @staticmethod
    def unauthorized(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send unauthorized error (HTTP 401).
        Equivalent to PHP's unauthorize() method.
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 401, headers)

    @staticmethod
    def forbidden(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send forbidden error (HTTP 403).
        Equivalent to PHP's forbidden() method.
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 403, headers)

    @staticmethod
    def not_found(data: Optional[Any] = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send not found error (HTTP 404).
        Equivalent to PHP's notFound() method.
        """
        if data is None:
            data = {"message": "No data found"}

        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 404, headers)

    @staticmethod
    def method_not_allowed(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send method not allowed error (HTTP 405).
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 405, headers)

    @staticmethod
    def conflict(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send conflict error (HTTP 409).
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 409, headers)

    @staticmethod
    def unprocessable_entity(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send validation error (HTTP 422).
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 422, headers)

    @staticmethod
    def too_many_requests(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send rate limit error (HTTP 429).
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 429, headers)

    @staticmethod
    def server_error(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send server error (HTTP 500).
        Equivalent to PHP's serverError() method.
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 500, headers)

    @staticmethod
    def service_unavailable(data: Any = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send service unavailable error (HTTP 503).
        """
        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 503, headers)

    @staticmethod
    def error(message: str, status_code: int = 400, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send error response with message.
        Equivalent to PHP's error() method.
        """
        return ApiResponse._create_response({"message": message}, status_code, headers)

    @staticmethod
    def error_with_message(message: str, code: int = 400, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send error response with fixed message.
        Equivalent to PHP's errorWithMessage() method.
        """
        return ApiResponse._create_response({"message": message}, code, headers)

    @staticmethod
    def validation_error(
        errors: Dict[str, List[str]],
        message: str = "The given data was invalid.",
        status_code: int = 422
    ) -> Response:
        """
        Send Laravel-style validation error response with detailed field errors.

        Args:
            errors: Dictionary of field names to lists of error messages
            message: General error message
            status_code: HTTP status code (default: 422)

        Returns:
            Flask Response object
        """
        data = {
            "message": message,
            "errors": errors
        }

        return ApiResponse._create_response(data, status_code)

    @staticmethod
    def paginated_response(data: List[Any], pagination_info: Dict[str, Any], message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send paginated response with metadata.
        """
        response_data = {
            "data": data,
            "pagination": pagination_info
        }

        return ApiResponse._create_response(response_data, 200, headers)

    @staticmethod
    def bulk_response(results: List[Dict[str, Any]], message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send bulk operation response.
        """
        success_count = sum(1 for result in results if result.get("success", False))
        failure_count = len(results) - success_count

        response_data = {
            "results": results,
            "summary": {
                "total": len(results),
                "success": success_count,
                "failed": failure_count
            }
        }

        status_code = 200 if failure_count == 0 else 207  # Multi-status for partial success
        return ApiResponse._create_response(response_data, status_code, headers)

    @staticmethod
    def no_data_found(data: Optional[Any] = None, message: Optional[str] = None, headers: Optional[Dict[str, str]] = None) -> Response:
        """
        Send no content found response (HTTP 204).
        Equivalent to PHP's noDataFound() method.
        """
        if data is None:
            return ApiResponse.no_content(headers)

        response_data = ApiResponse._body(data, message)
        return ApiResponse._create_response(response_data, 204, headers)

    @staticmethod
    def clear_cache():
        """
        Clear response cache for memory management.
        """
        ApiResponse._response_cache.clear()
        logger.info("ApiResponse cache cleared")


# Performance-optimized decorator for automatic response handling
def api_response(status_code: int = 200, key: Optional[str] = None, headers: Optional[Dict[str, str]] = None):
    """
    Decorator for automatic API response formatting.
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            try:
                result = func(*args, **kwargs)

                # If function already returns a response tuple, return as-is
                if isinstance(result, tuple) and len(result) == 2:
                    return result

                # Otherwise, format the response
                return ApiResponse._create_response(
                    ApiResponse._body(result, key),
                    status_code,
                    headers
                )
            except Exception as e:
                is_debug = current_app.config.get('DEBUG', False)

                # Log the actual exception with full traceback
                logger.error(f"Unhandled exception in {func.__name__}: {str(e)}", exc_info=True)

                # Return detailed error in development, generic in production
                if is_debug:
                    error_data = {
                        "message": "Internal server error",
                        "function": func.__name__,
                        "error": str(e),
                        "debug_mode": True
                    }
                else:
                    error_data = {"message": "Internal server error"}
                return ApiResponse.server_error(error_data)
        return wrapper
    return decorator


# Backward compatibility functions (legacy support)
def success_response(data: Any, message: str = "Success") -> Response:
    """Legacy function for backward compatibility."""
    return ApiResponse.ok({"data": data, "message": message})

def error_response(message: str = "An error occurred", code: int = 400) -> Response:
    """Legacy function for backward compatibility."""
    return ApiResponse.error_with_message(message, code)

def not_found_response(message: str = "Resource not found") -> Response:
    """Legacy function for backward compatibility."""
    return ApiResponse.not_found({"message": message})

def validation_error_response(errors: List[Dict[str, Any]]) -> Response:
    """Legacy function for backward compatibility."""
    return ApiResponse.validation_error("Validation errors", errors)
