"""
Flask routes for follow-up research functionality.
"""

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

from ..constants import ResearchStatus
from ..exceptions import DuplicateResearchError, SystemAtCapacityError
from ..llm.providers.base import normalize_provider
from .service import FollowUpResearchService
from .models import FollowUpRequest
from ..utilities.url_utils import is_safe_custom_llm_endpoint
from ..security.decorators import require_json_body
from ..web.auth.decorators import login_required
from ..web.auth.password_utils import resolve_user_password

# Create blueprint
followup_bp = Blueprint("followup", __name__, url_prefix="/api/followup")

# NOTE: Routes use session["username"] (not .get()) intentionally.
# @login_required guarantees the key exists; direct access fails fast
# if the decorator is ever removed.


@followup_bp.route("/prepare", methods=["POST"])
@login_required
@require_json_body(error_format="success")
def prepare_followup():
    """
    Prepare a follow-up research by loading parent context.

    Request body:
    {
        "parent_research_id": "uuid",
        "question": "follow-up question"
    }

    Returns:
    {
        "success": true,
        "parent_summary": "...",
        "available_sources": 10,
        "suggested_strategy": "source-based"
    }
    """
    try:
        data = request.get_json()
        parent_id = data.get("parent_research_id")
        question = data.get("question")

        if not parent_id or not question:
            return jsonify(
                {
                    "success": False,
                    "error": "Missing parent_research_id or question",
                }
            ), 400

        # Get username from session
        username = session["username"]

        # Get settings snapshot to use for suggested strategy
        from ..settings.manager import SettingsManager
        from ..database.session_context import get_user_db_session

        with get_user_db_session(username) as db_session:
            settings_manager = SettingsManager(db_session=db_session)
            settings_snapshot = settings_manager.get_all_settings()

        # Get strategy from settings
        strategy_from_settings = settings_snapshot.get(
            "search.search_strategy", {}
        ).get("value", "source-based")

        # Initialize service
        service = FollowUpResearchService(username=username)

        # Load parent context
        parent_data = service.load_parent_research(parent_id)

        if not parent_data:
            logger.warning("Parent research {} not found", parent_id)
            return jsonify(
                {"success": False, "error": "Parent research not found"}
            ), 404

        # Prepare response with parent context summary
        response = {
            "success": True,
            "parent_summary": parent_data.get("query", ""),
            "available_sources": len(parent_data.get("resources", [])),
            "suggested_strategy": strategy_from_settings,  # Use strategy from settings
            "parent_research": {
                "id": parent_id,
                "query": parent_data.get("query", ""),
                "sources_count": len(parent_data.get("resources", [])),
            },
        }

        return jsonify(response)

    except Exception:
        logger.exception("Error preparing follow-up")
        return jsonify(
            {"success": False, "error": "An internal error has occurred."}
        ), 500


