"""Flask routes for benchmark web interface."""

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

from .benchmark_service import benchmark_service
from ...web.utils.templates import render_template_with_defaults
from ...web.services.settings_manager import SettingsManager
from ...utilities.db_utils import get_db_session, get_db_setting

# Create blueprint for benchmark routes
benchmark_bp = Blueprint("benchmark", __name__, url_prefix="/benchmark")


@benchmark_bp.route("/")
def index():
    """Benchmark dashboard page."""
    # Load evaluation settings from database
    eval_settings = {
        "evaluation_provider": get_db_setting(
            "benchmark.evaluation.provider", "openai_endpoint"
        ),
        "evaluation_model": get_db_setting("benchmark.evaluation.model", ""),
        "evaluation_endpoint_url": get_db_setting(
            "benchmark.evaluation.endpoint_url", ""
        ),
        "evaluation_temperature": get_db_setting(
            "benchmark.evaluation.temperature", 0
        ),
    }

    return render_template_with_defaults(
        "pages/benchmark.html", eval_settings=eval_settings
    )


@benchmark_bp.route("/results")
def results():
    """Benchmark results history page."""
    return render_template_with_defaults("pages/benchmark_results.html")


@benchmark_bp.route("/api/start", methods=["POST"])
def start_benchmark():
    """Start a new benchmark run."""
    try:
        data = request.get_json()

        if not data:
            return jsonify({"error": "No data provided"}), 400

        # Extract configuration
        run_name = data.get("run_name")

        # Get search config from database instead of request
        from ...web.services.settings_manager import SettingsManager
        from ...utilities.db_utils import get_db_session

        session = get_db_session()
        settings_manager = SettingsManager(db_session=session)

        # Build search config from database settings
        search_config = {
            "iterations": int(
                settings_manager.get_setting("search.iterations", 8)
            ),
            "questions_per_iteration": int(
                settings_manager.get_setting(
                    "search.questions_per_iteration", 5
                )
            ),
            "search_tool": settings_manager.get_setting(
                "search.tool", "searxng"
            ),
            "search_strategy": settings_manager.get_setting(
                "search.search_strategy", "focused_iteration"
            ),
            "model_name": settings_manager.get_setting("llm.model"),
            "provider": settings_manager.get_setting("llm.provider"),
            "temperature": float(
                settings_manager.get_setting("llm.temperature", 0.7)
            ),
        }

        # Add provider-specific settings
        provider = search_config.get("provider")
        if provider == "openai_endpoint":
            search_config["openai_endpoint_url"] = settings_manager.get_setting(
                "llm.openai_endpoint.url"
            )
            search_config["openai_endpoint_api_key"] = (
                settings_manager.get_setting("llm.openai_endpoint.api_key")
            )
        elif provider == "openai":
            search_config["openai_api_key"] = settings_manager.get_setting(
                "llm.openai.api_key"
            )
        elif provider == "anthropic":
            search_config["anthropic_api_key"] = settings_manager.get_setting(
                "llm.anthropic.api_key"
            )

        # Get evaluation config from database settings or request
        if "evaluation_config" in data:
            evaluation_config = data["evaluation_config"]
        else:
            # Read evaluation config from database settings
            evaluation_provider = settings_manager.get_setting(
                "benchmark.evaluation.provider", "openai_endpoint"
            )
            evaluation_model = settings_manager.get_setting(
                "benchmark.evaluation.model", "anthropic/claude-3.7-sonnet"
            )
            evaluation_temperature = float(
                settings_manager.get_setting(
                    "benchmark.evaluation.temperature", 0
                )
            )

            evaluation_config = {
                "provider": evaluation_provider,
                "model_name": evaluation_model,
                "temperature": evaluation_temperature,
            }

            # Add provider-specific settings for evaluation
            if evaluation_provider == "openai_endpoint":
                evaluation_config["openai_endpoint_url"] = (
                    settings_manager.get_setting(
                        "benchmark.evaluation.endpoint_url",
                        "https://openrouter.ai/api/v1",
                    )
                )
                evaluation_config["openai_endpoint_api_key"] = (
                    settings_manager.get_setting("llm.openai_endpoint.api_key")
                )
            elif evaluation_provider == "openai":
                evaluation_config["openai_api_key"] = (
                    settings_manager.get_setting("llm.openai.api_key")
                )
            elif evaluation_provider == "anthropic":
                evaluation_config["anthropic_api_key"] = (
                    settings_manager.get_setting("llm.anthropic.api_key")
                )
        datasets_config = data.get("datasets_config", {})

        # Close database session
        session.close()

        # Validate datasets config
        if not datasets_config or not any(
            config.get("count", 0) > 0 for config in datasets_config.values()
        ):
            return jsonify(
                {
                    "error": "At least one dataset with count > 0 must be specified"
                }
            ), 400

        # Create benchmark run
        benchmark_run_id = benchmark_service.create_benchmark_run(
            run_name=run_name,
            search_config=search_config,
            evaluation_config=evaluation_config,
            datasets_config=datasets_config,
        )

        # Start benchmark
        success = benchmark_service.start_benchmark(benchmark_run_id)

        if success:
            return jsonify(
                {
                    "success": True,
                    "benchmark_run_id": benchmark_run_id,
                    "message": "Benchmark started successfully",
                }
            )
        else:
            return jsonify(
                {"success": False, "error": "Failed to start benchmark"}
            ), 500

    except Exception:
        logger.exception("Error starting benchmark")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@benchmark_bp.route("/api/running", methods=["GET"])
