import json

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

from ...constants import ResearchStatus
from ...database.models import ResearchHistory
from ...database.models.library import Document as Document
from ...database.session_context import get_user_db_session
from ..auth.decorators import login_required
from ..models.database import (
    get_logs_for_research,
    get_total_logs_for_research,
)
from ..routes.globals import get_active_research_snapshot
from ..services.research_service import get_research_strategy
from ...security.rate_limiter import limiter
from ...security import filter_research_metadata
from ..utils.templates import render_template_with_defaults

# Create a Blueprint for the history routes
history_bp = Blueprint("history", __name__, url_prefix="/history")

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


# resolve_report_path removed - reports are now stored in database


@history_bp.route("/")
@login_required
def history_page():
    """Render the history page"""
    return render_template_with_defaults("pages/history.html")


@history_bp.route("/api", methods=["GET"])
@login_required
def get_history():
    """Get the research history JSON data"""
    username = session["username"]

    try:
        limit = request.args.get("limit", 200, type=int)
        limit = max(1, min(limit, 500))
        offset = request.args.get("offset", 0, type=int)
        offset = max(0, offset)

        with get_user_db_session(username) as db_session:
            # Single query with JOIN to get history + document counts
            results = (
                db_session.query(
                    ResearchHistory,
                    func.count(Document.id).label("document_count"),
                )
                .outerjoin(Document, Document.research_id == ResearchHistory.id)
                .group_by(ResearchHistory.id)
                .order_by(ResearchHistory.created_at.desc())
                .limit(limit)
                .offset(offset)
                .all()
            )

            logger.debug(f"All research count: {len(results)}")

            # Convert to list of dicts
            history = []
            for research, doc_count in results:
                item = {
                    "id": research.id,
                    "title": research.title,
                    "query": research.query,
                    "mode": research.mode,
                    "status": research.status,
                    "created_at": research.created_at,
                    "completed_at": research.completed_at,
                    "duration_seconds": research.duration_seconds,
                    "document_count": doc_count,
                }

                item["metadata"] = filter_research_metadata(
                    research.research_meta
                )

                # Recalculate duration if null but both timestamps exist
                if (
                    item["duration_seconds"] is None
                    and item["created_at"]
                    and item["completed_at"]
                ):
                    try:
                        from dateutil import parser  # type: ignore[import-untyped]

                        start_time = parser.parse(item["created_at"])
                        end_time = parser.parse(item["completed_at"])
                        item["duration_seconds"] = int(
                            (end_time - start_time).total_seconds()
                        )
                    except Exception:
                        logger.warning("Error recalculating duration")
                        logger.debug("Duration error details", exc_info=True)

                history.append(item)

        # Format response to match what client expects
        response_data = {
            "status": "success",
            "items": history,  # Use 'items' key as expected by client
        }

        # CORS headers are handled by SecurityHeaders middleware
        return jsonify(response_data)
    except Exception:
        logger.exception("Error getting history")
        return jsonify(
            {
                "status": "error",
                "items": [],
                "message": "Failed to retrieve history",
            }
        )


@history_bp.route("/status/<string:research_id>")
@limiter.exempt
@login_required
def get_research_status(research_id):
    username = session["username"]

    with get_user_db_session(username) as db_session:
        research = (
            db_session.query(ResearchHistory).filter_by(id=research_id).first()
        )

        if not research:
            return jsonify(
                {"status": "error", "message": "Research not found"}
            ), 404

        # Extract attributes while session is active
        # to avoid DetachedInstanceError after the with block exits
        result = {
            "id": research.id,
            "query": research.query,
            "mode": research.mode,
            "status": research.status,
            "created_at": research.created_at,
            "completed_at": research.completed_at,
            "progress_log": research.progress_log,
            "report_path": research.report_path,
        }

    # Add progress information from active research (atomic snapshot)
    snapshot = get_active_research_snapshot(research_id)
    if snapshot is not None:
        result["progress"] = snapshot["progress"]
        result["log"] = snapshot["log"]
    elif result.get("status") == ResearchStatus.COMPLETED:
        result["progress"] = 100
        try:
            result["log"] = json.loads(result.get("progress_log", "[]"))
        except Exception:
            logger.warning(
                "Error parsing progress_log for research %s", research_id
            )
            result["log"] = []
    else:
        result["progress"] = 0
        try:
            result["log"] = json.loads(result.get("progress_log", "[]"))
        except Exception:
            logger.warning(
                "Error parsing progress_log for research %s", research_id
            )
            result["log"] = []

    return jsonify(result)