@followup_bp.route("/start", methods=["POST"])
@login_required
@require_json_body(error_format="success")
def start_followup():
    """
    Start a follow-up research.

    Request body:
    {
        "parent_research_id": "uuid",
        "question": "follow-up question",
        "strategy": "source-based",  # optional
        "max_iterations": 1,  # optional
        "questions_per_iteration": 3  # optional
    }

    Returns:
    {
        "success": true,
        "research_id": "new-uuid",
        "message": "Follow-up research started"
    }
    """
    try:
        from ..web.services.research_service import (
            start_research_process,
            run_research_process,
        )
        import uuid

        data = request.get_json()

        # Get username from session
        username = session["username"]

        # Get settings snapshot first to use database values
        from ..settings.manager import SettingsManager
        from ..database.session_context import get_user_db_session

        with get_user_db_session(username) as db_session:
            settings_manager = SettingsManager(db_session=db_session)
            settings_snapshot = settings_manager.get_all_settings()

        # Get strategy from settings snapshot, fallback to source-based if not set
        strategy_from_settings = settings_snapshot.get(
            "search.search_strategy", {}
        ).get("value", "source-based")

        # Get iterations and questions from settings snapshot
        iterations_from_settings = settings_snapshot.get(
            "search.iterations", {}
        ).get("value", 1)
        questions_from_settings = settings_snapshot.get(
            "search.questions_per_iteration", {}
        ).get("value", 3)

        # Create follow-up request using settings values
        followup_request = FollowUpRequest(
            parent_research_id=data.get("parent_research_id"),
            question=data.get("question"),
            strategy=strategy_from_settings,  # Use strategy from settings
            max_iterations=iterations_from_settings,  # Use iterations from settings
            questions_per_iteration=questions_from_settings,  # Use questions from settings
        )

        # Initialize service
        service = FollowUpResearchService(username=username)

        # Prepare research parameters
        research_params = service.perform_followup(followup_request)

        logger.info(f"Research params type: {type(research_params)}")
        logger.info(
            f"Research params keys: {research_params.keys() if isinstance(research_params, dict) else 'Not a dict'}"
        )
        logger.info(
            f"Query value: {research_params.get('query') if isinstance(research_params, dict) else 'N/A'}"
        )
        logger.info(
            f"Query type: {type(research_params.get('query')) if isinstance(research_params, dict) else 'N/A'}"
        )

        # Get user password for metrics database access.
        # Shared helper (password_utils) so every research entry point makes
        # the same encryption-aware decision and logs it the same way.
        # Must check BEFORE creating ResearchHistory to avoid orphaned records.
        user_password, session_expired = resolve_user_password(username)

        if session_expired:
            # Use success/error keys to match followup API convention
            # (the followup frontend checks data.success and data.error)
            return jsonify(
                {
                    "success": False,
                    "error": "Your session has expired. Please log out and log back in to start research.",
                }
            ), 401

        # Pre-flight: refuse to spawn a research thread (and create an
        # orphan ResearchHistory row) when llm.model is empty. Mirrors the
        # empty-model check in research_routes.start_research — same
        # contract: HTTP 400 with an actionable message before any DB
        # writes or thread spawning. (This blueprint uses success/error
        # response keys rather than status/message, matching the followup
        # API convention used by the other returns in this function.)
        if not settings_snapshot.get("llm.model", {}).get("value"):
            logger.error(
                "Follow-up research blocked: llm.model is not configured"
            )
            return jsonify(
                {
                    "success": False,
                    "error": "Model is required. Please configure a model in the settings.",
                }
            ), 400

        # SSRF pre-flight on the LLM endpoint: reject metadata / link-local
        # targets at the request boundary, before any DB row is written.
        # This is fail-fast defense-in-depth — the OpenAI-compatible provider's
        # assert_base_url_safe re-validates the same URL before the client is
        # built. Private IPs and localhost pass because local LLMs
        # (Ollama / LM Studio / vLLM) live there, including scheme-less
        # endpoints (the helper normalizes exactly as the provider does).
        custom_endpoint = settings_snapshot.get(
            "llm.openai_endpoint.url", {}
        ).get("value")
        if not is_safe_custom_llm_endpoint(custom_endpoint):
            return (
                jsonify(
                    {
                        "success": False,
                        "error": "Invalid custom endpoint URL",
                    }
                ),
                400,
            )

        # Generate new research ID
        research_id = str(uuid.uuid4())

        # Create database entry (settings_snapshot already captured above)
        from ..database.models import ResearchHistory
        from datetime import datetime, UTC

        created_at = datetime.now(UTC).isoformat()

        with get_user_db_session(username) as db_session:
            # Create the database entry (required for tracking)
            research_meta = {
                "submission": {
                    "parent_research_id": data.get("parent_research_id"),
                    "question": data.get("question"),
                    "strategy": "contextual-followup",
                },
            }

            research = ResearchHistory(
                id=research_id,
                query=research_params["query"],
                mode="quick",  # Use 'quick' not 'quick_summary'
                status=ResearchStatus.IN_PROGRESS,
                created_at=created_at,
                progress_log=[{"time": created_at, "progress": 0}],
                research_meta=research_meta,
            )
            db_session.add(research)
            db_session.commit()
            logger.info(
                f"Created follow-up research entry with ID: {research_id}"
            )

        # Start the research process using the existing infrastructure
        # Use quick_summary mode for follow-ups by default
        logger.info(
            f"Starting follow-up research for query of type: {type(research_params.get('query'))}"
        )

        # Get model and search settings from user's settings
        model_provider = settings_snapshot.get("llm.provider", {}).get(
            "value", "ollama"
        )
        # Normalize provider to lowercase canonical form
        model_provider = normalize_provider(model_provider)
        model = settings_snapshot.get("llm.model", {}).get("value", "")
        search_engine = settings_snapshot.get("search.tool", {}).get(
            "value", "searxng"
        )

        # Spawn the research thread. If the spawn fails, the
        # ResearchHistory row committed above would otherwise be
        # permanently orphaned with status=IN_PROGRESS. Catch any
        # exception, flip the status to FAILED, and return a clear
        # error — same contract as the queue processor's terminal-
        # failure branch (#3481) and the direct-UI spawn-failure path.
        try:
            start_research_process(
                research_id,
                research_params["query"],
                "quick",  # Use 'quick' for quick summary mode
                run_research_process,
                username=username,
                user_password=user_password,  # gitleaks:allow
                model_provider=model_provider,  # Pass model provider
                model=model,  # Pass model name
                search_engine=search_engine,  # Pass search engine
                custom_endpoint=custom_endpoint,  # Pass custom endpoint if any
                strategy="enhanced-contextual-followup",  # Use enhanced contextual follow-up strategy
                iterations=research_params["max_iterations"],
                questions_per_iteration=research_params[
                    "questions_per_iteration"
                ],
                delegate_strategy=research_params.get(
                    "delegate_strategy", "source-based"
                ),
                research_context=research_params["research_context"],
                parent_research_id=research_params[
                    "parent_research_id"
                ],  # Pass parent research ID
                settings_snapshot=settings_snapshot,
            )
        except DuplicateResearchError:
            # A live thread already owns this research_id. Do NOT delete
            # the row or mark it FAILED — the row belongs to the live
            # thread and mutating it would terminate the running
            # research from the user's perspective. Same contract as
            # research_routes.start_research's duplicate-thread branch.
            logger.warning(
                f"Duplicate live thread detected for follow-up "
                f"{research_id}; leaving state intact"
            )
            return jsonify(
                {
                    "success": False,
                    "error": "Research is already running.",
                }
            ), 409
        except SystemAtCapacityError:
            # System at concurrent-research capacity. Roll back the
            # IN_PROGRESS row committed above and return 429.
            logger.warning(
                f"SystemAtCapacityError starting follow-up {research_id}"
            )
            try:
                from ..database.session_context import get_user_db_session
                from ..database.models import ResearchHistory

                with get_user_db_session(username) as cleanup_session:
                    cleanup_session.query(ResearchHistory).filter_by(
                        id=research_id
                    ).delete()
                    cleanup_session.commit()
            except Exception:
                logger.exception(
                    "Cleanup after follow-up capacity reject raised"
                )
            return jsonify(
                {
                    "success": False,
                    "error": "Server is at research capacity. Please retry shortly.",
                }
            ), 429
        except Exception:
            logger.exception(
                f"Failed to spawn follow-up research thread for {research_id}"
            )
            try:
                from ..database.session_context import get_user_db_session
                from ..database.models import ResearchHistory

                with get_user_db_session(username) as cleanup_session:
                    research_row = (
                        cleanup_session.query(ResearchHistory)
                        .filter_by(id=research_id)
                        .first()
                    )
                    if research_row:
                        research_row.status = ResearchStatus.FAILED
                    cleanup_session.commit()
            except Exception:
                logger.exception("Cleanup after follow-up spawn failure raised")
            return jsonify(
                {
                    "success": False,
                    "error": "Failed to start follow-up research. Please try again.",
                }
            ), 500

        return jsonify(
            {
                "success": True,
                "research_id": research_id,
                "message": "Follow-up research started",
            }
        )

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