import time

from quart import Blueprint, jsonify, request

from scribe.auth import login_required, get_current_user_id
from scribe.services.access import owner_names_for
from scribe.services.embeddings import semantic_search_notes
from scribe.services.retrieval_telemetry import record_retrieval

# This route searches with a looser floor than the MCP tool default — it powers
# an interactive feed where loosely-related hits still have value.
_REST_SEARCH_THRESHOLD = 0.3

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


def _content_type_to_is_task(content_type: str) -> bool | None:
    """Map content_type query param to semantic_search_notes is_task arg."""
    if content_type == "note":
        return False
    if content_type == "task":
        return True
    return None  # "all" or unknown → no filter


@search_bp.route("", methods=["GET"])
@login_required
async def search_route():
    uid = get_current_user_id()
    q = (request.args.get("q") or "").strip()
    if not q:
        return jsonify({"error": "q is required"}), 400

    content_type = request.args.get("content_type", "all")
    limit = min(request.args.get("limit", 10, type=int), 50)
    is_task = _content_type_to_is_task(content_type)
    # Same association filter the MCP tool takes (#33). The project filter this
    # route is still missing is #2463's — it carries a default-scope UI decision
    # this change must not preempt.
    system_id = request.args.get("system_id", type=int)

    t0 = time.perf_counter()
    results = await semantic_search_notes(
        uid, q, limit=limit, is_task=is_task, threshold=_REST_SEARCH_THRESHOLD,
        system_id=system_id,
        # The user typed this, so it reaches everything they may read.
        scope="read",
    )
    record_retrieval(
        user_id=uid, source="rest_search", query=q,
        threshold=_REST_SEARCH_THRESHOLD, limit=limit,
        project_id=None, is_task=is_task, results=results,
        duration_ms=(time.perf_counter() - t0) * 1000.0,
    )
    owners = await owner_names_for(
        {int(note.user_id) for _s, note in results if note.user_id != uid}
    )
    return jsonify({
        "results": [
            {
                "id": note.id,
                "title": note.title,
                "body": note.body or "",
                "is_task": note.is_task,
                "tags": note.tags or [],
                "similarity": score,
                **(
                    {"shared": True, "owner": owners.get(int(note.user_id))}
                    if note.user_id != uid else {}
                ),
            }
            for score, note in results  # semantic_search_notes returns list[tuple[float, Note]]
        ],
        "total": len(results),
    })
