"""Hybrid vector + FTS search with RRF reranking."""

import logging
import re

from ars25.core.db import get_table, table_exists
from ars25.ingestion.embedder import embed_query
from ars25.models.statute import SearchResult

logger = logging.getLogger(__name__)

# Detect statute references like "25-403" or "§ 25-403"
STATUTE_PATTERN = re.compile(r"\b25-(\d+(?:\.\d+)?)\b")
SECTION_PATTERN = re.compile(r"^25-\d+(\.\d+)?$")


def _row_to_result(row: dict, score: float) -> SearchResult:
    return SearchResult(
        section=row["section"],
        title=row["title"],
        chunk_text=row["text"],
        score=score,
        chapter_number=row["chapter_number"],
        chapter_title=row["chapter_title"],
        article_number=row["article_number"],
        article_title=row["article_title"],
        url=row["url"],
    )


def search(
    query: str,
    top_k: int = 10,
    chapter: int | None = None,
    article: int | None = None,
) -> list[SearchResult]:
    """Hybrid vector+FTS search with optional chapter/article filters.

    Auto-detects statute references in the query and fetches them directly.
    Falls back to vector-only if FTS index is not available.
    """
    if not table_exists():
        return []

    table = get_table()

    # Build filter clause
    filters: list[str] = []
    if chapter is not None:
        filters.append(f"chapter_number = {int(chapter)}")
    if article is not None:
        filters.append(f"article_number = {int(article)}")
    where_clause = " AND ".join(filters) if filters else None

    results: list[SearchResult] = []
    seen_keys: set[str] = set()

    # If query mentions a specific section, fetch it directly first
    statute_match = STATUTE_PATTERN.search(query)
    if statute_match:
        section = f"25-{statute_match.group(1)}"
        if SECTION_PATTERN.match(section):
            direct_filter = f"section = '{section}'"
            if where_clause:
                direct_filter = f"{direct_filter} AND {where_clause}"
            try:
                direct_rows = (
                    table.search()
                    .where(direct_filter)
                    .limit(5)
                    .to_list()
                )
                for row in direct_rows:
                    key = f"{row['section']}_{row['chunk_index']}"
                    if key not in seen_keys:
                        seen_keys.add(key)
                        results.append(_row_to_result(row, 1.0))
            except Exception as e:
                logger.warning("Direct lookup failed: %s", e)

    # Run hybrid search
    query_vector = embed_query(query)

    try:
        # Try hybrid (vector + FTS) with RRF reranking
        from lancedb.rerankers import RRFReranker

        builder = (
            table.search(query_vector, query_type="hybrid")
            .text(query)
            .rerank(RRFReranker())
        )
        if where_clause:
            builder = builder.where(where_clause, prefilter=True)
        hybrid_rows = builder.limit(top_k).to_list()

        for row in hybrid_rows:
            key = f"{row['section']}_{row['chunk_index']}"
            if key not in seen_keys:
                seen_keys.add(key)
                score = row.get("_relevance_score", 0.0)
                results.append(_row_to_result(row, score))

    except Exception as e:
        # Fall back to vector-only search
        logger.warning("Hybrid search failed (%s), falling back to vector-only", e)
        builder = table.search(query_vector)
        if where_clause:
            builder = builder.where(where_clause, prefilter=True)
        vector_rows = builder.limit(top_k).to_list()

        for row in vector_rows:
            key = f"{row['section']}_{row['chunk_index']}"
            if key not in seen_keys:
                seen_keys.add(key)
                score = row.get("_distance", 0.0)
                # Convert distance to similarity score (lower distance = higher similarity)
                results.append(_row_to_result(row, 1.0 / (1.0 + score)))

    return results[:top_k]
