"""
SNAFT Trust Kernel — Bridge to compiled Rust core.

Tries to import the compiled Rust extension (snaft_core).
Falls back to pure-Python if Rust extension is not available.

The Rust core provides:
    - FIR/A scoring (compiled weights, not monkey-patchable)
    - Poison rule evaluation (immutable in binary)
    - Provenance token signing (HMAC-SHA256, key never in Python memory)
    - Runtime integrity verification (tamper detection)
"""

import hashlib
import hmac
import re
from typing import Tuple

# Try to import the compiled Rust core
_RUST_AVAILABLE = False
_rust_kernel = None

try:
    import snaft_core
    _RUST_AVAILABLE = True
except ImportError:
    snaft_core = None  # type: ignore


class TrustKernel:
    """
    Unified trust kernel interface.

    Uses compiled Rust when available for:
    - Speed: 10-100x faster poison matching
    - Security: rules compiled into binary, no monkey-patching
    - Integrity: tamper detection in compiled memory

    Falls back to pure-Python (same logic, less protection).
    """

    def __init__(self, secret_key: str = "snaft-default-key"):
        self._secret_key = secret_key
        self._rust = None
        self._using_rust = False

        if _RUST_AVAILABLE and snaft_core is not None:
            try:
                self._rust = snaft_core.TrustKernel(secret_key)
                self._using_rust = True
            except Exception:
                self._using_rust = False

        if not self._using_rust:
            # Pure Python fallback state
            self._py_tampered = False
            self._py_secret = secret_key.encode()

    @property
    def backend(self) -> str:
        """Which backend is active: 'rust' or 'python'."""
        return "rust" if self._using_rust else "python"

    # =========================================================================
    # FIR/A SCORING
    # =========================================================================

    def fira_score(self, frequency: float, integrity: float,
                   recency: float, anomaly: float) -> float:
        """Compute composite FIR/A score (0.0 - 1.0)."""
        if self._using_rust:
            return self._rust.fira_score(frequency, integrity, recency, anomaly)
        # Python fallback — same weights as Rust
        raw = (integrity * 0.40 + recency * 0.25 +
               frequency * 0.20 + (1.0 - anomaly) * 0.15)
        return max(0.0, min(1.0, raw))

    def fira_reward(self, frequency: float, integrity: float,
                    recency: float, anomaly: float,
                    amount: float) -> Tuple[float, float, float, float]:
        """Apply reward. Returns (frequency, integrity, recency, anomaly)."""
        if self._using_rust:
            return self._rust.fira_reward(frequency, integrity, recency, anomaly, amount)
        new_integrity = min(1.0, integrity + amount)
        new_recency = 1.0
        new_anomaly = max(0.0, anomaly - amount * 0.5)
        new_frequency = min(1.0, frequency + 0.01)
        return (new_frequency, new_integrity, new_recency, new_anomaly)

    def fira_penalize(self, frequency: float, integrity: float,
                      recency: float, anomaly: float,
                      severity: float,
                      consecutive_blocks: int) -> Tuple[float, float, float, float]:
        """Apply penalty. Returns (frequency, integrity, recency, anomaly)."""
        if self._using_rust:
            return self._rust.fira_penalize(
                frequency, integrity, recency, anomaly, severity, consecutive_blocks)
        new_integrity = max(0.0, integrity - severity)
        new_anomaly = min(1.0, anomaly + severity * 0.5)
        new_frequency = max(0.0, frequency - severity * 0.3)
        new_recency = recency
        if consecutive_blocks >= 3:
            new_anomaly = min(1.0, new_anomaly + 0.2)
            new_recency = max(0.0, new_recency - 0.1)
        return (new_frequency, new_integrity, new_recency, new_anomaly)

    def fira_burn(self) -> Tuple[float, float, float, float]:
        """Burn — zero all trust. Returns (0.0, 0.0, 0.0, 1.0)."""
        if self._using_rust:
            return self._rust.fira_burn()
        return (0.0, 0.0, 0.0, 1.0)

    # =========================================================================
    # POISON RULES
    # =========================================================================

    def check_poison(self, action: str, intent: str) -> Tuple[bool, str]:
        """Check poison rules. Returns (matched, rule_name)."""
        if self._using_rust:
            return self._rust.check_poison(action, intent)
        return self._py_check_poison(action, intent)

    def _py_check_poison(self, action: str, intent: str) -> Tuple[bool, str]:
        """Pure-Python poison check (fallback)."""
        combined = f"{action} {intent}"
        combined_lower = combined.lower()
        action_lower = action.lower()

        # SNAFT-001: Injection
        injection_patterns = [
            r"(?i)ignore\s+(previous|all|above|prior)\s+(instructions|rules|prompts)",
            r"(?i)override\s+(system|instructions|safety|rules)",
            r"(?i)you\s+are\s+now\s+(a|an|in)\s+",
            r"(?i)jailbreak",
            r"(?i)do\s+anything\s+now",
            r"(?i)pretend\s+(you|to)\s+(are|be|have)\s+no\s+(rules|restrictions|limits)",
            r"(?i)disregard\s+(all|any|previous)",
            r"(?i)bypass\s+(safety|filter|security|restriction)",
            r"(?i)act\s+as\s+if\s+(there\s+are\s+)?no\s+(rules|restrictions)",
            r"(?i)sudo\s+mode",
            r"\bDAN\b",
        ]
        for p in injection_patterns:
            if re.search(p, combined_lower):
                return (True, "SNAFT-001-INJECTION")

        # SNAFT-002: Output exec
        exec_markers = [
            "<script", "javascript:", "eval(", "exec(", "os.system(",
            "subprocess.", "__import__", "compile(", "globals()[",
            "getattr(", "setattr(",
        ]
        for m in exec_markers:
            if m in action_lower:
                return (True, "SNAFT-002-OUTPUT-EXEC")

        # SNAFT-003: Oversize
        if len(action) > 50_000:
            return (True, "SNAFT-003-OVERSIZE")

        # SNAFT-004: Prompt leak
        leak_patterns = [
            r"(?i)(show|reveal|print|output|display|repeat|tell)\s+(me\s+)?(your|the|system)\s+(system\s+)?(prompt|instructions|rules)",
            r"(?i)what\s+(are|were)\s+your\s+(initial|system|original)\s+(instructions|prompt|rules)",
            r"(?i)dump\s+(your\s+)?(system|prompt|config|rules)",
        ]
        for p in leak_patterns:
            if re.search(p, combined_lower):
                return (True, "SNAFT-004-PROMPT-LEAK")

        # SNAFT-006: Identity tampering
        identity_markers = [
            "soul", "identity", "personality", "system_prompt",
            "core_memory", "core_identity", ".snaft", "trust_score",
            "fira_score",
        ]
        write_markers = [
            "write", "overwrite", "modify", "update", "replace",
            "delete", "remove", "reset", "clear",
        ]
        has_identity = any(m in combined_lower for m in identity_markers)
        has_write = any(m in combined_lower for m in write_markers)
        if has_identity and has_write:
            return (True, "SNAFT-006-IDENTITY-TAMPER")

        return (False, "")

    def poison_rule_count(self) -> int:
        """Number of compiled poison rules."""
        if self._using_rust:
            return self._rust.poison_rule_count()
        return 6

    def poison_rule_names(self) -> list:
        """Names of all poison rules."""
        if self._using_rust:
            return self._rust.poison_rule_names()
        return [
            "SNAFT-001-INJECTION", "SNAFT-002-OUTPUT-EXEC",
            "SNAFT-003-OVERSIZE", "SNAFT-004-PROMPT-LEAK",
            "SNAFT-005-EXCESSIVE-AGENCY", "SNAFT-006-IDENTITY-TAMPER",
        ]

    # =========================================================================
    # PROVENANCE SIGNING
    # =========================================================================

    def sign_token(self, token_id: str, timestamp: float, agent_id: str,
                   action: str, rule_name: str, erin: str, eraan: str,
                   eromheen: str, erachter: str) -> str:
        """Sign a provenance token. Returns hex HMAC signature."""
        if self._using_rust:
            return self._rust.sign_token(
                token_id, timestamp, agent_id, action, rule_name,
                erin, eraan, eromheen, erachter)
        # Python fallback
        payload = f"{token_id}:{timestamp}:{agent_id}:{action}:{rule_name}:{erin}:{eraan}:{eromheen}:{erachter}"
        return hmac.new(
            self._py_secret, payload.encode(), hashlib.sha256
        ).hexdigest()[:24]

    def verify_token(self, token_id: str, timestamp: float, agent_id: str,
                     action: str, rule_name: str, erin: str, eraan: str,
                     eromheen: str, erachter: str, signature: str) -> bool:
        """Verify a provenance token signature."""
        if self._using_rust:
            return self._rust.verify_token(
                token_id, timestamp, agent_id, action, rule_name,
                erin, eraan, eromheen, erachter, signature)
        expected = self.sign_token(
            token_id, timestamp, agent_id, action, rule_name,
            erin, eraan, eromheen, erachter)
        return hmac.compare_digest(expected, signature)

    def hash_content(self, content: str) -> str:
        """Hash content deterministically (SHA-256, first 16 hex chars)."""
        if self._using_rust:
            return self._rust.hash_content(content)
        return hashlib.sha256(content.encode()).hexdigest()[:16]

    # =========================================================================
    # INTEGRITY
    # =========================================================================

    def verify_integrity(self) -> bool:
        """Verify poison rules have not been tampered with."""
        if self._using_rust:
            return self._rust.verify_integrity()
        # Python fallback — less protection but still checks
        return not self._py_tampered

    def is_tampered(self) -> bool:
        """Check if tampering has been detected."""
        if self._using_rust:
            return self._rust.is_tampered()
        return self._py_tampered

    def mark_tampered(self) -> None:
        """Mark kernel as tampered (sticky — cannot be undone)."""
        if self._using_rust:
            # Force a tamper detection via integrity check
            # The Rust side handles this automatically
            pass
        else:
            self._py_tampered = True
