import logging
import os
from importlib import resources as importlib_resources

from flask import (
    Flask,
    jsonify,
    make_response,
    request,
    send_from_directory,
)
from flask_wtf.csrf import CSRFProtect
from loguru import logger

from ..utilities.log_utils import InterceptHandler
from .models.database import DB_PATH, init_db
from .services.socket_service import SocketIOService


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

    Returns:
        tuple: (app, socketio) - The configured Flask app and SocketIO instance
    """
    # Set Werkzeug logger to WARNING level to suppress Socket.IO polling logs
    logging.getLogger("werkzeug").setLevel(logging.WARNING)
    logging.getLogger("werkzeug").addHandler(InterceptHandler())

    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
        )
        logger.debug(f"Using package static path: {STATIC_DIR}")
        logger.debug(f"Using package template path: {TEMPLATE_DIR}")
    except Exception:
        # Fallback for development
        logger.exception("Package directories not found, using fallback paths")
        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")

    # Disable CSRF for API routes
    @app.before_request
    def disable_csrf_for_api():
        if (
            request.path.startswith("/api/v1/")
            or request.path.startswith("/research/api/")
            or request.path.startswith("/benchmark/api/")
        ):
            csrf.protect = lambda: None

    # 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 the database
    create_database(app)
    init_db()

    # Register socket service
    socket_service = SocketIOService(app=app)

    # Apply middleware
    apply_middleware(app)

    # Register blueprints
    register_blueprints(app)

    # Register error handlers
    register_error_handlers(app)

    return app, socket_service


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/") or request.path.startswith(
            "/research/api/"
        ):
            response.headers["Access-Control-Allow-Origin"] = "*"
            response.headers["Access-Control-Allow-Methods"] = (
                "GET, POST, PUT, DELETE, OPTIONS"
            )
            response.headers["Access-Control-Allow-Headers"] = (
                "Content-Type, Authorization, X-Requested-With, X-HTTP-Method-Override"
            )
            response.headers["Access-Control-Allow-Credentials"] = "true"
            response.headers["Access-Control-Max-Age"] = "3600"

        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:
                logger.exception("WebSocket preprocessing error")
                # Return empty response to prevent further processing
                return "", 200

    # Handle CORS preflight requests
    @app.before_request
    def handle_preflight():
        if request.method == "OPTIONS":
            if request.path.startswith("/api/") or request.path.startswith(
                "/research/api/"
            ):
                response = app.make_default_options_response()
                response.headers["Access-Control-Allow-Origin"] = "*"
                response.headers["Access-Control-Allow-Methods"] = (
                    "GET, POST, PUT, DELETE, OPTIONS"
                )
                response.headers["Access-Control-Allow-Headers"] = (
                    "Content-Type, Authorization, X-Requested-With, X-HTTP-Method-Override"
                )
                response.headers["Access-Control-Allow-Credentials"] = "true"
                response.headers["Access-Control-Max-Age"] = "3600"
                return response


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

    # Import blueprints
    from .api import api_blueprint  # Import the API blueprint
    from .routes.api_routes import api_bp  # Import the API blueprint
    from .routes.history_routes import history_bp
    from .routes.metrics_routes import metrics_bp
    from .routes.research_routes import research_bp
    from .routes.settings_routes import settings_bp
    from ..benchmarks.web_api.benchmark_routes import benchmark_bp

    # Add root route
    @app.route("/")
    def index():
        """Root route - serve the research page directly"""
        from .utils.templates import render_template_with_defaults
        from ..utilities.db_utils import get_db_setting

        # Load current settings from database
        settings = {
            "llm_provider": get_db_setting("llm.provider", "ollama"),
            "llm_model": get_db_setting("llm.model", ""),
            "llm_openai_endpoint_url": get_db_setting(
                "llm.openai_endpoint.url", ""
            ),
            "search_tool": get_db_setting("search.tool", ""),
            "search_iterations": get_db_setting("search.iterations", 2),
            "search_questions_per_iteration": get_db_setting(
                "search.questions_per_iteration", 3
            ),
        }

        # Debug logging
        logger.debug(f"Settings loaded: {settings}")

        return render_template_with_defaults(
            "pages/research.html", settings=settings
        )

    # Register blueprints
    app.register_blueprint(research_bp)
    app.register_blueprint(history_bp)  # Already has url_prefix="/history"
    app.register_blueprint(metrics_bp)
    app.register_blueprint(settings_bp)  # Already has url_prefix="/settings"
    app.register_blueprint(
        api_bp, url_prefix="/research/api"
    )  # Register API blueprint with prefix
    app.register_blueprint(benchmark_bp)  # Register benchmark blueprint

    # Register API v1 blueprint
    app.register_blueprint(api_blueprint)  # Already has url_prefix='/api/v1'

    # After registration, update CSRF exemptions
    if hasattr(app, "extensions") and "csrf" in app.extensions:
        csrf = app.extensions["csrf"]
        # Exempt the API blueprint routes by actual endpoints
        csrf.exempt("api_v1")
        csrf.exempt("api")
        for rule in app.url_map.iter_rules():
            if rule.endpoint and (
                rule.endpoint.startswith("api_v1.")
                or rule.endpoint.startswith("api.")
            ):
                csrf.exempt(rule.endpoint)

    # 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 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()
