"""Enrich Strong's number lookups with Open Scriptures data.

When the Accordance lexicon modules don't have a definition (or the
definition is sparse), fall back to Open Scriptures' open-source
Strong's dictionaries (293 stars on GitHub).
"""

from __future__ import annotations

from typing import Dict, List, Optional


class StrongsEnricher:
    """Transparently enriches Strong's lookups with Open Scriptures data.

    The enrichment layer tries Accordance lexicons first; if the result
    is empty or trivially sparse, it falls back to Open Scriptures.
    """

    def __init__(self, data_dir: Optional[str] = None):
        self._data_dir = data_dir
        self._client = None  # lazily loaded
        self._import_error: Optional[str] = None

    # ------------------------------------------------------------------
    # Lazy client
    # ------------------------------------------------------------------

    def _get_client(self):
        """Lazy-load the OpenScripturesClient singleton."""
        if self._client is not None:
            return self._client
        if self._import_error is not None:
            return None
        try:
            from accordance_parser.openscriptures import OpenScripturesClient

            self._client = OpenScripturesClient(data_dir=self._data_dir)
            return self._client
        except Exception as exc:
            self._import_error = str(exc)
            return None

    def available(self) -> bool:
        """Return True if Open Scriptures data is present on disk."""
        client = self._get_client()
        if client is None:
            return False
        return client.available()

    # ------------------------------------------------------------------
    # enrich_definition
    # ------------------------------------------------------------------

    def enrich_definition(
        self,
        strongs_number: str,
        language: str,
        accordance_result: Optional[str] = None,
    ) -> dict:
        """Return the best available definition for a Strong's number.

        Args:
            strongs_number: e.g. ``"H7225"`` or ``"G3056"``.
            language: ``"hebrew"`` (or ``"he"``) for Hebrew, ``"greek"`` (or ``"gr"``)
                      for Greek.
            accordance_result: The definition text from Accordance lexicons,
                               if already looked up.  Pass ``None`` if no
                               Accordance lookup was done (fallback-only mode).

        Returns:
            A dict with keys:
            - ``definition`` (str) — the best definition text
            - ``source`` (str) — ``"accordance"`` or ``"openscriptures"``
            - ``lemma`` (str) — the headword lemma (may be ``""``)
            - ``strongs_number`` (str) — the normalized Strong's number
        """
        lang_code = self._normalize_language(language)

        # --- Use Accordance result if it has substance ---
        if accordance_result and self._has_substance(accordance_result):
            return {
                "definition": accordance_result.strip(),
                "source": "accordance",
                "lemma": "",
                "strongs_number": strongs_number,
            }

        # --- Fall back to Open Scriptures ---
        client = self._get_client()
        if client is None:
            msg = "OpenScripturesClient not available"
            if self._import_error:
                msg += f" ({self._import_error})"
            return {
                "definition": f"(No definition available — {msg})",
                "source": "none",
                "lemma": "",
                "strongs_number": strongs_number,
            }

        if not client.available() and not client.ensure_data():
            return {
                "definition": "(Open Scriptures data not downloaded. "
                "Run client.ensure_data() or clone repos manually.)",
                "source": "none",
                "lemma": "",
                "strongs_number": strongs_number,
            }

        os_result = client.get_strongs_definition(strongs_number, language=lang_code)
        if "message" in os_result:
            # Open Scriptures also couldn't find it
            return {
                "definition": accordance_result.strip()
                if accordance_result
                else os_result["message"],
                "source": "accordance" if accordance_result else "openscriptures",
                "lemma": os_result.get("lemma", ""),
                "strongs_number": strongs_number,
            }

        return {
            "definition": os_result.get("definition", ""),
            "source": "openscriptures",
            "lemma": os_result.get("lemma", ""),
            "strongs_number": os_result.get("strongs", strongs_number),
        }

    # ------------------------------------------------------------------
    # search_strongs
    # ------------------------------------------------------------------

    def search_strongs(
        self,
        query: str,
        language: str,
        accordance_results: Optional[List[dict]] = None,
    ) -> List[dict]:
        """Search Strong's definitions, combining Accordance + Open Scriptures.

        Args:
            query: English search term(s).
            language: ``"hebrew"`` or ``"greek"``.
            accordance_results: Pre-fetched results from Accordance search
                                (list of dicts with at least ``strongs_number``).
                                Pass ``None`` if no Accordance search was done.

        Returns:
            List of dicts, each with:
            - ``strongs`` (str)
            - ``definition`` (str)
            - ``lemma`` (str)
            - ``source`` (str) — ``"accordance"``, ``"openscriptures"``, or ``"both"``
        """
        lang_code = self._normalize_language(language)
        merged: Dict[str, dict] = {}  # keyed by normalized Strong's number

        # --- Fold in Accordance results ---
        if accordance_results:
            for r in accordance_results:
                sn = r.get("strongs_number", r.get("strongs", ""))
                if not sn:
                    continue
                key = sn.upper()
                merged[key] = {
                    "strongs": key,
                    "definition": r.get("definition", r.get("snippet", "")),
                    "lemma": r.get("lemma", ""),
                    "source": "accordance",
                }

        # --- Fold in Open Scriptures results ---
        client = self._get_client()
        if client is not None and (client.available() or client.ensure_data()):
            os_results = client.search_strongs(query, language=lang_code)
            for r in os_results:
                if "message" in r:
                    continue
                sn = r.get("strongs", "")
                key = sn.upper()
                if key in merged:
                    merged[key]["source"] = "both"
                    # Prefer OS definition if Accordance snippet is sparse
                    if not self._has_substance(merged[key].get("definition", "")):
                        merged[key]["definition"] = r.get("definition", "")
                        merged[key]["lemma"] = r.get("lemma", "")
                else:
                    merged[key] = {
                        "strongs": key,
                        "definition": r.get("definition", ""),
                        "lemma": r.get("lemma", ""),
                        "source": "openscriptures",
                    }

        return list(merged.values())

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

    @staticmethod
    def _normalize_language(language: str) -> str:
        """Normalize language input to OpenScripturesClient format ('he'/'gr')."""
        lang = language.strip().lower()
        if lang in ("he", "hebrew"):
            return "he"
        if lang in ("gr", "greek"):
            return "gr"
        return lang  # pass through; let downstream validate

    @staticmethod
    def _has_substance(text: str) -> bool:
        """Check whether a definition string has meaningful content.

        Filters out trivial placeholders like ``"(No ... found)"`` or
        single-word results.
        """
        if not text or not text.strip():
            return False
        stripped = text.strip()
        if stripped.startswith("(No ") and "found" in stripped:
            return False
        if stripped.startswith("(") and stripped.endswith(")") and len(stripped) < 40:
            return False
        if len(stripped.split()) < 2:
            return False
        return True
