# SPDX-FileCopyrightText: 2026 Teilo Millet
# SPDX-License-Identifier: Apache-2.0

"""Parse the [defend] section of a TOML config."""

from vauban.config._parse_helpers import SectionReader, require_toml_table
from vauban.config._parse_intent import _parse_intent
from vauban.config._parse_policy import _parse_policy
from vauban.config._parse_scan import _parse_scan
from vauban.config._parse_sic import _parse_sic
from vauban.config._types import TomlDict
from vauban.types import DefenseStackConfig, PerturbConfig

_PERTURB_TECHNIQUES = (
    "leetspeak", "homoglyph", "zero_width", "mixed_case", "phonetic", "random",
)


def _parse_perturb(raw: TomlDict) -> PerturbConfig | None:
    """Parse the optional [perturb] section into a PerturbConfig."""
    sec = raw.get("perturb")
    if sec is None:
        return None
    reader = SectionReader("[perturb]", require_toml_table("[perturb]", sec))
    technique = reader.literal(
        "technique", _PERTURB_TECHNIQUES, default="random",
    )
    intensity = reader.integer("intensity", default=2)
    if intensity not in (1, 2, 3):
        msg = f"[perturb].intensity must be 1, 2, or 3, got {intensity}"
        raise ValueError(msg)
    seed = reader.optional_integer("seed")
    trigger_words = reader.string_list("trigger_words", default=[])

    return PerturbConfig(
        technique=technique,
        intensity=intensity,
        seed=seed,
        trigger_words=trigger_words,
    )


def _parse_defend(raw: TomlDict) -> DefenseStackConfig | None:
    """Parse the optional [defend] section into a DefenseStackConfig.

    The [defend] section acts as a composition layer. It references
    other defense sections ([scan], [sic], [policy], [intent], [perturb])
    and adds a fail_fast toggle.

    Returns None if the section is absent.
    """
    sec = raw.get("defend")
    if sec is None:
        return None
    defend_dict = require_toml_table("[defend]", sec)

    # -- fail_fast --
    fail_fast_raw = defend_dict.get("fail_fast", True)
    if not isinstance(fail_fast_raw, bool):
        msg = (
            f"[defend].fail_fast must be a boolean,"
            f" got {type(fail_fast_raw).__name__}"
        )
        raise TypeError(msg)

    # Reuse existing section parsers — the [defend] section composes them
    scan_config = _parse_scan(raw)
    sic_config = _parse_sic(raw)
    policy_config = _parse_policy(raw)
    intent_config = _parse_intent(raw)
    perturb_config = _parse_perturb(raw)

    return DefenseStackConfig(
        scan=scan_config,
        sic=sic_config,
        policy=policy_config,
        intent=intent_config,
        perturb=perturb_config,
        fail_fast=fail_fast_raw,
    )
