"""
Response formatting and utility functions
Enhanced with standardized API response formats
"""
import json
import logging
import os
from flask import jsonify, make_response
from datetime import datetime, date, timedelta
from decimal import Decimal


logger = logging.getLogger(__name__)

# Backward compatibility mode - can be disabled after migration
BACKWARD_COMPAT_MODE = os.getenv('API_BACKWARD_COMPAT', 'true').lower() == 'true'


class CustomJSONEncoder(json.JSONEncoder):
    """Custom JSON encoder that can handle Decimal types and datetime objects"""
    def default(self, obj):
        if isinstance(obj, Decimal):
            return float(obj)
        elif isinstance(obj, (datetime, date)):
            return obj.isoformat()
        elif isinstance(obj, timedelta):
            return obj.total_seconds()
        return super().default(obj)


def format_api_response(data, message="Success", meta=None, include_legacy_fields=None):
    """Format API response with consistent structure.
    
    Args:
        data: Response data (dict, list, or primitive)
        message: Success message
        meta: Optional metadata dict (timestamp, version, etc.)
        include_legacy_fields: Dict of legacy fields for backward compatibility
        
    Returns:
        Standardized response dictionary
    """
    response_data = {
        "success": True,
        "message": message,
        "data": data
    }
    
    # Add optional metadata
    if meta:
        response_data["meta"] = meta
    elif BACKWARD_COMPAT_MODE:
        # Auto-add timestamp in backward compat mode
        response_data["meta"] = {"timestamp": datetime.now().isoformat()}
    
    # Backward compatibility: include legacy fields at root level (only if it's a dict)
    if include_legacy_fields and BACKWARD_COMPAT_MODE and isinstance(include_legacy_fields, dict):
        response_data.update(include_legacy_fields)
    
    return response_data


def format_paginated_response(data, page, per_page, total_items, message="Success", meta=None):
    """Format paginated API response.
    
    Args:
        data: List of items for current page
        page: Current page number (1-indexed)
        per_page: Items per page
        total_items: Total number of items across all pages
        message: Success message
        meta: Optional metadata
        
    Returns:
        Paginated response dictionary
    """
    total_pages = (total_items + per_page - 1) // per_page if per_page > 0 else 0
    
    response_data = {
        "success": True,
        "message": message,
        "data": data,
        "pagination": {
            "current_page": page,
            "total_pages": total_pages,
            "per_page": per_page,
            "total_items": total_items
        }
    }
    
    if meta:
        response_data["meta"] = meta
    elif BACKWARD_COMPAT_MODE:
        response_data["meta"] = {"timestamp": datetime.now().isoformat()}
    
    return response_data


def create_success_response(data=None, message="Success", status_code=200):
    """
    Create a standardized success response
    
    Args:
        data: Response data (dict)
        message: Success message
        status_code: HTTP status code (default 200)
        
    Returns:
        Flask response tuple (jsonify(data), status_code)
    """
    response_data = {
        "status": "success",
        "message": message
    }
    
    if data is not None:
        if isinstance(data, dict):
            response_data.update(data)
        else:
            response_data["data"] = data
    
    return jsonify(response_data), status_code


def create_error_response(message, status_code=500, error_code=None, error_details=None, encrypt=False):
    """Create a standardized error response.
    
    Args:
        message: User-friendly error message
        status_code: HTTP status code
        error_code: Machine-readable error code (e.g., 'INVALID_CREDENTIALS')
        error_details: Technical details for developers (optional)
        encrypt: Whether to mark the response for encryption
        
    Returns:
        Flask Response object with error structure
    """
    error_data = {
        "success": False,
        "message": message
    }
    
    # Include error details if provided
    if error_code or error_details:
        error_data["error"] = {}
        if error_code:
            error_data["error"]["code"] = error_code
        if error_details:
            error_data["error"]["details"] = error_details
    
    # Add metadata in backward compat mode
    if BACKWARD_COMPAT_MODE:
        error_data["meta"] = {"timestamp": datetime.now().isoformat()}
    
    error_response = make_response(jsonify(error_data), status_code)
    
    if encrypt:
        error_response._encrypt_response = True
        
    return error_response


