"""
REST API for Local Deep Research.
Provides HTTP access to programmatic search and research capabilities.
"""

import time
from functools import wraps
from typing import Dict, Any

from flask import Blueprint, jsonify, request, Response
from loguru import logger

from ..api.research_functions import analyze_documents
from ..database.session_context import get_user_db_session
from ..security.decorators import require_json_body
from ..utilities.db_utils import get_settings_manager
from ..security.rate_limiter import (
    API_RATE_LIMIT_DEFAULT,
    api_rate_limit,
    get_current_username,
)

# Create a blueprint for the API
api_blueprint = Blueprint("api_v1", __name__, url_prefix="/api/v1")


def api_access_control(f):
    """
    Decorator to enforce API access control:
    - Check if user is authenticated
    - Check if API is enabled for the user
    - Pre-cache api_rate_limit on g so the rate limiter avoids a second DB read
    """

    @wraps(f)
    def decorated_function(*args, **kwargs):
        from flask import g

        username = get_current_username()

        if not username:
            return jsonify({"error": "Authentication required"}), 401

        # Read both settings in a single DB session
        api_enabled = True
        with get_user_db_session(username) as db_session:
            if db_session:
                settings_manager = get_settings_manager(db_session, username)
                api_enabled = settings_manager.get_setting(
                    "app.enable_api", True
                )
                # Pre-cache for _get_user_api_rate_limit() to avoid a second DB read
                g._api_rate_limit = settings_manager.get_setting(
                    "app.api_rate_limit", API_RATE_LIMIT_DEFAULT
                )

        if not api_enabled:
            return jsonify({"error": "API access is disabled"}), 403

        return f(*args, **kwargs)

    return decorated_function


@api_blueprint.route("/", methods=["GET"])
@api_access_control
@api_rate_limit
def api_documentation():
    """
    Provide documentation on the available API endpoints.
    """
    api_docs = {
        "api_version": "v1",
        "description": "REST API for Local Deep Research",
        "endpoints": [
            {
                "path": "/api/v1/quick_summary",
                "method": "POST",
                "description": "Generate a quick research summary",
                "parameters": {
                    "query": "Research query (required)",
                    "search_tool": "Search engine to use (optional)",
                    "iterations": "Number of search iterations (optional)",
                    "temperature": "LLM temperature (optional)",
                },
            },
            {
                "path": "/api/v1/generate_report",
                "method": "POST",
                "description": "Generate a comprehensive research report",
                "parameters": {
                    "query": "Research query (required)",
                    "output_file": "Path to save report (optional)",
                    "searches_per_section": "Searches per report section (optional)",
                    "model_name": "LLM model to use (optional)",
                    "temperature": "LLM temperature (optional)",
                },
            },
            {
                "path": "/api/v1/analyze_documents",
                "method": "POST",
                "description": "Search and analyze documents in a local collection",
                "parameters": {
                    "query": "Search query (required)",
                    "collection_name": "Local collection name (required)",
                    "max_results": "Maximum results to return (optional)",
                    "temperature": "LLM temperature (optional)",
                    "force_reindex": "Force collection reindexing (optional)",
                },
            },
        ],
    }

    return jsonify(api_docs)


@api_blueprint.route("/health", methods=["GET"])
def health_check():
    """Simple health check endpoint."""
    return jsonify(
        {"status": "ok", "message": "API is running", "timestamp": time.time()}
    )


@api_blueprint.route("/quick_summary_test", methods=["POST"])
@api_access_control
@api_rate_limit
@require_json_body(error_message="Query parameter is required")
def api_quick_summary_test():
    """Test endpoint using programmatic access with minimal parameters for fast testing."""
    data = request.json
    if "query" not in data:
        return jsonify({"error": "Query parameter is required"}), 400

    query = data.get("query")

    try:
        # Import here to avoid circular imports
        from ..api.research_functions import quick_summary

        logger.info(f"Processing quick_summary_test request: query='{query}'")

        # Use minimal parameters for faster testing
        result = quick_summary(
            query=query,
            search_tool="wikipedia",  # Use fast Wikipedia search for testing
            iterations=1,  # Single iteration for speed
            temperature=0.7,
        )

        return jsonify(result)
    except Exception:
        logger.exception("Error in quick_summary_test API")
        return (
            jsonify(
                {
                    "error": "An internal error has occurred. Please try again later."
                }
            ),
            500,
        )


def _serialize_results(results: Dict[str, Any]) -> Response:
    """
    Converts the results dictionary into a JSON string.

    Args:
        results: The results dictionary.

    Returns:
        The JSON string.

    """
    # The main thing that needs to be handled here is the `Document` instances.
    converted_results = results.copy()
    for finding in converted_results.get("findings", []):
        for i, document in enumerate(finding.get("documents", [])):
            finding["documents"][i] = {
                "metadata": document.metadata,
                "content": document.page_content,
            }

    return jsonify(converted_results)


