"""LanguageScanner for meshulash-guard SDK.

Blocks text written in languages not on a developer-configured allow-list.
The scanner is a pure config serializer: language detection runs on the
server side using langdetect (or equivalent). The SDK validates the allow-list
and serializes the config for /api/security/scan.

ISO 639-1 codes are plain 2-letter lowercase strings (e.g. "en", "he", "fr").
An enum would add friction with no value — langdetect returns raw strings.
"""

from __future__ import annotations

from ..enums import Action
from .base import BaseScanner


class LanguageScanner(BaseScanner):
    """Scanner that blocks text in languages not on an allow-list.

    Uses ISO 639-1 two-letter language codes (e.g. "en", "he", "fr").
    The server detects the language of the input and compares it against
    the allow-list; text in unlisted languages is handled with the
    configured action (or per-language override).

    Args:
        allowed_languages: Non-empty list of ISO 639-1 two-letter lowercase
            codes to allow. Text in any other language triggers the scanner.
        confidence_threshold: Minimum detection confidence [0.0, 1.0] to
            consider a language detection reliable. Defaults to 0.8.
        action: Default action to take for disallowed languages. Defaults to
            BLOCK.
        overrides: Optional per-language action overrides for disallowed
            languages. E.g. ``{"fr": Action.LOG}`` logs French instead of
            blocking. Keys must be valid ISO 639-1 codes (not in the
            allow-list is typical, but not enforced).

    Raises:
        ValueError: If allowed_languages is empty, contains invalid ISO 639-1
            codes (not exactly 2 lowercase ASCII letters), or if
            confidence_threshold is outside [0.0, 1.0].
    """

    def __init__(
        self,
        allowed_languages: list[str],
        confidence_threshold: float = 0.8,
        action: Action = Action.BLOCK,
        overrides: dict[str, Action] | None = None,
    ) -> None:
        if not allowed_languages:
            raise ValueError(
                "LanguageScanner requires at least one allowed language code. "
                "An empty allow-list would silently block all input."
            )
        for lang in allowed_languages:
            if not (len(lang) == 2 and lang.isalpha() and lang.islower()):
                raise ValueError(
                    f"LanguageScanner: invalid ISO 639-1 code {lang!r}. "
                    "Each code must be exactly 2 lowercase ASCII letters (e.g. 'en', 'he')."
                )
        if not (0.0 <= confidence_threshold <= 1.0):
            raise ValueError(
                f"LanguageScanner confidence_threshold must be in [0.0, 1.0], "
                f"got {confidence_threshold!r}."
            )

        self._allowed_languages = list(allowed_languages)
        self._confidence_threshold = confidence_threshold
        self._action = action
        self._overrides = dict(overrides) if overrides else None

    def to_guardline_spec(self) -> dict:
        """Return a flat guardline spec dict for this scanner configuration."""
        spec: dict = {
            "name": "LanguageScanner",
            "action": str(self._action),
            "allowed_languages": self._allowed_languages,
            "confidence_threshold": self._confidence_threshold,
            "level": 3,
        }
        if self._overrides:
            spec["overrides"] = {lang: str(act) for lang, act in self._overrides.items()}
        return spec
