"""
Semantic Search Routes

Provides endpoints for:
- Research history collection management and indexing
- Semantic search across any library collection
"""

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

from ....database.models.library import Collection, Document
from ....security.decorators import require_json_body
from ....web.auth.decorators import login_required
from ...utils import handle_api_error

search_bp = Blueprint("search", __name__, url_prefix="/library")

# =============================================================================
# Research History Collection & Indexing
# =============================================================================


@search_bp.route("/api/research-history/collection", methods=["GET"])
@login_required
def get_research_history_collection():
    """
    Get the Research History collection info and indexing status.

    Returns collection ID and statistics about indexed vs total research.
    Counts are derived from DocumentCollection membership (matching the
    collection page) rather than source_type_id filtering.
    """
    from ....constants import ResearchStatus
    from ....database.models.library import DocumentCollection
    from ....database.models.research import ResearchHistory
    from ....database.session_context import get_user_db_session
    from ....database.session_passwords import session_password_store
    from ..services.research_history_indexer import ResearchHistoryIndexer

    username = session["username"]
    session_id = session.get("session_id")

    db_password = None
    if session_id:
        db_password = (
            session_password_store.get_session_password(  # gitleaks:allow
                username, session_id
            )
        )

    try:
        indexer = ResearchHistoryIndexer(username, db_password)
        collection_id = indexer.get_or_create_collection()

        # Auto-convert any unconverted research entries to Documents.
        # This is a lightweight DB-only operation (no FAISS) that ensures
        # the collection page and history page always show consistent counts.
        try:
            indexer.convert_all_research(force=False)
        except Exception:
            logger.warning("Auto-conversion of research entries failed")

        with get_user_db_session(username, db_password) as db_session:
            # Total completed research with report content
            total_research = (
                db_session.query(ResearchHistory)
                .filter(ResearchHistory.status == ResearchStatus.COMPLETED)
                .filter(ResearchHistory.report_content.isnot(None))
                .filter(ResearchHistory.report_content != "")
                .count()
            )

            # Research entries represented in this collection
            # (via Document → DocumentCollection join, matching collection page)
            indexed_research = (
                db_session.query(Document.research_id)
                .join(
                    DocumentCollection,
                    DocumentCollection.document_id == Document.id,
                )
                .filter(DocumentCollection.collection_id == collection_id)
                .filter(Document.research_id.isnot(None))
                .distinct()
                .count()
            )

            # Document counts in collection
            total_documents = (
                db_session.query(DocumentCollection)
                .filter(DocumentCollection.collection_id == collection_id)
                .count()
            )
            indexed_documents = (
                db_session.query(DocumentCollection)
                .filter(DocumentCollection.collection_id == collection_id)
                .filter(DocumentCollection.indexed == True)  # noqa: E712
                .count()
            )

        return jsonify(
            {
                "success": True,
                "collection_id": collection_id,
                "total_research": total_research,
                "indexed_research": indexed_research,
                "total_documents": total_documents,
                "indexed_documents": indexed_documents,
            }
        )

    except Exception as e:
        return handle_api_error("getting research history collection", e)


@search_bp.route("/api/research-history/convert-all", methods=["POST"])
@login_required
def convert_all_research():
    """
    Convert all completed research entries into library Documents.

    Unlike the SSE index endpoint this is a synchronous JSON endpoint that
    creates Document rows (and DocumentCollection memberships) without
    triggering FAISS / RAG indexing.  Call this before the SSE index endpoint
    to avoid nested-session problems on SQLite.

    Request JSON (optional):
        force: If true, re-convert even already-converted entries (default false)

    Returns:
        JSON with converted, skipped, failed counts and collection_id
    """
    from ....database.session_passwords import session_password_store
    from ..services.research_history_indexer import ResearchHistoryIndexer

    username = session["username"]
    session_id = session.get("session_id")

    db_password = None
    if session_id:
        db_password = (
            session_password_store.get_session_password(  # gitleaks:allow
                username, session_id
            )
        )

    data = request.get_json() or {}
    force = data.get("force", False)

    try:
        indexer = ResearchHistoryIndexer(username, db_password)
        result = indexer.convert_all_research(force=force)
        return jsonify({"success": True, **result})

    except Exception as e:
        return handle_api_error("converting all research", e)