@api_blueprint.route("/quick_summary", methods=["POST"])
@api_access_control
@api_rate_limit
@require_json_body(error_message="Query parameter is required")
def api_quick_summary():
    """
    Generate a quick research summary via REST API.

    POST /api/v1/quick_summary
    {
        "query": "Advances in fusion energy research",
        "search_tool": "auto",  # Optional: search engine to use
        "iterations": 2,        # Optional: number of search iterations
        "temperature": 0.7      # Optional: LLM temperature
    }
    """
    logger.debug("API quick_summary endpoint called")
    data = request.json
    logger.debug(f"Request data keys: {list(data.keys())}")

    if "query" not in data:
        logger.debug("Missing query parameter")
        return jsonify({"error": "Query parameter is required"}), 400

    # Extract query and validate type
    query = data.get("query")
    if not isinstance(query, str):
        return jsonify({"error": "Query must be a string"}), 400
    params = {k: v for k, v in data.items() if k != "query"}
    logger.debug(
        f"Query length: {len(query) if query else 0}, params keys: {list(params.keys()) if params else 'None'}"
    )

    username = get_current_username()
    if username:
        params["username"] = username

    try:
        # Import here to avoid circular imports
        from ..api.research_functions import quick_summary
        from ..database.session_context import get_user_db_session
        from ..utilities.db_utils import get_settings_manager

        logger.info(
            f"Processing quick_summary request: query='{query}' for user='{username}'"
        )

        # Set reasonable defaults for API use
        params.setdefault("temperature", 0.7)
        params.setdefault("search_tool", "auto")
        params.setdefault("iterations", 1)

        # Get settings snapshot for the user
        if username:
            try:
                logger.debug(f"Getting settings snapshot for user: {username}")
                with get_user_db_session(username) as db_session:
                    if db_session:
                        try:
                            settings_manager = get_settings_manager(
                                db_session, username
                            )
                            all_settings = settings_manager.get_all_settings()
                            # Extract just the values for the settings snapshot
                            settings_snapshot = {}
                            for key, setting in all_settings.items():
                                if (
                                    isinstance(setting, dict)
                                    and "value" in setting
                                ):
                                    settings_snapshot[key] = setting["value"]
                                else:
                                    settings_snapshot[key] = setting
                            params["settings_snapshot"] = settings_snapshot
                            logger.debug(
                                f"Got settings snapshot with {len(settings_snapshot)} settings"
                            )
                        except AttributeError as ae:
                            logger.exception(
                                f"SettingsManager attribute error: {ae}. "
                                f"Type: {type(settings_manager) if 'settings_manager' in locals() else 'Unknown'}"
                            )
                            raise
                    else:
                        logger.warning(
                            f"No database session for user: {username}"
                        )
            except Exception:
                logger.warning("Failed to get settings snapshot")
                # Continue with empty snapshot rather than failing
                params["settings_snapshot"] = {}
        else:
            logger.debug("No username in session, skipping settings snapshot")
            params["settings_snapshot"] = {}

        # Call the actual research function
        result = quick_summary(query, **params)

        return _serialize_results(result)
    except TimeoutError:
        logger.exception("Request timed out")
        return (
            jsonify(
                {
                    "error": "Request timed out. Please try with a simpler query or fewer iterations."
                }
            ),
            504,
        )
    except Exception:
        logger.exception("Error in quick_summary API")
        return (
            jsonify(
                {
                    "error": "An internal error has occurred. Please try again later."
                }
            ),
            500,
        )


@api_blueprint.route("/generate_report", methods=["POST"])
@api_access_control
@api_rate_limit
@require_json_body(error_message="Query parameter is required")
def api_generate_report():
    """
    Generate a comprehensive research report via REST API.

    POST /api/v1/generate_report
    {
        "query": "Impact of climate change on agriculture",
        "output_file": "/path/to/save/report.md",  # Optional
        "searches_per_section": 2,                 # Optional
        "model_name": "gpt-4",                     # Optional
        "temperature": 0.5                         # Optional
    }
    """
    data = request.json
    if "query" not in data:
        return jsonify({"error": "Query parameter is required"}), 400

    query = data.get("query")
    params = {k: v for k, v in data.items() if k != "query"}

    try:
        # Import here to avoid circular imports
        from ..api.research_functions import generate_report

        # Set reasonable defaults for API use
        params.setdefault("searches_per_section", 1)
        params.setdefault("temperature", 0.7)

        logger.info(
            f"Processing generate_report request: query='{query}', params={params}"
        )

        result = generate_report(query, **params)

        # Don't return the full content for large reports
        if (
            result
            and "content" in result
            and isinstance(result["content"], str)
            and len(result["content"]) > 10000
        ):
            # Include a summary of the report content
            content_preview = (
                result["content"][:2000] + "... [Content truncated]"
            )
            result["content"] = content_preview
            result["content_truncated"] = True

        return jsonify(result)
    except TimeoutError:
        logger.exception("Request timed out")
        return (
            jsonify(
                {"error": "Request timed out. Please try with a simpler query."}
            ),
            504,
        )
    except Exception:
        logger.exception("Error in generate_report API")
        return (
            jsonify(
                {
                    "error": "An internal error has occurred. Please try again later."
                }
            ),
            500,
        )


@api_blueprint.route("/analyze_documents", methods=["POST"])
@api_access_control
@api_rate_limit
@require_json_body(
    error_message="Both query and collection_name parameters are required"
)
def api_analyze_documents():
    """
    Search and analyze documents in a local collection via REST API.

    POST /api/v1/analyze_documents
    {
        "query": "neural networks in medicine",
        "collection_name": "my_collection",          # Required: local collection name
        "max_results": 20,                         # Optional: max results to return
        "temperature": 0.7,                        # Optional: LLM temperature
        "force_reindex": false                     # Optional: force reindexing
    }
    """
    data = request.json
    if "query" not in data or "collection_name" not in data:
        return (
            jsonify(
                {
                    "error": "Both query and collection_name parameters are required"
                }
            ),
            400,
        )

    query = data.get("query")
    collection_name = data.get("collection_name")
    params = {
        k: v for k, v in data.items() if k not in ["query", "collection_name"]
    }

    try:
        result = analyze_documents(query, collection_name, **params)
        return jsonify(result)
    except Exception:
        logger.exception("Error in analyze_documents API")
        return (
            jsonify(
                {
                    "error": "An internal error has occurred. Please try again later."
                }
            ),
            500,
        )
