import logging
import os
import time
from logging.handlers import RotatingFileHandler

from cryptography.fernet import Fernet
from flask import Flask, g, request
from flask_bcrypt import Bcrypt
from flask_ckeditor import CKEditor
from flask_login import LoginManager
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import event
from sqlalchemy.engine import Engine

from web_application.config import Config
from web_application.constants import AI_TOOLS, MAX_CONTENT_LENGTH, USER_ENDPOINTS

base_dir = os.path.dirname(os.path.abspath(__file__))
db = SQLAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
login_manager.login_view = "user.login"
login_manager.login_message_category = "info"
migrate = Migrate()
ckeditor = CKEditor()

# FERNET ENCRYPTION
# Load the key from the environment variable
ENCRYPTION_KEY = os.environ.get("FLASK_FERNET_KEY")

if ENCRYPTION_KEY is None:
    # Handle the error, perhaps by raising an exception or logging
    raise EnvironmentError("FLASK_FERNET_KEY environment variable not set.")

# Initialize the Fernet cipher suite
cipher_suite = Fernet(ENCRYPTION_KEY.encode())


def setup_logging(app):
    """Configure comprehensive logging"""
    # Create logs directory
    log_dir = os.path.join(os.path.dirname(base_dir), "logs")
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)

    # Clear default handlers
    app.logger.handlers.clear()

    # Set log level
    app.logger.setLevel(logging.INFO)

    # Console handler
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    console_formatter = logging.Formatter(
        "%(asctime)s [%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S"
    )
    console_handler.setFormatter(console_formatter)

    # File handler for all logs
    file_handler = RotatingFileHandler(
        os.path.join(log_dir, "da_application.log"),
        maxBytes=MAX_CONTENT_LENGTH,  # 10MB
        backupCount=10,
    )
    file_handler.setLevel(logging.INFO)
    file_formatter = logging.Formatter(
        "%(asctime)s [%(levelname)s] [%(name)s] [%(pathname)s:%(lineno)d]\n"
        "Message: %(message)s\n",
        datefmt="%Y-%m-%d %H:%M:%S",
    )
    file_handler.setFormatter(file_formatter)

    # Error handler
    error_handler = RotatingFileHandler(
        os.path.join(log_dir, "da_errors.log"),
        maxBytes=MAX_CONTENT_LENGTH,
        backupCount=20,
    )
    error_handler.setLevel(logging.ERROR)
    error_formatter = logging.Formatter(
        "%(asctime)s [%(levelname)s] [%(name)s] [%(pathname)s:%(lineno)d]\n"
        "Message: %(message)s\n"
        "%(exc_info)s\n",
        datefmt="%Y-%m-%d %H:%M:%S",
    )
    error_handler.setFormatter(error_formatter)

    # Database handler
    db_handler = RotatingFileHandler(
        os.path.join(log_dir, "da_database.log"),
        maxBytes=MAX_CONTENT_LENGTH,
        backupCount=5,
    )
    db_handler.setLevel(logging.WARNING)
    db_handler.setFormatter(file_formatter)

    # Add handlers
    app.logger.addHandler(console_handler)
    app.logger.addHandler(file_handler)
    app.logger.addHandler(error_handler)

    # SQLAlchemy logging
    sqlalchemy_logger = logging.getLogger("sqlalchemy.engine")
    sqlalchemy_logger.addHandler(db_handler)
    sqlalchemy_logger.setLevel(logging.WARNING)

    pool_logger = logging.getLogger("sqlalchemy.pool")
    pool_logger.addHandler(db_handler)
    pool_logger.setLevel(logging.WARNING)

    app.logger.info("=" * 80)
    app.logger.info("Application Starting")
    app.logger.info(f"Environment: {app.config.get('ENV', 'production')}")
    app.logger.info("=" * 80)


