"""Matcher mixin and optional acceleration imports for GovernanceEngine."""

from __future__ import annotations

from typing import Any

from acgs_lite.constitution import Severity
from acgs_lite.errors import ConstitutionalViolationError

from .models import ValidationResult, Violation
from .rust import _HAS_AHO, _HAS_RUST, _RUST_ALLOW, _RUST_DENY, _RUST_DENY_CRITICAL

_ac_mod: Any = None
if _HAS_AHO:
    import ahocorasick as _ac_mod  # type: ignore[no-redef]

_rust_mod: Any = None
if _HAS_RUST:
    import acgs_lite_rust as _rust_mod  # type: ignore[no-redef]

_ac = _ac_mod
_rust = _rust_mod


class GovernanceMatcherMixin:
    """Validation helpers shared by GovernanceEngine."""

    _ac_iter: Any
    _no_anchor_patterns: list[tuple[int, Any]]
    _pat_anchor_dispatch: tuple[Any, ...]
    _rule_data: list[tuple[Any, ...]]
    _rule_excs: list[Any]
    _rule_id_to_exc_idx: dict[str, int]
    _rust_validator: Any
    strict: bool

    def _new_fast_allow_result(self) -> ValidationResult:
        raise NotImplementedError

    def _new_fast_result(
        self,
        *,
        valid: bool,
        violations: list[Violation],
        action: str,
    ) -> ValidationResult:
        raise NotImplementedError

    def _validate_rust_no_context(
        self,
        action: str,
        decision: int,
        data: int,
        rule_excs: list[Any],
        fast_records: Any,
    ) -> ValidationResult | None:
        if decision == _RUST_ALLOW:
            fast_records.append(None)
            return self._new_fast_allow_result()
        elif decision == _RUST_DENY_CRITICAL:
            if not (0 <= data < len(rule_excs)):
                fast_records.append(None)
                raise ConstitutionalViolationError(
                    "Critical rule violation (index out of range)",
                    rule_id="UNKNOWN",
                    severity="critical",
                    action=action[:200],
                )
            _e_src = rule_excs[data]
            fast_records.append(None)
            raise ConstitutionalViolationError(
                str(_e_src),
                rule_id=_e_src.rule_id,
                severity=_e_src.severity,
                action=action[:200],
            )
        elif decision == _RUST_DENY:
            _bm = data
            _a200 = action[:200]
            _vlist: list[Violation] = []
            while _bm:
                _idx = (_bm & -_bm).bit_length() - 1
                _bm &= _bm - 1
                _rd = self._rule_data[_idx]
                _vlist.append(Violation(_rd[0], _rd[1], _rd[2], _a200, _rd[4]))
            fast_records.append(None)
            _has_blocking = any(v.severity.blocks() for v in _vlist)
            return self._new_fast_result(valid=not _has_blocking, violations=_vlist, action=action)
        return None

    def _validate_rust_gov_context(
        self,
        action: str,
        decision: int,
        data: int,
        context: dict[str, Any],
        rule_excs: list[Any],
        fast_records: Any,
    ) -> ValidationResult | None:
        _merged_bm = 0
        _has_critical = False
        _crit_idx = -1
        if decision == _RUST_DENY_CRITICAL:
            _crit_idx = data
            _has_critical = True
        elif decision == _RUST_DENY:
            _merged_bm = data
        _ctx_det = context.get("action_detail")
        _ctx_desc = context.get("action_description")
        for _cv in (_ctx_det, _ctx_desc):
            if _cv is not None and isinstance(_cv, str):
                _ctx_dec, _ctx_data = self._rust_validator.validate_hot(
                    _cv if _cv.islower() else _cv.lower()
                )
                if _ctx_dec == _RUST_DENY_CRITICAL and not _has_critical:
                    _crit_idx = _ctx_data
                    _has_critical = True
                elif _ctx_dec == _RUST_DENY:
                    _merged_bm |= _ctx_data
        if _has_critical:
            if not (0 <= _crit_idx < len(rule_excs)):
                fast_records.append(None)
                raise ConstitutionalViolationError(
                    "Critical rule violation (index out of range)",
                    rule_id="UNKNOWN",
                    severity="critical",
                    action=action[:200],
                )
            _e_src = rule_excs[_crit_idx]
            fast_records.append(None)
            raise ConstitutionalViolationError(
                str(_e_src),
                rule_id=_e_src.rule_id,
                severity=_e_src.severity,
                action=action[:200],
            )
        if _merged_bm:
            _a200 = action[:200]
            _vlist: list[Violation] = []
            _bv_ctx: Violation | None = None
            while _merged_bm:
                _idx = (_merged_bm & -_merged_bm).bit_length() - 1
                _merged_bm &= _merged_bm - 1
                _rd = self._rule_data[_idx]
                _v = Violation(_rd[0], _rd[1], _rd[2], _a200, _rd[4])
                _vlist.append(_v)
                if _bv_ctx is None and _v.severity.blocks():
                    _bv_ctx = _v
            if _bv_ctx is not None and self.strict:
                raise ConstitutionalViolationError(
                    f"Action blocked by rule {_bv_ctx.rule_id}: {_bv_ctx.rule_text}",
                    rule_id=_bv_ctx.rule_id,
                    severity=_bv_ctx.severity.value,
                    action=_a200,
                )
            fast_records.append(None)
            _has_blocking = any(v.severity.blocks() for v in _vlist)
            return self._new_fast_result(valid=not _has_blocking, violations=_vlist, action=action)
        fast_records.append(None)
        return self._new_fast_allow_result()

    def _validate_rust_metadata_context(
        self,
        action: str,
        decision: int,
        data: int,
        rule_excs: list[Any],
        fast_records: Any,
        is_noop: bool,
    ) -> ValidationResult | None:
        if decision == _RUST_ALLOW:
            if is_noop:
                fast_records.append(None)
            return self._new_fast_allow_result()
        elif decision == _RUST_DENY_CRITICAL:
            if not (0 <= data < len(rule_excs)):
                if is_noop:
                    fast_records.append(None)
                raise ConstitutionalViolationError(
                    "Critical rule violation (index out of range)",
                    rule_id="UNKNOWN",
                    severity="critical",
                    action=action[:200],
                )
            _e_src = rule_excs[data]
            if is_noop:
                fast_records.append(None)
            raise ConstitutionalViolationError(
                str(_e_src),
                rule_id=_e_src.rule_id,
                severity=_e_src.severity,
                action=action[:200],
            )
        elif decision == _RUST_DENY:
            _bm = data
            _a200 = action[:200]
            _vlist: list[Violation] = []
            while _bm:
                _idx = (_bm & -_bm).bit_length() - 1
                _bm &= _bm - 1
                _rd = self._rule_data[_idx]
                _vlist.append(Violation(_rd[0], _rd[1], _rd[2], _a200, _rd[4]))
            if is_noop:
                fast_records.append(None)
            _has_blocking = any(v.severity.blocks() for v in _vlist)
            return self._new_fast_result(valid=not _has_blocking, violations=_vlist, action=action)
        return None

    def _validate_rust_full(
        self,
        action: str,
        strict: bool,
        ctx_pairs: list[tuple[str, str]],
        rule_excs: list[Any],
        fast_records: Any,
    ) -> ValidationResult | None:
        _decision, _violations, _blocking = self._rust_validator.validate_full(
            action.lower(), ctx_pairs
        )
        if _decision == _RUST_ALLOW:
            if fast_records is not None:
                fast_records.append(None)
                return self._new_fast_allow_result()
        elif _decision == _RUST_DENY_CRITICAL:
            _vt_id = _violations[0][0]
            _idx = self._rule_id_to_exc_idx.get(_vt_id)
            if _idx is None:
                _vt_id, _vt_text, _vt_sev, _, _ = _violations[0]
                raise ConstitutionalViolationError(
                    f"Action blocked by rule {_vt_id}: {_vt_text}",
                    rule_id=_vt_id,
                    severity=_vt_sev,
                    action=action[:200],
                )
            _e_src = rule_excs[_idx]
            raise ConstitutionalViolationError(
                str(_e_src),
                rule_id=_e_src.rule_id,
                severity=_e_src.severity,
                action=action[:200],
            )
        elif _decision == _RUST_DENY:
            _a200 = action[:200]
            _SEV = Severity
            _vlist = [
                Violation(rid, rtxt, _SEV(sev), _a200, cat)
                for rid, rtxt, sev, _, cat in _violations
            ]
            if _blocking and strict:
                _bv = next((v for v in _vlist if v.severity.blocks()), _vlist[0])
                _exc_idx = self._rule_id_to_exc_idx.get(_bv.rule_id, -1)
                if _exc_idx >= 0:
                    _e_src = rule_excs[_exc_idx]
                    raise ConstitutionalViolationError(
                        str(_e_src),
                        rule_id=_e_src.rule_id,
                        severity=_e_src.severity,
                        action=_a200,
                    )
                raise ConstitutionalViolationError(
                    f"Action blocked by rule {_bv.rule_id}: {_bv.rule_text}",
                    rule_id=_bv.rule_id,
                    severity=_bv.severity.value,
                    action=_a200,
                )
            if fast_records is not None:
                return self._new_fast_result(
                    valid=not _blocking,
                    violations=_vlist,
                    action=action,
                )
        return None

    def _validate_python_ac(
        self,
        action: str,
        strict: bool,
        text_lower: str,
        positive_verb_mode: bool,
        violations: list[Violation] | None,
    ) -> list[Violation] | None:
        action_200 = action[:200]
        if positive_verb_mode:
            fired = 0
            _hit_anchors = 0
            for _end_idx, _payload in self._ac_iter(text_lower):
                _ptype = _payload[0]
                if _ptype == 0:
                    for rule_idx, kw_has_neg in _payload[1]:
                        if not kw_has_neg:
                            continue
                        _bit = 1 << rule_idx
                        if fired & _bit:
                            continue
                        fired |= _bit
                        rid, rtxt, rsev, _, rcat, is_crit, _ = self._rule_data[rule_idx]
                        if strict and is_crit:
                            _e_src = self._rule_excs[rule_idx]
                            raise ConstitutionalViolationError(
                                str(_e_src),
                                rule_id=_e_src.rule_id,
                                severity=_e_src.severity,
                                action=action_200,
                            )
                        if violations is None:
                            violations = []
                        violations.append(Violation(rid, rtxt, rsev, action_200, rcat))
                elif _ptype == 1:
                    _hit_anchors |= 1 << _payload[1]
                else:
                    _hit_anchors |= 1 << _payload[2]
                    for rule_idx, kw_has_neg in _payload[1]:
                        if not kw_has_neg:
                            continue
                        _bit = 1 << rule_idx
                        if fired & _bit:
                            continue
                        fired |= _bit
                        rid, rtxt, rsev, _, rcat, is_crit, _ = self._rule_data[rule_idx]
                        if strict and is_crit:
                            _e_src = self._rule_excs[rule_idx]
                            raise ConstitutionalViolationError(
                                str(_e_src),
                                rule_id=_e_src.rule_id,
                                severity=_e_src.severity,
                                action=action_200,
                            )
                        if violations is None:
                            violations = []
                        violations.append(Violation(rid, rtxt, rsev, action_200, rcat))
            if _hit_anchors or self._no_anchor_patterns:
                if _hit_anchors:
                    _tmp_a = _hit_anchors
                    while _tmp_a:
                        _lsb_a = _tmp_a & -_tmp_a
                        _ai = _lsb_a.bit_length() - 1
                        _tmp_a ^= _lsb_a
                        for rule_idx, pat in self._pat_anchor_dispatch[_ai][1]:
                            _bit = 1 << rule_idx
                            if not (fired & _bit) and pat.search(text_lower):
                                fired |= _bit
                                rid, rtxt, rsev, _, rcat, is_crit, _ = self._rule_data[rule_idx]
                                if strict and is_crit:
                                    _e_src = self._rule_excs[rule_idx]
                                    raise ConstitutionalViolationError(
                                        str(_e_src),
                                        rule_id=_e_src.rule_id,
                                        severity=_e_src.severity,
                                        action=action_200,
                                    )
                                if violations is None:
                                    violations = []
                                violations.append(Violation(rid, rtxt, rsev, action_200, rcat))
                for rule_idx, pat in self._no_anchor_patterns:
                    _bit = 1 << rule_idx
                    if not (fired & _bit) and pat.search(text_lower):
                        fired |= _bit
                        rid, rtxt, rsev, _, rcat, is_crit, _ = self._rule_data[rule_idx]
                        if strict and is_crit:
                            _e_src = self._rule_excs[rule_idx]
                            raise ConstitutionalViolationError(
                                str(_e_src),
                                rule_id=_e_src.rule_id,
                                severity=_e_src.severity,
                                action=action_200,
                            )
                        if violations is None:
                            violations = []
                        violations.append(Violation(rid, rtxt, rsev, action_200, rcat))
            return violations
        fired = 0
        _hit_anchors = 0
        for _end_idx, _payload in self._ac_iter(text_lower):
            _ptype = _payload[0]
            if _ptype == 0:
                for rule_idx, _kw_has_neg in _payload[1]:
                    _bit = 1 << rule_idx
                    if fired & _bit:
                        continue
                    fired |= _bit
                    rid, rtxt, rsev, _, rcat, is_crit, _ = self._rule_data[rule_idx]
                    if strict and is_crit:
                        _e_src = self._rule_excs[rule_idx]
                        raise ConstitutionalViolationError(
                            str(_e_src),
                            rule_id=_e_src.rule_id,
                            severity=_e_src.severity,
                            action=action_200,
                        )
                    if violations is None:
                        violations = []
                    violations.append(Violation(rid, rtxt, rsev, action_200, rcat))
            elif _ptype == 1:
                _hit_anchors |= 1 << _payload[1]
            else:
                _hit_anchors |= 1 << _payload[2]
                for rule_idx, _kw_has_neg in _payload[1]:
                    _bit = 1 << rule_idx
                    if fired & _bit:
                        continue
                    fired |= _bit
                    rid, rtxt, rsev, _, rcat, is_crit, _ = self._rule_data[rule_idx]
                    if strict and is_crit:
                        _e_src = self._rule_excs[rule_idx]
                        raise ConstitutionalViolationError(
                            str(_e_src),
                            rule_id=_e_src.rule_id,
                            severity=_e_src.severity,
                            action=action_200,
                        )
                    if violations is None:
                        violations = []
                    violations.append(Violation(rid, rtxt, rsev, action_200, rcat))
        if _hit_anchors or self._no_anchor_patterns:
            if _hit_anchors:
                _tmp_a = _hit_anchors
                while _tmp_a:
                    _lsb_a = _tmp_a & -_tmp_a
                    _ai = _lsb_a.bit_length() - 1
                    _tmp_a ^= _lsb_a
                    for rule_idx, pat in self._pat_anchor_dispatch[_ai][1]:
                        _bit = 1 << rule_idx
                        if not (fired & _bit) and pat.search(text_lower):
                            fired |= _bit
                            rid, rtxt, rsev, _, rcat, is_crit, _ = self._rule_data[rule_idx]
                            if strict and is_crit:
                                _e_src = self._rule_excs[rule_idx]
                                raise ConstitutionalViolationError(
                                    str(_e_src),
                                    rule_id=_e_src.rule_id,
                                    severity=_e_src.severity,
                                    action=action_200,
                                )
                            if violations is None:
                                violations = []
                            violations.append(Violation(rid, rtxt, rsev, action_200, rcat))
            for rule_idx, pat in self._no_anchor_patterns:
                _bit = 1 << rule_idx
                if not (fired & _bit) and pat.search(text_lower):
                    fired |= _bit
                    rid, rtxt, rsev, _, rcat, is_crit, _ = self._rule_data[rule_idx]
                    if strict and is_crit:
                        _e_src = self._rule_excs[rule_idx]
                        raise ConstitutionalViolationError(
                            str(_e_src),
                            rule_id=_e_src.rule_id,
                            severity=_e_src.severity,
                            action=action_200,
                        )
                    if violations is None:
                        violations = []
                    violations.append(Violation(rid, rtxt, rsev, action_200, rcat))
        return violations


__all__ = [
    "GovernanceMatcherMixin",
    "_HAS_AHO",
    "_HAS_RUST",
    "_RUST_ALLOW",
    "_RUST_DENY",
    "_RUST_DENY_CRITICAL",
    "_ac",
    "_rust",
]
