"""
Thin LookupEngine: Transliteration → Morphology → Search pipeline.
No full NA28 / Metzger integration yet.
"""

from __future__ import annotations

from dataclasses import dataclass, field

from .search_index import (
    SearchIndex,
    detect_language,
    _reverse_transliterate_greek_variants,
    _reverse_transliterate_hebrew,
)
from .morphology import MorphologyDB


@dataclass
class LookupResult:
    """A single search hit from the index."""

    source_type: str  # 'bible', 'lexicon', 'commentary'
    source_name: str  # module abbreviation (e.g. 'BDAG')
    source_id: str  # verse ref, headword, paragraph id
    snippet: str  # FTS5-highlighted snippet
    citation: str  # formatted citation string
    rank: float  # BM25 rank (lower = better)


@dataclass
class ResolvedQuery:
    """The fully-resolved result of a lookup pipeline run."""

    original: str
    language: str  # 'el', 'he', or 'en'
    reversed_form: str | None = None
    lemmas: list[str] = field(default_factory=list)
    results_by_type: dict[str, list[LookupResult]] = field(default_factory=dict)


class LookupEngine:
    """Orchestrates Transliterator → MorphologyDB → SearchIndex."""

    # Transliteration-detection markers.  Digraphs (th, ph, ch, ps) and
    # common Greek word endings (os, ēs, on) distinguish transliterated
    # ASCII from plain English.
    _GREEK_MARKERS = frozenset(
        {"th", "ph", "ch", "ps", "ē", "ō", "rh", "pt", "pn", "gn", "mn"}
    )
    _GREEK_ENDINGS = frozenset(
        (
            "os",
            "ēs",
            "on",
            "ē",
            "ōn",
            "is",
            "us",
            "oi",
            "ai",
            "ou",
            "ma",
            "sis",
            "ia",
            "eia",
            "ous",
            "as",
            "ōs",
            "ō",
        )
    )
    _HEBREW_MARKERS = frozenset({"ḥ", "ṭ", "ṣ", "ʾ", "ʿ", "š", "ś"})

    def __init__(self, search_index: SearchIndex, morphology_db: MorphologyDB):
        self._si = search_index
        self._morph = morphology_db

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    def resolve(self, query: str, max_per_type: int = 10) -> ResolvedQuery:
        """Run the full lookup pipeline and return a ResolvedQuery."""
        query = query.replace("\x00", "")  # defense-in-depth: strip null bytes
        lang, reversed_form = self._detect(query)

        # Lemma lookup (Greek / Hebrew forms only)
        lemmas: list[str] = []
        search_form = reversed_form if reversed_form else query
        if lang in ("el", "he"):
            lemmas = self._morph.lemma_from_form(search_form, lang)
            # False-positive guard for Greek ASCII: digraph "pt" catches
            # English words like "baptism".  Hebrew diacritics (ḥ, ṭ, …)
            # hardly ever appear in English, so only fall back for Greek.
            if not lemmas and reversed_form and lang == "el":
                lang, reversed_form, search_form = "en", None, query

        # Search across all resource types
        results: dict[str, list[LookupResult]] = {
            "bible": [],
            "lexicon": [],
            "commentary": [],
        }
        seen: set[tuple[str, str]] = set()
        for q in {search_form, *lemmas[:3]}:
            fuzzy = lang in ("el", "he")
            for source_type in ("bible", "lexicon", "commentary"):
                for raw in self._si.search(
                    q, source_type, lang, max_per_type, fuzzy=fuzzy
                ):
                    key = (raw[1], raw[2])  # (source_name, source_id)
                    if key in seen:
                        continue
                    seen.add(key)
                    results[source_type].append(
                        LookupResult(
                            source_type=raw[0],
                            source_name=raw[1],
                            source_id=raw[2],
                            snippet=raw[3],
                            citation=raw[4],
                            rank=raw[5],
                        )
                    )

        for k in results:
            results[k].sort(key=lambda r: r.rank)

        return ResolvedQuery(
            original=query,
            language=lang,
            reversed_form=reversed_form,
            lemmas=lemmas,
            results_by_type=results,
        )

    # ------------------------------------------------------------------
    # Helpers
    # ------------------------------------------------------------------

    def _detect(self, query: str) -> tuple[str, str | None]:
        """Return (language, reversed_form_or_None)."""
        detected = detect_language(query)
        if detected in ("el", "he"):
            return detected, None
        if not query.strip():
            return "en", None

        # Reject queries that contain non-letter characters (digits, punct)
        # or native-script Greek/Hebrew glyphs (handled above).
        # Latin Extended characters (ē, ḥ, etc.) are fine.
        if not all(c.isalpha() or c.isspace() for c in query):
            return "en", None
        if any(
            0x370 <= ord(c) <= 0x3FF
            or 0x1F00 <= ord(c) <= 0x1FFF
            or 0x590 <= ord(c) <= 0x5FF
            for c in query
        ):
            return "en", None  # native-script chars; already handled above

        lower = query.strip().lower()
        if any(m in lower for m in self._HEBREW_MARKERS):
            return "he", _reverse_transliterate_hebrew(query)

        has_marker = any(m in lower for m in self._GREEK_MARKERS)
        has_ending = any(lower.endswith(e) for e in self._GREEK_ENDINGS)
        if has_marker or has_ending:
            variants = _reverse_transliterate_greek_variants(query)
            return "el", variants[0]  # primary mapping (e→ε, o→ο)

        return "en", None