def get_running_benchmark():
    """Check if there's a running benchmark and return its ID."""
    try:
        from ...utilities.db_utils import get_db_session
        from ..models.benchmark_models import BenchmarkRun, BenchmarkStatus

        session = get_db_session()

        # Find any benchmark that's currently running
        running_benchmark = (
            session.query(BenchmarkRun)
            .filter(BenchmarkRun.status == BenchmarkStatus.IN_PROGRESS)
            .order_by(BenchmarkRun.created_at.desc())
            .first()
        )

        session.close()

        if running_benchmark:
            return jsonify(
                {
                    "success": True,
                    "benchmark_run_id": running_benchmark.id,
                    "run_name": running_benchmark.run_name,
                    "total_examples": running_benchmark.total_examples,
                    "completed_examples": running_benchmark.completed_examples,
                }
            )
        else:
            return jsonify(
                {"success": False, "message": "No running benchmark found"}
            )

    except Exception:
        logger.exception("Error checking for running benchmark")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@benchmark_bp.route("/api/status/<int:benchmark_run_id>", methods=["GET"])
def get_benchmark_status(benchmark_run_id: int):
    """Get status of a benchmark run."""
    try:
        status = benchmark_service.get_benchmark_status(benchmark_run_id)

        if status:
            return jsonify({"success": True, "status": status})
        else:
            return jsonify(
                {"success": False, "error": "Benchmark run not found"}
            ), 404

    except Exception:
        logger.exception("Error getting benchmark status")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@benchmark_bp.route("/api/cancel/<int:benchmark_run_id>", methods=["POST"])
def cancel_benchmark(benchmark_run_id: int):
    """Cancel a running benchmark."""
    try:
        success = benchmark_service.cancel_benchmark(benchmark_run_id)

        if success:
            return jsonify(
                {"success": True, "message": "Benchmark cancelled successfully"}
            )
        else:
            return jsonify(
                {"success": False, "error": "Failed to cancel benchmark"}
            ), 500

    except Exception:
        logger.exception("Error cancelling benchmark")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@benchmark_bp.route("/api/history", methods=["GET"])