def setup_database_events(app):
    """Setup SQLAlchemy event listeners for monitoring"""

    @event.listens_for(Engine, "connect")
    def receive_connect(dbapi_conn, connection_record):
        app.logger.info("Database connection established")

    @event.listens_for(Engine, "checkout")
    def receive_checkout(dbapi_conn, connection_record, connection_proxy):
        app.logger.debug("Connection checked out from pool")

    @event.listens_for(Engine, "checkin")
    def receive_checkin(dbapi_conn, connection_record):
        app.logger.debug("Connection returned to pool")

    @event.listens_for(Engine, "close")
    def receive_close(dbapi_conn, connection_record):
        app.logger.info("Database connection closed")

    @event.listens_for(Engine, "handle_error")
    def handle_error(exception_context):
        app.logger.error(
            f"Database error occurred: {exception_context.original_exception}",
            exc_info=True,
        )


def setup_request_logging(app):
    """Setup request/response logging"""

    @app.before_request
    def before_request():
        g.start_time = time.time()
        app.logger.debug(f"Request: {request.method} {request.url}")
        app.logger.debug(f"Remote addr: {request.remote_addr}")

    @app.after_request
    def after_request(response):
        if hasattr(g, "start_time"):
            elapsed = time.time() - g.start_time
            app.logger.info(
                f"{request.method} {request.path} [{response.status_code}] {elapsed:.3f}s"
            )
        return response

    @app.teardown_request
    def teardown_request(exception=None):
        if exception:
            app.logger.error(f"Request teardown error: {exception}", exc_info=True)


def setup_error_handlers(app):
    """Setup error handlers with logging"""
    import traceback

    @app.errorhandler(Exception)
    def handle_exception(e):
        app.logger.error("=" * 80)
        app.logger.error(f"Unhandled exception: {request.method} {request.url}")
        app.logger.error(f"Remote addr: {request.remote_addr}")
        app.logger.error(f"Exception type: {type(e).__name__}")
        app.logger.error(f"Exception message: {str(e)}")
        app.logger.error("Full traceback:")
        app.logger.error(traceback.format_exc())
        app.logger.error("=" * 80)

        return "An internal error occurred. Please try again later.", 500

    @app.errorhandler(404)
    def not_found(e):
        app.logger.warning(f"404 Not Found: {request.method} {request.url}")
        return "Page not found", 404

    @app.errorhandler(500)
    def internal_error(e):
        app.logger.error(f"500 Error: {request.method} {request.url}", exc_info=True)
        return "Internal server error", 500


def test_database_connection(app):
    """Test database connection on startup"""
    from sqlalchemy import text

    try:
        with app.app_context():
            _ = db.session.execute(text("SELECT 1"))
            app.logger.info("OK: Database connection test successful")
            return True
    except Exception as e:
        app.logger.error(f"ERROR: Database connection test failed: {e}")
        app.logger.exception("Database connection error details:")
        return False


def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(Config)

    # Initialize extensions
    db.init_app(app)
    login_manager.init_app(app)
    bcrypt.init_app(app)
    migrate.init_app(app, db)
    ckeditor.init_app(app)

    # Setup logging first
    setup_logging(app)

    # Setup database monitoring
    setup_database_events(app)

    # Setup request logging
    setup_request_logging(app)

    # Setup error handlers
    setup_error_handlers(app)

    app.config["MAX_CONTENT_LENGTH"] = MAX_CONTENT_LENGTH

    # Test database connection
    with app.app_context():
        test_database_connection(app)

    @app.context_processor
    def inject_global_variables():
        return {
            "user_endpoints": USER_ENDPOINTS,
            "ai_tools": AI_TOOLS,
        }

    # Import blueprints
    from web_application.admin.main.routes import admin
    from web_application.site.main.routes import main
    from web_application.site.yolo.routes import yolo
    from web_application.user.routes import user

    # Register blueprints
    app.register_blueprint(admin)
    app.register_blueprint(main)
    app.register_blueprint(user)
    app.register_blueprint(yolo, url_prefix="/yolo")

    app.logger.info("All blueprints registered successfully")
    app.logger.info("Application initialization complete")

    return app