@search_bp.route(
    "/api/research/<string:research_id>/add-to-collection", methods=["POST"]
)
@login_required
@require_json_body(error_format="success")
def add_research_to_collection(research_id):
    """
    Add a research entry to a specific collection.

    This allows users to organize research into custom collections
    in addition to the default Research History collection.

    Args:
        research_id: UUID of the research to add

    Request JSON:
        collection_id: UUID of the target collection (required)
    """
    from ....database.session_context import get_user_db_session
    from ....database.session_passwords import session_password_store
    from ..services.research_history_indexer import ResearchHistoryIndexer

    username = session["username"]
    session_id = session.get("session_id")

    db_password = None
    if session_id:
        db_password = (
            session_password_store.get_session_password(  # gitleaks:allow
                username, session_id
            )
        )

    data = request.get_json()
    collection_id = data.get("collection_id")

    if not collection_id:
        return jsonify(
            {
                "success": False,
                "error": "collection_id is required",
            }
        ), 400

    try:
        # Verify collection exists
        with get_user_db_session(username, db_password) as db_session:
            collection = (
                db_session.query(Collection)
                .filter(Collection.id == collection_id)
                .first()
            )
            if not collection:
                return jsonify(
                    {
                        "success": False,
                        "error": "Collection not found",
                    }
                ), 404

            collection_name = collection.name

        indexer = ResearchHistoryIndexer(username, db_password)
        result = indexer.index_research(
            research_id,
            collection_id=collection_id,
        )

        if result["status"] == "error":
            return jsonify(
                {
                    "success": False,
                    "error": result.get("error", "Operation failed."),
                }
            ), 400

        result["collection_name"] = collection_name
        return jsonify({"success": True, **result})

    except Exception as e:
        return handle_api_error("adding research to collection", e)


# =============================================================================
# Collection Search (generic — works for any collection type)
# =============================================================================


@search_bp.route(
    "/api/collections/<string:collection_id>/search", methods=["POST"]
)
@login_required
@require_json_body(error_format="success")
def search_collection(collection_id):
    """Search any collection using semantic similarity.

    Delegates to CollectionSearchEngine instead of reimplementing FAISS search.

    Request JSON:
        query: Search query string
        limit: Maximum number of results (default 10)
    """
    from ....database.session_context import get_user_db_session
    from ....database.session_passwords import session_password_store
    from ....web_search_engines.engines.search_engine_collection import (
        CollectionSearchEngine,
    )

    username = session["username"]
    session_id = session.get("session_id")

    db_password = None
    if session_id:
        db_password = (
            session_password_store.get_session_password(  # gitleaks:allow
                username, session_id
            )
        )

    data = request.get_json()
    query = data.get("query", "").strip()

    if len(query) > 10000:
        return jsonify(
            {"success": False, "error": "Query too long (max 10000 characters)"}
        ), 400

    try:
        limit = max(1, min(int(data.get("limit", 10)), 50))
    except (TypeError, ValueError):
        limit = 10

    if not query:
        return jsonify({"success": False, "error": "Query is required"}), 400

    try:
        # Verify collection exists and get its type
        with get_user_db_session(username, db_password) as db_session:
            collection = (
                db_session.query(Collection).filter_by(id=collection_id).first()
            )
            if not collection:
                return jsonify(
                    {"success": False, "error": "Collection not found"}
                ), 404
            collection_type = collection.collection_type
            collection_name = collection.name

        # Delegate to CollectionSearchEngine
        engine = CollectionSearchEngine(
            collection_id=collection_id,
            collection_name=collection_name,
            max_results=limit * 2,
            settings_snapshot={"_username": username},
        )
        raw_results = engine.search(query, limit=limit * 2)

        # Transform CollectionSearchEngine format -> API format
        results = []
        for r in raw_results:
            meta = r.get("metadata", {})
            results.append(
                {
                    "document_id": meta.get("document_id")
                    or meta.get("source_id"),
                    "title": r.get("title", "Untitled"),
                    "snippet": r.get("snippet", ""),
                    "similarity": round(r.get("relevance_score", 0) * 100, 1),
                    "url": meta.get("source"),
                }
            )
            if len(results) >= limit:
                break

        # For research_history collections, enrich with report/source type
        if collection_type == "research_history":
            _enrich_with_research_metadata(results, username, db_password)

        # Always enrich with document-level metadata (file type, domain)
        _enrich_with_document_metadata(results, username, db_password)

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

    except Exception as e:
        return handle_api_error("searching collection", e)