def get_benchmark_history():
    """Get list of recent benchmark runs."""
    try:
        from ...utilities.db_utils import get_db_session
        from ..models.benchmark_models import BenchmarkRun

        session = get_db_session()

        # Get all benchmark runs (completed, failed, cancelled, or in-progress)
        runs = (
            session.query(BenchmarkRun)
            .order_by(BenchmarkRun.created_at.desc())
            .limit(50)
            .all()
        )

        # Format runs for display
        formatted_runs = []
        for run in runs:
            # Calculate average processing time from results
            avg_processing_time = None
            avg_search_results = None
            try:
                from ..models.benchmark_models import BenchmarkResult
                from sqlalchemy import func

                avg_result = (
                    session.query(func.avg(BenchmarkResult.processing_time))
                    .filter(
                        BenchmarkResult.benchmark_run_id == run.id,
                        BenchmarkResult.processing_time.isnot(None),
                        BenchmarkResult.processing_time > 0,
                    )
                    .scalar()
                )

                if avg_result:
                    avg_processing_time = float(avg_result)
            except Exception as e:
                logger.warning(
                    f"Error calculating avg processing time for run {run.id}: {e}"
                )

            # Calculate average search results and total search requests from metrics
            total_search_requests = None
            try:
                from ...metrics.search_tracker import get_search_tracker
                from ...metrics.db_models import SearchCall

                # Get all results for this run to find research_ids
                results = (
                    session.query(BenchmarkResult)
                    .filter(BenchmarkResult.benchmark_run_id == run.id)
                    .all()
                )

                research_ids = [r.research_id for r in results if r.research_id]

                if research_ids:
                    tracker = get_search_tracker()
                    with tracker.db.get_session() as metric_session:
                        # Get all search calls for these research_ids
                        search_calls = (
                            metric_session.query(SearchCall)
                            .filter(SearchCall.research_id.in_(research_ids))
                            .all()
                        )

                        # Group by research_id and calculate metrics per research session
                        research_results = {}
                        research_requests = {}

                        for call in search_calls:
                            if call.research_id:
                                if call.research_id not in research_results:
                                    research_results[call.research_id] = 0
                                    research_requests[call.research_id] = 0
                                research_results[call.research_id] += (
                                    call.results_count or 0
                                )
                                research_requests[call.research_id] += 1

                        # Calculate averages across research sessions
                        if research_results:
                            total_results = sum(research_results.values())
                            avg_search_results = total_results / len(
                                research_results
                            )

                            total_requests = sum(research_requests.values())
                            total_search_requests = total_requests / len(
                                research_requests
                            )

            except Exception as e:
                logger.warning(
                    f"Error calculating search metrics for run {run.id}: {e}"
                )

            formatted_runs.append(
                {
                    "id": run.id,
                    "run_name": run.run_name or f"Benchmark #{run.id}",
                    "created_at": run.created_at.isoformat(),
                    "total_examples": run.total_examples,
                    "completed_examples": run.completed_examples,
                    "overall_accuracy": run.overall_accuracy,
                    "status": run.status.value,
                    "search_config": run.search_config,
                    "evaluation_config": run.evaluation_config,
                    "datasets_config": run.datasets_config,
                    "avg_processing_time": avg_processing_time,
                    "avg_search_results": avg_search_results,
                    "total_search_requests": total_search_requests,
                }
            )

        session.close()

        return jsonify({"success": True, "runs": formatted_runs})

    except Exception:
        logger.exception("Error getting benchmark history")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@benchmark_bp.route("/api/results/<int:benchmark_run_id>", methods=["GET"])
