import logging
import os
from importlib import resources as importlib_resources

from flask import (
    Flask,
    jsonify,
    make_response,
    redirect,
    request,
    send_from_directory,
    url_for,
)
from flask_socketio import SocketIO
from flask_wtf.csrf import CSRFProtect

from .models.database import DB_PATH, init_db

# Initialize logger
logger = logging.getLogger(__name__)


def create_app():
    """
    Create and configure the Flask application.

    Returns:
        tuple: (app, socketio) - The configured Flask app and SocketIO instance
    """
    # Configure logging
    logging.basicConfig(level=logging.INFO)

    # Set Werkzeug logger to WARNING level to suppress Socket.IO polling logs
    logging.getLogger("werkzeug").setLevel(logging.WARNING)

    try:
        # Get directories based on package installation
        PACKAGE_DIR = importlib_resources.files("local_deep_research") / "web"
        with importlib_resources.as_file(PACKAGE_DIR) as package_dir:
            STATIC_DIR = (package_dir / "static").as_posix()
            TEMPLATE_DIR = (package_dir / "templates").as_posix()

        # Initialize Flask app with package directories
        app = Flask(__name__, static_folder=STATIC_DIR, template_folder=TEMPLATE_DIR)
        print(f"Using package static path: {STATIC_DIR}")
        print(f"Using package template path: {TEMPLATE_DIR}")
    except Exception as e:
        # Fallback for development
        print(f"Package directories not found, using fallback paths: {str(e)}")
        app = Flask(
            __name__,
            static_folder=os.path.abspath("static"),
            template_folder=os.path.abspath("templates"),
        )

    # App configuration
    app.config["SECRET_KEY"] = "deep-research-secret-key"

    # Initialize CSRF protection
    csrf = CSRFProtect(app)
    # Exempt Socket.IO from CSRF protection
    csrf.exempt("research.socket_io")

    # Database configuration - Use unified ldr.db from the database module
    db_path = DB_PATH
    app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{db_path}"
    logger.info(f"Using database at {db_path}")
    app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
    app.config["SQLALCHEMY_ECHO"] = False

    # Initialize extensions
    socketio = SocketIO(
        app,
        cors_allowed_origins="*",
        async_mode="threading",
        path="/research/socket.io",
        logger=False,
        engineio_logger=False,
        ping_timeout=20,
        ping_interval=5,
    )

    # Initialize the database
    create_database(app)
    init_db()

    # Register socket service
    from .services.socket_service import set_socketio

    set_socketio(socketio)

    # Apply middleware
    apply_middleware(app)

    # Register blueprints
    register_blueprints(app)

    # Register error handlers
    register_error_handlers(app)

    # Register socket event handlers
    register_socket_events(socketio)

    return app, socketio


def apply_middleware(app):
    """Apply middleware to the Flask app."""

    # Add Content Security Policy headers to allow Socket.IO to function
    @app.after_request
    def add_security_headers(response):
        # Define a permissive CSP for development that allows Socket.IO to function
        csp = (
            "default-src 'self'; "
            "connect-src 'self' ws: wss: http: https:; "
            "script-src 'self' 'unsafe-inline' 'unsafe-eval' cdnjs.cloudflare.com cdn.jsdelivr.net unpkg.com; "
            "style-src 'self' 'unsafe-inline' cdnjs.cloudflare.com; "
            "font-src 'self' cdnjs.cloudflare.com; "
            "img-src 'self' data:; "
            "worker-src blob:; "
            "frame-src 'self';"
        )

        response.headers["Content-Security-Policy"] = csp
        response.headers["X-Content-Security-Policy"] = csp

        # Add CORS headers for API requests
        if request.path.startswith("/api/"):
            response.headers["Access-Control-Allow-Origin"] = "*"
            response.headers["Access-Control-Allow-Methods"] = (
                "GET, POST, DELETE, OPTIONS"
            )
            response.headers["Access-Control-Allow-Headers"] = "Content-Type"

        return response

    # Add a middleware layer to handle abrupt disconnections
    @app.before_request
    def handle_websocket_requests():
        if request.path.startswith("/research/socket.io"):
            try:
                if not request.environ.get("werkzeug.socket"):
                    return
            except Exception as e:
                print(f"WebSocket preprocessing error: {e}")
                # Return empty response to prevent further processing
                return "", 200


def register_blueprints(app):
    """Register blueprints with the Flask app."""

    # Import blueprints
    from .routes.api_routes import api_bp  # Import the API blueprint
    from .routes.history_routes import history_bp
    from .routes.research_routes import research_bp
    from .routes.settings_routes import settings_bp

    # Register blueprints
    app.register_blueprint(research_bp)
    app.register_blueprint(history_bp, url_prefix="/research/api")
    app.register_blueprint(settings_bp)
    app.register_blueprint(
        api_bp, url_prefix="/research/api"
    )  # Register API blueprint with prefix

    # Add root route redirect
    @app.route("/")
    def root_index():
        return redirect(url_for("research.index"))

    # Add favicon route
    @app.route("/favicon.ico")
    def favicon():
        return send_from_directory(
            app.static_folder, "favicon.ico", mimetype="image/x-icon"
        )

    # Add static route at the app level for compatibility
    @app.route("/static/<path:path>")
    def app_serve_static(path):
        return send_from_directory(app.static_folder, path)


def register_error_handlers(app):
    """Register error handlers with the Flask app."""

    @app.errorhandler(404)
    def not_found(error):
        return make_response(jsonify({"error": "Not found"}), 404)

    @app.errorhandler(500)
    def server_error(error):
        return make_response(jsonify({"error": "Server error"}), 500)


def register_socket_events(socketio):
    """Register Socket.IO event handlers."""

    from .routes.research_routes import get_globals
    from .services.socket_service import (
        handle_connect,
        handle_default_error,
        handle_disconnect,
        handle_socket_error,
        handle_subscribe,
    )

    @socketio.on("connect")
    def on_connect():
        handle_connect(request)

    @socketio.on("disconnect")
    def on_disconnect():
        handle_disconnect(request)

    @socketio.on("subscribe_to_research")
    def on_subscribe(data):
        globals_dict = get_globals()
        active_research = globals_dict.get("active_research", {})
        handle_subscribe(data, request, active_research)

    @socketio.on_error
    def on_error(e):
        return handle_socket_error(e)

    @socketio.on_error_default
    def on_default_error(e):
        return handle_default_error(e)


def create_database(app):
    """
    Create the database and tables for the application.
    """
    from sqlalchemy import create_engine
    from sqlalchemy.orm import scoped_session, sessionmaker

    from .database.migrations import run_migrations
    from .database.models import Base

    # Configure SQLite to use URI mode, which allows for relative file paths
    engine = create_engine(
        app.config["SQLALCHEMY_DATABASE_URI"],
        echo=app.config.get("SQLALCHEMY_ECHO", False),
        connect_args={"check_same_thread": False},
    )

    app.engine = engine

    # Create all tables
    Base.metadata.create_all(engine)

    # Configure session factory
    session_factory = sessionmaker(bind=engine, autocommit=False, autoflush=False)
    app.db_session = scoped_session(session_factory)

    # Run migrations and setup predefined settings
    run_migrations(engine, app.db_session)

    # Add teardown context
    @app.teardown_appcontext
    def remove_session(exception=None):
        app.db_session.remove()
