"""Guardrail config parsing helpers."""
from __future__ import annotations

from typing import Any

from logos_adk.errors import ConfigError
from logos_adk.guardrails import (
    ContentFilter,
    ContentModerationGuard,
    Guardrail,
    HallucinationGuard,
    KeywordGuard,
    MaxLengthGuard,
    RegexGuard,
    ToxicityGuard,
)


def _parse_guardrail_item(item: Any) -> Guardrail:
    if not isinstance(item, dict):
        raise ConfigError("guardrail entries must be mappings")
    guard_type = item.get("type")
    if not isinstance(guard_type, str) or not guard_type:
        raise ConfigError("guardrail entry requires non-empty 'type'")

    if guard_type == "content_filter":
        blocked = item.get("blocked")
        if not isinstance(blocked, list) or not all(isinstance(entry, str) for entry in blocked):
            raise ConfigError("content_filter guard requires a string list 'blocked'")
        return ContentFilter(
            blocked=blocked,
            case_sensitive=bool(item.get("case_sensitive", False)),
            action=str(item.get("action", "block")),
            override_with=str(item.get("override_with", "I can't help with that request.")),
        )
    if guard_type == "regex":
        deny = item.get("deny")
        allow = item.get("allow")
        if deny is not None and (not isinstance(deny, list) or not all(isinstance(entry, str) for entry in deny)):
            raise ConfigError("regex guard 'deny' must be a string list")
        if allow is not None and (not isinstance(allow, list) or not all(isinstance(entry, str) for entry in allow)):
            raise ConfigError("regex guard 'allow' must be a string list")
        return RegexGuard(
            deny=deny,
            allow=allow,
            action=str(item.get("action", "block")),
            message=str(item.get("message", "Content matched a denied pattern")),
        )
    if guard_type == "max_length":
        max_chars = item.get("max_chars")
        if not isinstance(max_chars, int) or max_chars <= 0:
            raise ConfigError("max_length guard requires positive integer 'max_chars'")
        return MaxLengthGuard(max_chars=max_chars, action=str(item.get("action", "block")))
    if guard_type == "keyword":
        keywords = item.get("keywords")
        if not isinstance(keywords, list) or not all(isinstance(entry, str) for entry in keywords):
            raise ConfigError("keyword guard requires a string list 'keywords'")
        return KeywordGuard(
            keywords=keywords,
            action=str(item.get("action", "warn")),
            message=str(item.get("message", "Keyword detected")),
            case_sensitive=bool(item.get("case_sensitive", False)),
            override_with=item.get("override_with"),
        )
    if guard_type == "moderation":
        blocked_categories = item.get("blocked_categories")
        if blocked_categories is not None and (
            not isinstance(blocked_categories, list)
            or not all(isinstance(entry, str) for entry in blocked_categories)
        ):
            raise ConfigError("moderation guard 'blocked_categories' must be a string list")
        return ContentModerationGuard(
            blocked_categories=blocked_categories,
            action=str(item.get("action", "block")),
        )
    if guard_type == "toxicity":
        toxic_terms = item.get("toxic_terms")
        if toxic_terms is not None and (
            not isinstance(toxic_terms, list) or not all(isinstance(entry, str) for entry in toxic_terms)
        ):
            raise ConfigError("toxicity guard 'toxic_terms' must be a string list")
        threshold = item.get("threshold", 1)
        if not isinstance(threshold, int) or threshold <= 0:
            raise ConfigError("toxicity guard 'threshold' must be a positive integer")
        return ToxicityGuard(
            toxic_terms=toxic_terms,
            threshold=threshold,
            action=str(item.get("action", "block")),
        )
    if guard_type == "hallucination":
        required_facts = item.get("required_facts")
        if required_facts is not None and (
            not isinstance(required_facts, list) or not all(isinstance(entry, str) for entry in required_facts)
        ):
            raise ConfigError("hallucination guard 'required_facts' must be a string list")
        reference_text = item.get("reference_text")
        if reference_text is not None and not isinstance(reference_text, str):
            raise ConfigError("hallucination guard 'reference_text' must be a string")
        return HallucinationGuard(
            reference_text=reference_text,
            required_facts=required_facts,
            action=str(item.get("action", "warn")),
        )

    raise ConfigError(f"Unsupported guardrail type: {guard_type}")


def load_guardrails_from_config(config: Any) -> tuple[list[Guardrail], list[Guardrail]]:
    """Build input/output guardrails from YAML config."""
    if config is None:
        return [], []
    if not isinstance(config, dict):
        raise ConfigError("guardrails config must be a mapping")

    unknown_keys = set(config) - {"input", "output", "both"}
    if unknown_keys:
        raise ConfigError(f"Unknown guardrails config sections: {', '.join(sorted(unknown_keys))}")

    def parse_list(key: str) -> list[Guardrail]:
        items = config.get(key, [])
        if not isinstance(items, list):
            raise ConfigError(f"guardrails.{key} must be a list")
        return [_parse_guardrail_item(item) for item in items]

    shared = parse_list("both")
    input_guardrails = parse_list("input") + shared
    output_guardrails = parse_list("output") + shared
    return input_guardrails, output_guardrails


__all__ = ["load_guardrails_from_config"]