def format_validation_error(validation_errors, message="Validation failed"):
    """Create validation error response (400).
    
    Args:
        validation_errors: Dict or list of validation errors
        message: Error message
        
    Returns:
        Flask Response object
    """
    return create_error_response(
        message=message,
        status_code=400,
        error_code="VALIDATION_ERROR",
        error_details=validation_errors
    )


def format_not_found_error(resource_type="Resource", resource_id=None):
    """Create not found error response (404).
    
    Args:
        resource_type: Type of resource (e.g., 'Course', 'User')
        resource_id: ID of the resource
        
    Returns:
        Flask Response object
    """
    message = f"{resource_type} not found"
    if resource_id:
        message += f" (ID: {resource_id})"
    
    return create_error_response(
        message=message,
        status_code=404,
        error_code="NOT_FOUND",
        error_details={"resource_type": resource_type, "resource_id": resource_id}
    )


def format_unauthorized_error(message="Unauthorized access", require_login=True):
    """Create unauthorized error response (401 or 403).
    
    Args:
        message: Error message
        require_login: If True, returns 401 (authentication required),
                      if False, returns 403 (insufficient permissions)
        
    Returns:
        Flask Response object
    """
    status_code = 401 if require_login else 403
    error_code = "AUTHENTICATION_REQUIRED" if require_login else "INSUFFICIENT_PERMISSIONS"
    
    return create_error_response(
        message=message,
        status_code=status_code,
        error_code=error_code
    )


def format_server_error(message="Internal server error", error_details=None):
    """Create server error response (500).
    
    Args:
        message: Error message
        error_details: Technical error details
        
    Returns:
        Flask Response object
    """
    # Don't expose internal error details in production
    if os.getenv('FLASK_ENV') == 'production' and error_details:
        logger.error(f"Server error: {error_details}")
        error_details = None
    
    return create_error_response(
        message=message,
        status_code=500,
        error_code="INTERNAL_ERROR",
        error_details=error_details
    )


def convert_analytics_numbers(data):
    """Convert Decimal and string numbers to proper numeric types for analytics data"""
    if isinstance(data, dict):
        converted = {}
        for key, value in data.items():
            if key in ['avg_score', 'best_score', 'worst_score', 'score_percentage', 'accuracy_percentage', 
                      'avg_time_per_question', 'avg_performance', 'daily_avg_score', 'recent_avg']:
                # Convert to float for percentage/score values
                try:
                    converted[key] = float(value) if value is not None else 0.0
                except (ValueError, TypeError):
                    converted[key] = 0.0
            elif key in ['total_quizzes', 'total_questions_attempted', 'total_correct_answers', 
                        'quiz_count', 'questions_attempted', 'correct_answers', 'total_questions',
                        'session_count', 'quizzes_taken', 'current_streak', 'study_hour']:
                # Convert to int for count values
                try:
                    converted[key] = int(value) if value is not None else 0
                except (ValueError, TypeError):
                    converted[key] = 0
            elif key in ['total_time_spent', 'time_taken']:
                # Convert to float for time values
                try:
                    converted[key] = float(value) if value is not None else 0.0
                except (ValueError, TypeError):
                    converted[key] = 0.0
            else:
                converted[key] = value
        return converted
    elif isinstance(data, list):
        return [convert_analytics_numbers(item) for item in data]
    else:
        return data


def encrypt_data(data):
    """Wrap the original encrypt_data function with JSON encoding that handles Decimal types"""
    try:
        # Convert data to JSON string using our custom encoder
        json_data = json.dumps(data, cls=CustomJSONEncoder)
        # Pass the parsed JSON to the original encrypt function
        from security import encrypt_data as original_encrypt_data
        return original_encrypt_data(json.loads(json_data))
    except Exception as e:
        logger.error(f"Error preparing data for encryption: {str(e)}")
        # Fall back to direct encryption if the custom approach fails
        from security import encrypt_data as original_encrypt_data
        return original_encrypt_data(data)


# Utility function to get database connection context manager
def get_db_connection():
    """Get database connection using context manager for proper cleanup"""
    from database import ManagedDBConnection, get_cached_connection
    return ManagedDBConnection(get_cached_connection())


def fetch_as_dict(cursor):
    """Fetch all rows from cursor as list of dicts"""
    columns = [col[0] for col in cursor.description] if cursor.description else []
    return [dict(zip(columns, row)) for row in cursor.fetchall()]