from fastapi import Request, status, HTTPException
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from sqlalchemy.exc import IntegrityError
from jose.exceptions import JWTError
import traceback
import os
from ...shared.utils.response_helpers import error_response
from ...shared.config.settings import settings

def _add_cors_headers(response: JSONResponse, request: Request) -> JSONResponse:
    """Add CORS headers to response for cross-origin requests."""
    origin = request.headers.get('Origin')
    if origin:
        # Check if origin is allowed (development or production)
        if settings.is_development:
            # Allow localhost origins in development
            if origin.startswith('http://localhost') or origin.startswith('http://127.0.0.1'):
                response.headers['Access-Control-Allow-Origin'] = origin
                response.headers['Access-Control-Allow-Credentials'] = 'true'
                response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, PATCH, OPTIONS'
                response.headers['Access-Control-Allow-Headers'] = '*'
        else:
            # In production, check against CORS_ORIGINS
            if origin in settings.CORS_ORIGINS:
                response.headers['Access-Control-Allow-Origin'] = origin
                response.headers['Access-Control-Allow-Credentials'] = 'true'
                response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, PATCH, OPTIONS'
                response.headers['Access-Control-Allow-Headers'] = '*'
    return response

async def validation_exception_handler(request: Request, exc: RequestValidationError):
    errors = []
    for error in exc.errors():
        field = '.'.join((str(loc) for loc in error['loc'] if loc != 'body'))
        errors.append({'field': field, 'message': error['msg']})
    first_error = errors[0]['message'] if errors else 'Validation error'
    request_id = getattr(request.state, 'request_id', None) if hasattr(request, 'state') else None
    response_content = error_response(
        message=first_error,
        errors=errors,
        request_id=request_id
    )
    response = JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content=response_content)
    # Add CORS headers to error responses
    return _add_cors_headers(response, request)

async def integrity_error_handler(request: Request, exc: IntegrityError):
    error_msg = str(exc.orig) if hasattr(exc, 'orig') else str(exc)
    request_id = getattr(request.state, 'request_id', None) if hasattr(request, 'state') else None
    if 'Duplicate entry' in error_msg or 'UNIQUE constraint' in error_msg:
        response_content = error_response(
            message='Duplicate entry',
            errors=[{'message': 'This record already exists'}],
            request_id=request_id
        )
        response = JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content=response_content)
    else:
        response_content = error_response(
            message='Database integrity error',
            request_id=request_id
        )
        response = JSONResponse(status_code=status.HTTP_400_BAD_REQUEST, content=response_content)
    # Add CORS headers to error responses
    return _add_cors_headers(response, request)

async def jwt_error_handler(request: Request, exc: JWTError):
    request_id = getattr(request.state, 'request_id', None) if hasattr(request, 'state') else None
    response_content = error_response(
        message='Invalid token',
        request_id=request_id
    )
    response = JSONResponse(status_code=status.HTTP_401_UNAUTHORIZED, content=response_content)
    # Add CORS headers to error responses
    return _add_cors_headers(response, request)

async def http_exception_handler(request: Request, exc: HTTPException):
    request_id = getattr(request.state, 'request_id', None) if hasattr(request, 'state') else None
    if isinstance(exc.detail, dict):
        response_content = exc.detail.copy()
        if request_id and 'request_id' not in response_content:
            response_content['request_id'] = request_id
        # Ensure it has standard error response format
        if 'status' not in response_content:
            response_content['status'] = 'error'
        if 'success' not in response_content:
            response_content['success'] = False
        response = JSONResponse(status_code=exc.status_code, content=response_content)
    else:
        response_content = error_response(
            message=str(exc.detail) if exc.detail else 'An error occurred',
            request_id=request_id
        )
        response = JSONResponse(status_code=exc.status_code, content=response_content)
    
    # Add CORS headers to error responses
    return _add_cors_headers(response, request)

async def general_exception_handler(request: Request, exc: Exception):
    from ...shared.config.logging_config import get_logger
    logger = get_logger(__name__)
    request_id = getattr(request.state, 'request_id', None)
    
    # Log full error details server-side
    logger.error(
        f'Unhandled exception: {type(exc).__name__}: {str(exc)}',
        extra={
            'request_id': request_id,
            'path': request.url.path,
            'method': request.method,
            'exception_type': type(exc).__name__,
            'client_ip': request.client.host if request.client else None,
            'user_agent': request.headers.get('User-Agent')
        },
        exc_info=True
    )
    
    # Determine status code and message
    if isinstance(exc, Exception) and hasattr(exc, 'status_code'):
        status_code = exc.status_code
        if hasattr(exc, 'detail'):
            detail = exc.detail
            if isinstance(detail, dict):
                response = JSONResponse(status_code=status_code, content=detail)
                return _add_cors_headers(response, request)
            message = str(detail) if detail else 'An error occurred'
        else:
            message = str(exc) if str(exc) else 'Internal server error'
    else:
        status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
        # Don't expose internal error details in production
        if settings.is_production:
            message = 'An internal server error occurred. Please try again later.'
        else:
            message = str(exc) if str(exc) else 'Internal server error'
    
    response_content = error_response(
        message=message,
        request_id=request_id
    )
    
    # NEVER include stack traces in production responses
    # Always log stack traces server-side only for debugging
    if settings.is_development:
        # Only include stack traces in development mode
        # Double-check environment to prevent accidental exposure
        env_check = os.getenv('ENVIRONMENT', 'development').lower()
        if env_check == 'development':
            response_content['stack'] = traceback.format_exc()
            response_content['error_type'] = type(exc).__name__
        else:
            # Log warning if development flag is set but environment says otherwise
            logger.warning(f'is_development=True but ENVIRONMENT={env_check}. Not including stack trace in response.')
    # Stack traces are always logged server-side via exc_info=True above
    response = JSONResponse(status_code=status_code, content=response_content)
    # Add CORS headers to error responses
    return _add_cors_headers(response, request)