import logging
import os
import time
from logging.handlers import RotatingFileHandler

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 USER_ENDPOINTS

base_dir = os.path.dirname(os.path.abspath(__file__))
db = SQLAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
login_manager.login_view = "kullanici.giris"
login_manager.login_message_category = "info"
migrate = Migrate()
ckeditor = CKEditor()


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=10 * 1024 * 1024,  # 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=10 * 1024 * 1024,
        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=10 * 1024 * 1024,
        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")
        # Store current context on the persistent connection record
        try:
            from flask import has_request_context, request
            from flask_login import current_user
            
            if has_request_context():
                connection_record.info["last_request_url"] = request.url
                connection_record.info["last_user_id"] = getattr(current_user, "id", "Anonymous")
        except Exception:
            pass

    @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, "before_cursor_execute")
    def before_cursor_execute(
        conn, cursor, statement, parameters, context, execmany
    ):
        # Store the last statement and parameters on the connection record info
        # Using .info on connection_record (per-connection persistent dict)
        conn.info["last_statement"] = statement
        conn.info["last_parameters"] = parameters

    @event.listens_for(Engine, "handle_error")
    def handle_error(exception_context):
        import traceback

        original = exception_context.original_exception
        error_msg = str(original)
        
        # List of errors that indicate a corrupted or lost connection
        disconnect_errors = [
            "Packet sequence number wrong",
            "Command Out of Sync",
            "MySQL server has gone away",
            "Lost connection to MySQL server",
            "Broken pipe"
        ]

        # Extract last query and context
        last_sql = "N/A"
        last_params = "N/A"
        last_url = "N/A"
        last_user = "N/A"
        
        # In handle_error, context.connection is the Connection object
        # which has an .info dict that mirrors the DBAPI connection's info
        if exception_context.connection:
            last_sql = exception_context.connection.info.get("last_statement", "N/A")
            last_params = exception_context.connection.info.get("last_parameters", "N/A")
            
            # Retrieve persistent metadata from the connection record if possible
            # Note: SQLAlchemy 2.0 handle_error context has connection, but finding record is harder
            # Fallback to connection.info which we've been using
            last_url = exception_context.connection.info.get("last_request_url", "N/A")
            last_user = exception_context.connection.info.get("last_user_id", "N/A")

        # Capture app stack trace
        app_trace = ""
        try:
            extracted_trace = traceback.extract_stack()
            app_frames = [f for f in extracted_trace if "web_application" in f.filename]
            if app_frames:
                app_trace = "".join(traceback.format_list(app_frames))
            else:
                app_trace = "No web_application frames found in stack."
        except Exception as e:
            app_trace = f"Failed to capture trace: {e}"

        is_disconnect = any(err in error_msg for err in disconnect_errors)
        
        if is_disconnect:
            # Toning down the level for expected pre-ping disconnects, but keeping high detail
            log_fn = app.logger.warning
            app.logger.warning(
                f"Detected database connection issue: {error_msg}. Forcing connection discard.\n"
                f"Context: URL={last_url}, User={last_user}\n"
                f"Last SQL on this connection: {last_sql}"
            )
            exception_context.is_disconnect = True
        else:
            log_fn = app.logger.error

        log_fn(
            "==================== DATABASE ERROR DIAGNOSTICS ====================\n"
            f"Error Status: {error_msg}\n"
            f"Last Request URL: {last_url}\n"
            f"Last User ID: {last_user}\n"
            f"Failing/Last SQL: {last_sql}\n"
            f"SQL Params: {last_params}\n"
            "-------------------- APPLICATION TRACE --------------------\n"
            f"{app_trace}\n"
            "-------------------- FULL TRACEBACK --------------------\n",
            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():
            result = 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_class)


    # 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)

    # Test database connection
    with app.app_context():
        test_database_connection(app)

    @app.context_processor
    def inject_global_variables():
        return {
            "user_endpoints": USER_ENDPOINTS,
        }

    # Import blueprints
    from web_application.admin.bolum.routes import ad_bolum
    from web_application.admin.hat.routes import ad_hat
    from web_application.admin.is_emri.routes import ad_is_emri
    from web_application.admin.kullanici.routes import ad_kullanici
    from web_application.admin.musteri.routes import ad_musteri
    from web_application.admin.urun.routes import ad_urun
    from web_application.api.routes import api_bp
    from web_application.kullanici.routes import kullanici
    from web_application.site.ana.routes import ana
    from web_application.site.hat.routes import st_hat

    # Register blueprints
    app.register_blueprint(api_bp)
    app.register_blueprint(ad_bolum)
    app.register_blueprint(ad_kullanici)
    app.register_blueprint(ad_urun)
    app.register_blueprint(ad_musteri)
    app.register_blueprint(ad_is_emri)
    app.register_blueprint(ad_hat)
    app.register_blueprint(ana)
    app.register_blueprint(st_hat)
    app.register_blueprint(kullanici)

    app.logger.info("All blueprints registered successfully")
    app.logger.info("Application initialization complete")

    return app