def get_benchmark_results(benchmark_run_id: int):
    """Get detailed results for a benchmark run."""
    try:
        from ...utilities.db_utils import get_db_session
        from ..models.benchmark_models import BenchmarkResult

        logger.info(f"Getting results for benchmark {benchmark_run_id}")
        session = get_db_session()

        # Get recent results (limit to last 10)
        limit = int(request.args.get("limit", 10))

        results = (
            session.query(BenchmarkResult)
            .filter(BenchmarkResult.benchmark_run_id == benchmark_run_id)
            # Temporarily show all results including pending evaluations
            # .filter(
            #     BenchmarkResult.is_correct.isnot(None)
            # )  # Only completed evaluations
            .order_by(BenchmarkResult.id.desc())  # Most recent first
            .limit(limit)
            .all()
        )

        logger.info(f"Found {len(results)} results")

        # Build a map of research_id to total search results
        search_results_by_research_id = {}
        try:
            from ...metrics.search_tracker import get_search_tracker
            from ...metrics.db_models import SearchCall

            tracker = get_search_tracker()

            # Get all unique research_ids from our results
            research_ids = [r.research_id for r in results if r.research_id]

            if research_ids:
                with tracker.db.get_session() as metric_session:
                    # Get all search calls for these research_ids
                    all_search_calls = (
                        metric_session.query(SearchCall)
                        .filter(SearchCall.research_id.in_(research_ids))
                        .all()
                    )

                    # Group search results by research_id
                    for call in all_search_calls:
                        if call.research_id:
                            if (
                                call.research_id
                                not in search_results_by_research_id
                            ):
                                search_results_by_research_id[
                                    call.research_id
                                ] = 0
                            search_results_by_research_id[call.research_id] += (
                                call.results_count or 0
                            )

                    logger.info(
                        f"Found search metrics for {len(search_results_by_research_id)} research IDs from {len(all_search_calls)} total search calls"
                    )
                    logger.debug(
                        f"Research IDs from results: {research_ids[:5] if len(research_ids) > 5 else research_ids}"
                    )
                    logger.debug(
                        f"Search results by research_id: {dict(list(search_results_by_research_id.items())[:5])}"
                    )
        except Exception:
            logger.exception(
                f"Error getting search metrics for benchmark {benchmark_run_id}"
            )

        # Format results for UI display
        formatted_results = []
        for result in results:
            # Get search result count using research_id
            search_result_count = 0

            try:
                if (
                    result.research_id
                    and result.research_id in search_results_by_research_id
                ):
                    search_result_count = search_results_by_research_id[
                        result.research_id
                    ]
                    logger.debug(
                        f"Found {search_result_count} search results for research_id {result.research_id}"
                    )

            except Exception:
                logger.exception(
                    f"Error getting search results for result {result.example_id}"
                )

            # Fallback to sources if available and we didn't find metrics
            if search_result_count == 0 and result.sources:
                try:
                    if isinstance(result.sources, list):
                        search_result_count = len(result.sources)
                    elif (
                        isinstance(result.sources, dict)
                        and "all_links_of_system" in result.sources
                    ):
                        search_result_count = len(
                            result.sources["all_links_of_system"]
                        )
                except:
                    pass

            formatted_results.append(
                {
                    "example_id": result.example_id,
                    "dataset_type": result.dataset_type.value,
                    "question": result.question,
                    "correct_answer": result.correct_answer,
                    "model_answer": result.extracted_answer,
                    "full_response": result.response,
                    "is_correct": result.is_correct,
                    "confidence": result.confidence,
                    "grader_response": result.grader_response,
                    "processing_time": result.processing_time,
                    "search_result_count": search_result_count,
                    "sources": result.sources,
                    "completed_at": result.completed_at.isoformat()
                    if result.completed_at
                    else None,
                }
            )

        session.close()

        return jsonify({"success": True, "results": formatted_results})

    except Exception:
        logger.exception("Error getting benchmark results")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@benchmark_bp.route("/api/configs", methods=["GET"])
