import traceback

from flask import Flask, jsonify, request, redirect
from flask_jwt_extended import get_jwt_identity, verify_jwt_in_request
from sqlalchemy.exc import SQLAlchemyError
from werkzeug.exceptions import HTTPException

from common.extensions import db, api_rest, jwt, minio_client, simple_encryption, cors, cache_manager
import os
import logging.config

from common.models.user import TenantDomain
from common.utils.cors_utils import get_allowed_origins
from common.utils.database import Database
from config.logging_config import configure_logging
from .api.document_api import document_ns
from .api.auth import auth_ns
from .api.specialist_execution_api import specialist_execution_ns
from config.config import get_config
from common.utils.celery_utils import make_celery, init_celery
from common.utils.eveai_exceptions import EveAIException
from common.utils.debug_utils import register_request_debugger


def create_app(config_file=None):
    app = Flask(__name__)

    environment = os.getenv('FLASK_ENV', 'development')

    match environment:
        case 'development':
            app.config.from_object(get_config('dev'))
        case 'test':
            app.config.from_object(get_config('test'))
        case 'staging':
            app.config.from_object(get_config('staging'))
        case 'production':
            app.config.from_object(get_config('prod'))
        case _:
            app.config.from_object(get_config('dev'))

    app.config['SESSION_KEY_PREFIX'] = 'eveai_api_'

    app.celery = make_celery(app.name, app.config)
    init_celery(app.celery, app)

    configure_logging()
    logger = logging.getLogger(__name__)

    logger.info("eveai_api starting up")

    # Register Necessary Extensions
    register_extensions(app)

    # register Namespaces
    register_namespaces(api_rest)

    # Register Blueprints
    register_blueprints(app)

    # Register Error Handlers
    register_error_handlers(app)

    # Register Request Debugger
    register_request_debugger(app)

    # Register Cache Handlers
    register_cache_handlers(app)

    @app.before_request
    def check_cors():
        if request.method == 'OPTIONS':
            return '', 200  # Allow OPTIONS to pass through

        origin = request.headers.get('Origin')
        if not origin:
            return  # Not a CORS request

        # Get tenant ID from request
        if verify_jwt_in_request():
            tenant_id = get_jwt_identity()
            if not tenant_id:
                return
        else:
            return

        # Check if origin is allowed for this tenant
        allowed_origins = get_allowed_origins(tenant_id)

        if origin not in allowed_origins:
            app.logger.warning(f'Origin {origin} not allowed for tenant {tenant_id}')
            return {'error': 'Origin not allowed'}, 403

    @app.before_request
    def set_tenant_schema():
        # Check if this a health check request
        if request.path.startswith('/_healthz') or request.path.startswith('/healthz'):
            pass
        else:
            try:
                verify_jwt_in_request(optional=True)
                tenant_id = get_jwt_identity()

                if tenant_id:
                    Database(tenant_id).switch_schema()
            except Exception as e:
                app.logger.error(f'Error in before_request: {str(e)}')
                # Don't raise the exception here, let the request continue
                # The appropriate error handling will be done in the specific endpoints

    @app.route('/api/v1')
    def swagger():
        return redirect('/api/v1/')

    return app


def register_extensions(app):
    db.init_app(app)
    api_rest.init_app(app,
                      title='EveAI API',
                      version='1.0',
                      description='EveAI API',
                      doc='/api/v1/',
                      prefix='/api/v1'),
    jwt.init_app(app)
    minio_client.init_app(app)
    simple_encryption.init_app(app)
    cache_manager.init_app(app)
    cors.init_app(app, resources={
        r"/api/v1/*": {
            "origins": "*",
            "methods": ["GET", "POST", "PUT", "OPTIONS"],
            "allow_headers": ["Content-Type", "Authorization", "X-Requested-With"],
            "expose_headers": ["Content-Length", "Content-Range"],
            "supports_credentials": True,
            "max_age": 1728000,  # 20 days
            # "allow_credentials": True
        }
    })


def register_namespaces(app):
    api_rest.add_namespace(document_ns, path='/api/v1/documents')
    api_rest.add_namespace(auth_ns, path='/api/v1/auth')
    api_rest.add_namespace(specialist_execution_ns, path='/api/v1/specialist-execution')


def register_blueprints(app):
    from .views.healthz_views import healthz_bp
    app.register_blueprint(healthz_bp)


def register_error_handlers(app):
    @app.errorhandler(Exception)
    def handle_exception(e):
        """Handle all unhandled exceptions with detailed error responses"""
        # Get the current exception info
        exc_info = traceback.format_exc()

        # Log the full exception details
        app.logger.error(f"Unhandled exception: {str(e)}\n{exc_info}")

        # Start with a default error response
        response = {
            "error": "Internal Server Error",
            "message": str(e),
            "type": e.__class__.__name__
        }

        status_code = 500

        # Handle specific types of exceptions
        if isinstance(e, HTTPException):
            status_code = e.code
            response["error"] = e.name

        elif isinstance(e, SQLAlchemyError):
            response["error"] = "Database Error"
            response["details"] = str(e.__cause__ or e)

        elif isinstance(e, ValueError):
            status_code = 400
            response["error"] = "Invalid Input"

        # In development, include additional debug information
        if app.debug:
            response["debug"] = {
                "exception": exc_info,
                "class": e.__class__.__name__,
                "module": e.__class__.__module__
            }

        return jsonify(response), status_code

    @app.errorhandler(404)
    def not_found_error(e):
        return jsonify({
            "error": "Not Found",
            "message": str(e),
            "type": "NotFoundError"
        }), 404

    @app.errorhandler(400)
    def bad_request_error(e):
        return jsonify({
            "error": "Bad Request",
            "message": str(e),
            "type": "BadRequestError"
        }), 400


def register_cache_handlers(app):
    from common.utils.cache.config_cache import register_config_cache_handlers
    register_config_cache_handlers(cache_manager)
