"""Search endpoint — POST /api/v1/search (Phase 3.2, master plan sec 9.1)."""

from __future__ import annotations

import logging
import time
from typing import Annotated

from fastapi import APIRouter, Depends
from sqlalchemy import text
from sqlalchemy.orm import Session

from app.core.db import get_db
from app.schemas.search import SearchParams
from app.schemas.search_response import SearchResponse, SearchResultItem
from app.services.cache import get_search_cache
from app.services.search_query import build_count_query, build_search_query

logger = logging.getLogger(__name__)

router = APIRouter()


def _map_row(row: object) -> dict[str, object]:
    """Маппит строку matview в поля SearchResultItem.

    SQL отдаёт колонку `cadastral_number`, а в схеме поле зовётся
    `kadastr_num` — без явного маппинга значение молча теряется (#1514).
    """
    data = dict(row)
    if "cadastral_number" in data:
        data["kadastr_num"] = data.pop("cadastral_number")
    return data


@router.post("/search", response_model=SearchResponse)
async def search(
    params: SearchParams,
    db: Annotated[Session, Depends(get_db)],
) -> SearchResponse:
    """Поиск listings по фильтрам (cross-source matview + Redis 5min cache)."""
    started = time.monotonic()
    cache = get_search_cache()
    cache_key = cache.search_key(params.model_dump(mode="json"))

    cached = await cache.get(cache_key)
    if cached is not None:
        logger.info(
            "search cache HIT key=%s page=%d size=%d",
            cache_key[:24], params.page, params.page_size,
        )
        resp = SearchResponse.model_validate(cached)
        resp.cache_hit = True
        return resp

    sql, args = build_search_query(params)
    rows = db.execute(text(sql), args).mappings().all()

    count_sql, count_args = build_count_query(params)
    total = int(db.execute(text(count_sql), count_args).scalar() or 0)

    items = [SearchResultItem.model_validate(_map_row(r)) for r in rows]
    elapsed_ms = (time.monotonic() - started) * 1000.0
    response = SearchResponse(
        items=items,
        total=total,
        page=params.page,
        page_size=params.page_size,
        elapsed_ms=round(elapsed_ms, 1),
        cache_hit=False,
    )

    await cache.set(cache_key, response.model_dump(mode="json"), ttl=cache.TTL_SEARCH)
    logger.info(
        "search MISS page=%d size=%d total=%d items=%d elapsed=%.1fms",
        params.page, params.page_size, total, len(items), elapsed_ms,
    )
    return response