def _enrich_with_research_metadata(results, username, db_password):
    """Add report/source type and research context to search results."""
    from ....database.models.library import SourceType
    from ....database.models.research import ResearchHistory
    from ....database.session_context import get_user_db_session

    doc_ids = [r["document_id"] for r in results if r.get("document_id")]
    if not doc_ids:
        return

    with get_user_db_session(username, db_password) as db_session:
        rows = (
            db_session.query(
                Document.id.label("document_id"),
                SourceType.name.label("source_type_name"),
                ResearchHistory.title.label("research_title"),
                ResearchHistory.query.label("research_query"),
                ResearchHistory.created_at.label("research_created_at"),
                Document.research_id,
            )
            .outerjoin(SourceType, Document.source_type_id == SourceType.id)
            .outerjoin(
                ResearchHistory,
                Document.research_id == ResearchHistory.id,
            )
            .filter(Document.id.in_(doc_ids))
            .all()
        )
        lookup = {row.document_id: row for row in rows}

    for result in results:
        row = lookup.get(result.get("document_id"))
        if row:
            result["type"] = (
                "report"
                if row.source_type_name == "research_report"
                else "source"
            )
            result["research_id"] = row.research_id
            result["research_title"] = row.research_title or (
                row.research_query[:100] if row.research_query else ""
            )
            result["research_query"] = row.research_query
            result["research_created_at"] = (
                row.research_created_at
                if isinstance(row.research_created_at, str)
                else row.research_created_at.isoformat()
                if row.research_created_at
                else None
            )
        else:
            result["type"] = "source"
            result["research_id"] = None
            result["research_title"] = ""
            result["research_query"] = None
            result["research_created_at"] = None


def _enrich_with_document_metadata(results, username, db_password):
    """Add file type, domain, and creation date to search results."""
    from urllib.parse import urlparse

    from ....database.session_context import get_user_db_session

    doc_ids = [r["document_id"] for r in results if r.get("document_id")]
    if not doc_ids:
        return

    with get_user_db_session(username, db_password) as db_session:
        rows = (
            db_session.query(
                Document.id.label("document_id"),
                Document.file_type,
                Document.original_url,
                Document.created_at,
            )
            .filter(Document.id.in_(doc_ids))
            .all()
        )
        lookup = {row.document_id: row for row in rows}

    for result in results:
        row = lookup.get(result.get("document_id"))
        if row:
            result["file_type"] = row.file_type
            result["created_at"] = (
                row.created_at
                if isinstance(row.created_at, str)
                else row.created_at.isoformat()
                if row.created_at
                else None
            )
            if row.original_url:
                try:
                    result["domain"] = urlparse(row.original_url).netloc
                except (ValueError, AttributeError):
                    result["domain"] = "unknown"
            else:
                result["domain"] = None
        else:
            result.setdefault("file_type", "unknown")
            result.setdefault("domain", None)
            result.setdefault("created_at", None)