def get_saved_configs():
    """Get list of saved benchmark configurations."""
    try:
        # TODO: Implement saved configs retrieval from database
        # For now return default configs
        default_configs = [
            {
                "id": 1,
                "name": "Quick Test",
                "description": "Fast benchmark with minimal examples",
                "search_config": {
                    "iterations": 3,
                    "questions_per_iteration": 3,
                    "search_tool": "searxng",
                    "search_strategy": "focused_iteration",
                },
                "datasets_config": {
                    "simpleqa": {"count": 10},
                    "browsecomp": {"count": 5},
                },
            },
            {
                "id": 2,
                "name": "Standard Evaluation",
                "description": "Comprehensive benchmark with standard settings",
                "search_config": {
                    "iterations": 8,
                    "questions_per_iteration": 5,
                    "search_tool": "searxng",
                    "search_strategy": "focused_iteration",
                },
                "datasets_config": {
                    "simpleqa": {"count": 50},
                    "browsecomp": {"count": 25},
                },
            },
        ]

        return jsonify({"success": True, "configs": default_configs})

    except Exception:
        logger.exception("Error getting saved configs")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@benchmark_bp.route("/api/start-simple", methods=["POST"])
def start_benchmark_simple():
    """Start a benchmark using current database settings."""
    try:
        data = request.get_json()
        datasets_config = data.get("datasets_config", {})

        # Validate datasets
        if not datasets_config or not any(
            config.get("count", 0) > 0 for config in datasets_config.values()
        ):
            return jsonify(
                {
                    "error": "At least one dataset with count > 0 must be specified"
                }
            ), 400

        # Get current settings from database
        session = get_db_session()
        settings_manager = SettingsManager(db_session=session)

        # Build search config from database settings
        search_config = {
            "iterations": int(
                settings_manager.get_setting("search.iterations", 8)
            ),
            "questions_per_iteration": int(
                settings_manager.get_setting(
                    "search.questions_per_iteration", 5
                )
            ),
            "search_tool": settings_manager.get_setting(
                "search.tool", "searxng"
            ),
            "search_strategy": settings_manager.get_setting(
                "search.search_strategy", "focused_iteration"
            ),
            "model_name": settings_manager.get_setting("llm.model"),
            "provider": settings_manager.get_setting("llm.provider"),
            "temperature": float(
                settings_manager.get_setting("llm.temperature", 0.7)
            ),
        }

        # Add provider-specific settings
        provider = search_config.get("provider")
        if provider == "openai_endpoint":
            search_config["openai_endpoint_url"] = settings_manager.get_setting(
                "llm.openai_endpoint.url"
            )
            search_config["openai_endpoint_api_key"] = (
                settings_manager.get_setting("llm.openai_endpoint.api_key")
            )
        elif provider == "openai":
            search_config["openai_api_key"] = settings_manager.get_setting(
                "llm.openai.api_key"
            )
        elif provider == "anthropic":
            search_config["anthropic_api_key"] = settings_manager.get_setting(
                "llm.anthropic.api_key"
            )

        # Read evaluation config from database settings
        evaluation_provider = settings_manager.get_setting(
            "benchmark.evaluation.provider", "openai_endpoint"
        )
        evaluation_model = settings_manager.get_setting(
            "benchmark.evaluation.model", "anthropic/claude-3.7-sonnet"
        )
        evaluation_temperature = float(
            settings_manager.get_setting("benchmark.evaluation.temperature", 0)
        )

        evaluation_config = {
            "provider": evaluation_provider,
            "model_name": evaluation_model,
            "temperature": evaluation_temperature,
        }

        # Add provider-specific settings for evaluation
        if evaluation_provider == "openai_endpoint":
            evaluation_config["openai_endpoint_url"] = (
                settings_manager.get_setting(
                    "benchmark.evaluation.endpoint_url",
                    "https://openrouter.ai/api/v1",
                )
            )
            evaluation_config["openai_endpoint_api_key"] = (
                settings_manager.get_setting("llm.openai_endpoint.api_key")
            )
        elif evaluation_provider == "openai":
            evaluation_config["openai_api_key"] = settings_manager.get_setting(
                "llm.openai.api_key"
            )
        elif evaluation_provider == "anthropic":
            evaluation_config["anthropic_api_key"] = (
                settings_manager.get_setting("llm.anthropic.api_key")
            )

        session.close()

        # Create and start benchmark
        benchmark_run_id = benchmark_service.create_benchmark_run(
            run_name=f"Quick Benchmark - {data.get('run_name', '')}",
            search_config=search_config,
            evaluation_config=evaluation_config,
            datasets_config=datasets_config,
        )

        success = benchmark_service.start_benchmark(benchmark_run_id)

        if success:
            return jsonify(
                {
                    "success": True,
                    "benchmark_run_id": benchmark_run_id,
                    "message": "Benchmark started with current settings",
                }
            )
        else:
            return jsonify(
                {"success": False, "error": "Failed to start benchmark"}
            ), 500

    except Exception:
        logger.exception("Error starting simple benchmark")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@benchmark_bp.route("/api/validate-config", methods=["POST"])
