"""Governance configuration dataclass for BeaconLangGraphConfig integration."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Callable


@dataclass
class GovernanceConfig:
    """Configuration for governance policy enforcement.

    Groups governance-specific parameters for use with
    :class:`~lumenova_beacon.tracing.integrations.langchain.BeaconLangGraphConfig`.
    Shared context parameters (``agent_name``, ``session_id``, ``environment``,
    ``user_*``, ``metadata``) are excluded — they are propagated automatically
    from the parent ``BeaconLangGraphConfig``.

    Args:
        stack_ids: Explicit policy stack IDs to evaluate against.  At least
            one of ``stack_ids`` or ``tags`` must be provided.
        tags: Tag-based policy stack discovery.
        fail_open: When ``True`` (default), allow the action if the governance
            API is unreachable or returns an unexpected error.
        enabled_hooks: Set of hook names that should trigger evaluation.
            Valid values: ``'pre_tool'``, ``'post_tool'``, ``'pre_llm'``,
            ``'post_llm'``.  Defaults to all four.
        result_parser: Optional callable ``(result_dict) -> bool`` that
            determines whether the response allows the action.
        timeout: Override the transport timeout (in seconds) for governance
            evaluation calls.
        debug: When ``True``, enable DEBUG-level logging for the governance
            handler and client.
        on_violation: Strategy when a policy blocks an action.
            ``'raise'`` (default) raises ``GovernanceViolationError``.
            ``'stop'`` sets a stopped flag and raises, preventing all
            subsequent tool/LLM calls from proceeding.
        max_violations: After this many violations, escalate to ``'stop'``
            regardless of the ``on_violation`` setting.  ``None`` disables.
        agent_id: Identifier for the agent, included in ``input.source``.
    """

    stack_ids: list[str] | None = None
    tags: list[str] | None = None
    fail_open: bool = True
    enabled_hooks: set[str] | None = None
    result_parser: Callable[[dict[str, Any]], bool] | None = None
    timeout: float | None = None
    debug: bool = False
    on_violation: str = 'raise'
    max_violations: int | None = None
    agent_id: str | None = None

    def __post_init__(self) -> None:
        valid = ('raise', 'stop')
        if self.on_violation not in valid:
            raise ValueError(
                f'on_violation must be one of {valid!r}, got {self.on_violation!r}'
            )