@history_bp.route("/details/<string:research_id>")
@login_required
def get_research_details(research_id):
    """Get detailed progress log for a specific research"""

    logger.debug(f"Details route accessed for research_id: {research_id}")

    username = session["username"]

    try:
        with get_user_db_session(username) as db_session:
            research = (
                db_session.query(ResearchHistory)
                .filter_by(id=research_id)
                .first()
            )
            logger.debug(f"Research found: {research.id if research else None}")

            if not research:
                logger.error(f"Research not found for id: {research_id}")
                return jsonify(
                    {"status": "error", "message": "Research not found"}
                ), 404

            # Extract all needed attributes while session is active
            # to avoid DetachedInstanceError after the with block exits
            research_data = {
                "query": research.query,
                "mode": research.mode,
                "status": research.status,
                "created_at": research.created_at,
                "completed_at": research.completed_at,
            }
    except Exception:
        logger.exception("Database error")
        return jsonify(
            {
                "status": "error",
                "message": "An internal database error occurred.",
            }
        ), 500

    # Get logs from the dedicated log database
    logs = get_logs_for_research(research_id)

    # Get strategy information
    strategy_name = get_research_strategy(research_id)

    # Get an atomic snapshot of active research state
    snapshot = get_active_research_snapshot(research_id)

    # If this is an active research, merge with any in-memory logs
    if snapshot is not None:
        # Use the logs from memory temporarily until they're saved to the database
        memory_logs = snapshot["log"]

        # Filter out logs that are already in the database by timestamp
        db_timestamps = {log["time"] for log in logs}
        unique_memory_logs = [
            log for log in memory_logs if log["time"] not in db_timestamps
        ]

        # Add unique memory logs to our return list
        logs.extend(unique_memory_logs)

        # Sort logs by timestamp
        logs.sort(key=lambda x: x["time"])

    progress = (
        snapshot["progress"]
        if snapshot is not None
        else (100 if research_data["status"] == ResearchStatus.COMPLETED else 0)
    )

    return jsonify(
        {
            "research_id": research_id,
            "query": research_data["query"],
            "mode": research_data["mode"],
            "status": research_data["status"],
            "strategy": strategy_name,
            "progress": progress,
            "created_at": research_data["created_at"],
            "completed_at": research_data["completed_at"],
            "log": logs,
        }
    )


@history_bp.route("/report/<string:research_id>")
@login_required
def get_report(research_id):
    from ...storage import get_report_storage
    from ..auth.decorators import current_user

    username = current_user()

    with get_user_db_session(username) as db_session:
        research = (
            db_session.query(ResearchHistory).filter_by(id=research_id).first()
        )

        if not research:
            return jsonify(
                {"status": "error", "message": "Report not found"}
            ), 404

        try:
            # Get report using storage abstraction
            storage = get_report_storage(session=db_session)
            report_data = storage.get_report_with_metadata(
                research_id, username
            )

            if not report_data:
                return jsonify(
                    {"status": "error", "message": "Report content not found"}
                ), 404

            # Extract content and metadata
            content = report_data.get("content", "")
            stored_metadata = report_data.get("metadata", {})

            # Create an enhanced metadata dictionary with database fields
            enhanced_metadata = {
                "query": research.query,
                "mode": research.mode,
                "created_at": research.created_at,
                "completed_at": research.completed_at,
                "duration": research.duration_seconds,
            }

            # Merge with stored metadata
            enhanced_metadata.update(stored_metadata)

            return jsonify(
                {
                    "status": "success",
                    "content": content,
                    "query": research.query,
                    "mode": research.mode,
                    "created_at": research.created_at,
                    "completed_at": research.completed_at,
                    "metadata": enhanced_metadata,
                }
            )
        except Exception:
            logger.exception(
                "Failed to retrieve report for research %s", research_id
            )
            return jsonify(
                {"status": "error", "message": "Failed to retrieve report"}
            ), 500


@history_bp.route("/markdown/<string:research_id>")
@login_required
def get_markdown(research_id):
    """Get markdown export for a specific research"""
    from ...storage import get_report_storage
    from ..auth.decorators import current_user

    username = current_user()

    with get_user_db_session(username) as db_session:
        research = (
            db_session.query(ResearchHistory).filter_by(id=research_id).first()
        )

        if not research:
            return jsonify(
                {"status": "error", "message": "Report not found"}
            ), 404

        try:
            # Get report using storage abstraction
            storage = get_report_storage(session=db_session)
            content = storage.get_report(research_id, username)

            if not content:
                return jsonify(
                    {"status": "error", "message": "Report content not found"}
                ), 404

            return jsonify({"status": "success", "content": content})
        except Exception:
            logger.exception(
                "Failed to retrieve markdown report for research %s",
                research_id,
            )
            return jsonify(
                {"status": "error", "message": "Failed to retrieve report"}
            ), 500


@history_bp.route("/logs/<string:research_id>")
@login_required
def get_research_logs(research_id):
    """Get logs for a specific research ID"""
    username = session["username"]

    # First check if the research exists
    with get_user_db_session(username) as db_session:
        research = (
            db_session.query(ResearchHistory).filter_by(id=research_id).first()
        )

        if not research:
            return jsonify(
                {"status": "error", "message": "Research not found"}
            ), 404

    # Retrieve logs from the database
    logs = get_logs_for_research(research_id)

    # Format logs correctly if needed
    formatted_logs = []
    for log in logs:
        log_entry = log.copy()
        # Ensure each log has time, message, and type fields
        log_entry["time"] = log.get("time", "")
        log_entry["message"] = log.get("message", "No message")
        log_entry["type"] = log.get("type", "info")
        formatted_logs.append(log_entry)

    return jsonify({"status": "success", "logs": formatted_logs})


@history_bp.route("/log_count/<string:research_id>")
@login_required
def get_log_count(research_id):
    """Get the total number of logs for a specific research ID"""
    # Get the total number of logs for this research ID
    total_logs = get_total_logs_for_research(research_id)

    return jsonify({"status": "success", "total_logs": total_logs})