def validate_config():
    """Validate a benchmark configuration."""
    try:
        data = request.get_json()

        if not data:
            return jsonify({"valid": False, "errors": ["No data provided"]})

        errors = []

        # Validate search config
        search_config = data.get("search_config", {})
        if not search_config.get("search_tool"):
            errors.append("Search tool is required")
        if not search_config.get("search_strategy"):
            errors.append("Search strategy is required")

        # Validate datasets config
        datasets_config = data.get("datasets_config", {})
        if not datasets_config:
            errors.append("At least one dataset must be configured")

        total_examples = sum(
            config.get("count", 0) for config in datasets_config.values()
        )
        if total_examples == 0:
            errors.append("Total examples must be greater than 0")

        if total_examples > 1000:
            errors.append(
                "Total examples should not exceed 1000 for web interface"
            )

        return jsonify(
            {
                "valid": len(errors) == 0,
                "errors": errors,
                "total_examples": total_examples,
            }
        )

    except Exception:
        logger.exception("Error validating config")
        return jsonify(
            {"valid": False, "errors": ["An internal error has occurred."]}
        ), 500


@benchmark_bp.route("/api/search-quality", methods=["GET"])
def get_search_quality():
    """Get current search quality metrics from rate limiting tracker."""
    try:
        from ...web_search_engines.rate_limiting import get_tracker

        tracker = get_tracker()
        quality_stats = tracker.get_search_quality_stats()

        return jsonify(
            {
                "success": True,
                "search_quality": quality_stats,
                "timestamp": time.time(),
            }
        )

    except Exception:
        logger.exception("Error getting search quality")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@benchmark_bp.route("/api/delete/<int:benchmark_run_id>", methods=["DELETE"])
def delete_benchmark_run(benchmark_run_id: int):
    """Delete a benchmark run and all its results."""
    try:
        from ...utilities.db_utils import get_db_session
        from ..models.benchmark_models import (
            BenchmarkRun,
            BenchmarkResult,
            BenchmarkProgress,
        )

        session = get_db_session()

        # Check if benchmark run exists
        benchmark_run = (
            session.query(BenchmarkRun)
            .filter(BenchmarkRun.id == benchmark_run_id)
            .first()
        )

        if not benchmark_run:
            session.close()
            return jsonify(
                {"success": False, "error": "Benchmark run not found"}
            ), 404

        # Prevent deletion of running benchmarks
        if benchmark_run.status.value == "in_progress":
            session.close()
            return jsonify(
                {
                    "success": False,
                    "error": "Cannot delete a running benchmark. Cancel it first.",
                }
            ), 400

        # Delete related records (cascade should handle this, but being explicit)
        session.query(BenchmarkResult).filter(
            BenchmarkResult.benchmark_run_id == benchmark_run_id
        ).delete()

        session.query(BenchmarkProgress).filter(
            BenchmarkProgress.benchmark_run_id == benchmark_run_id
        ).delete()

        # Delete the benchmark run
        session.delete(benchmark_run)
        session.commit()
        session.close()

        logger.info(f"Deleted benchmark run {benchmark_run_id}")
        return jsonify(
            {
                "success": True,
                "message": f"Benchmark run {benchmark_run_id} deleted successfully",
            }
        )

    except Exception:
        logger.exception(f"Error deleting benchmark run {benchmark_run_id}")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500
