"""Full-text search across transcripts and frame descriptions."""

import logging
import re
from dataclasses import dataclass, asdict
from typing import Optional

from .db import Database

logger = logging.getLogger(__name__)


@dataclass
class SearchResult:
    clip_id: int
    file_path: str
    source: str  # "transcript" or "frame"
    timestamp: Optional[float]
    matched_text: str
    rank: float
    snippet: str


def _sanitize_fts_query(query: str) -> str:
    """Sanitize a query string for FTS5.

    Strips special FTS5 operators and wraps tokens in quotes for phrase search
    if the raw query contains characters that would cause syntax errors.
    """
    if not query or not query.strip():
        return ""

    query = query.strip()

    # If the query looks like it uses intentional FTS5 syntax, pass it through
    # Otherwise, wrap it as a simple phrase/token search
    fts5_operators = {"AND", "OR", "NOT", "NEAR"}
    tokens = query.split()
    has_operators = any(t in fts5_operators for t in tokens)

    if has_operators:
        return query

    # Remove characters that break FTS5 syntax
    cleaned = re.sub(r'[^\w\s\-]', ' ', query)
    cleaned = " ".join(cleaned.split())

    if not cleaned:
        return ""

    return cleaned


def search_clips(db: Database, query: str, limit: int = 20) -> list[dict]:
    """Search across transcripts and frame descriptions, merge and rank results."""
    sanitized = _sanitize_fts_query(query)
    if not sanitized:
        return []

    results = []

    # Search transcripts
    try:
        transcript_hits = db.search_transcripts(sanitized, limit=limit)
        for hit in transcript_hits:
            results.append(SearchResult(
                clip_id=hit["clip_id"],
                file_path=hit["file_path"],
                source="transcript",
                timestamp=hit["start_time"],
                matched_text=hit["text"],
                rank=hit["rank"],
                snippet=f"[{hit['start_time']:.1f}s-{hit['end_time']:.1f}s] {hit['text']}",
            ))
    except Exception as e:
        logger.warning("Transcript FTS search failed, trying phrase search: %s", e)
        try:
            phrase_query = f'"{sanitized}"'
            transcript_hits = db.search_transcripts(phrase_query, limit=limit)
            for hit in transcript_hits:
                results.append(SearchResult(
                    clip_id=hit["clip_id"],
                    file_path=hit["file_path"],
                    source="transcript",
                    timestamp=hit["start_time"],
                    matched_text=hit["text"],
                    rank=hit["rank"],
                    snippet=f"[{hit['start_time']:.1f}s-{hit['end_time']:.1f}s] {hit['text']}",
                ))
        except Exception:
            logger.warning("Phrase fallback also failed for transcript search")

    # Search frame descriptions
    try:
        frame_hits = db.search_frames(sanitized, limit=limit)
        for hit in frame_hits:
            results.append(SearchResult(
                clip_id=hit["clip_id"],
                file_path=hit["file_path"],
                source="frame",
                timestamp=hit["timestamp"],
                matched_text=hit["description"],
                rank=hit["rank"],
                snippet=f"[{hit['timestamp']:.1f}s] {hit['description'][:200]}",
            ))
    except Exception as e:
        logger.warning("Frame FTS search failed, trying phrase search: %s", e)
        try:
            phrase_query = f'"{sanitized}"'
            frame_hits = db.search_frames(phrase_query, limit=limit)
            for hit in frame_hits:
                results.append(SearchResult(
                    clip_id=hit["clip_id"],
                    file_path=hit["file_path"],
                    source="frame",
                    timestamp=hit["timestamp"],
                    matched_text=hit["description"],
                    rank=hit["rank"],
                    snippet=f"[{hit['timestamp']:.1f}s] {hit['description'][:200]}",
                ))
        except Exception:
            logger.warning("Phrase fallback also failed for frame search")

    # Sort by rank (lower is better in FTS5)
    results.sort(key=lambda r: r.rank)

    # Trim to limit
    results = results[:limit]

    return [asdict(r) for r in results]
