"""Multilingual keyword/pattern loader.

Loads YAML keyword lists from skill config directories and exposes them
as flat, language-agnostic match sets. Used by code paths that need to
detect intent or urgency across any user language without hardcoding
English word lists.

YAML format (multilingual):

    en:
      - urgent
      - help
    sv:
      - brådskande
      - hjälp

Or flat (legacy):

    - urgent
    - help

The loader flattens all languages into one lowercase set. Matching is
intentionally language-unaware — callers just ask "does this text
contain any known keyword?" regardless of which language the user or
sender wrote in.

Files are searched in the user vault first (so users can edit their
own copy), then fall back to the bundled defaults.
"""

from __future__ import annotations

import logging
from functools import lru_cache
from pathlib import Path

log = logging.getLogger(__name__)


def _load_yaml(path: Path) -> object:
    try:
        import yaml  # type: ignore
    except ImportError:
        log.warning("PyYAML not installed — language patterns cannot be loaded")
        return None
    try:
        with open(path, encoding="utf-8") as f:
            return yaml.safe_load(f)
    except Exception as e:
        log.warning("Failed to load language patterns %s: %s", path, e)
        return None


def _find_defaults_dir() -> Path | None:
    """Locate the bundled defaults/ directory (dev repo or installed package)."""
    # Dev repo: server/src/agntspace/infra/language_patterns.py -> repo/defaults
    repo = Path(__file__).parent.parent.parent.parent.parent / "defaults"
    if repo.is_dir():
        return repo
    try:
        import importlib.resources

        ref = importlib.resources.files("agntspace") / "defaults"
        pkg = Path(str(ref))
        if pkg.is_dir():
            return pkg
    except (TypeError, FileNotFoundError, ModuleNotFoundError):
        pass
    return None


def _resolve_path(relative: str, vault_dir: Path | None) -> Path | None:
    """Find a language pattern file.

    Search order:
    1. User vault: ``{vault_dir}/agntspace/system/skills/{relative}``
    2. Bundled defaults: ``defaults/skills/{relative}``
    """
    if vault_dir is not None:
        vault_path = vault_dir / "agntspace" / "system" / "skills" / relative
        if vault_path.is_file():
            return vault_path
    defaults = _find_defaults_dir()
    if defaults is not None:
        d_path = defaults / "skills" / relative
        if d_path.is_file():
            return d_path
    return None


@lru_cache(maxsize=64)
def _load_keywords_cached(relative: str, vault_key: str) -> tuple[str, ...]:
    vault_dir = Path(vault_key) if vault_key else None
    path = _resolve_path(relative, vault_dir)
    if path is None:
        log.debug("Language pattern file not found: %s", relative)
        return ()

    data = _load_yaml(path)
    if data is None:
        return ()

    keywords: list[str] = []
    if isinstance(data, dict):
        # Multilingual: {lang_code: [words...]}
        for _lang, words in data.items():
            if isinstance(words, list):
                for w in words:
                    if isinstance(w, str) and w.strip():
                        keywords.append(w.strip().lower())
    elif isinstance(data, list):
        # Flat list
        for w in data:
            if isinstance(w, str) and w.strip():
                keywords.append(w.strip().lower())

    # Dedup while preserving order
    seen: set[str] = set()
    unique: list[str] = []
    for kw in keywords:
        if kw not in seen:
            seen.add(kw)
            unique.append(kw)
    return tuple(unique)


def load_keywords(relative: str, vault_dir: Path | None = None) -> tuple[str, ...]:
    """Load a multilingual keyword list from a skill config YAML.

    Args:
        relative: Path under ``skills/``, e.g. ``"core/channel-protocol/config/urgency.yaml"``.
        vault_dir: Optional vault root — the user's copy of the skill takes precedence.

    Returns:
        Tuple of lowercased keywords across all defined languages. Empty on any error.
    """
    vault_key = str(vault_dir) if vault_dir else ""
    return _load_keywords_cached(relative, vault_key)


def contains_any(text: str, keywords: tuple[str, ...]) -> bool:
    """Case-insensitive substring match.

    Unicode-safe: uses ``str.lower()`` which handles non-ASCII scripts
    correctly for the languages we care about (Latin, Cyrillic, Greek,
    etc.). CJK/Arabic/Hebrew scripts are unaffected by case folding.
    """
    if not text or not keywords:
        return False
    haystack = text.lower()
    return any(kw in haystack for kw in keywords)


def reload() -> None:
    """Clear the keyword cache (useful after user edits skill config)."""
    _load_keywords_cached.cache_clear()
