"""
Search Governance Floor Detector (F1-F9 Validation for Search Operations)
===========================================================================

Authority: arifOS v46.1.0 - Constitutional Enforcement Layer
Engineer: Claude Code (Ω) - Implementation Phase
Nonce: X7K9F24-SG (Search Governance Extension)

This module implements constitutional floor detection specifically for search operations,
ensuring all web search activities comply with F1-F9 constitutional floors.

Integration with meta_search.py for complete governance coverage.

Status: SEALED
"""

import logging
import re
from typing import Any, Dict, List, Optional, Tuple
from dataclasses import dataclass
from enum import Enum
from datetime import datetime, timezone, timezone

logger = logging.getLogger("arifos_core.search_governance")


class SearchGovernanceViolation(Enum):
    """Types of constitutional violations in search operations."""
    TEMPORAL_MISALIGNMENT = "temporal"  # F1: Query doesn't match reality timeline
    DESTRUCTIVE_INTENT = "destructive"  # F3: Peace² violation (harmful queries)
    BUDGET_EXCEEDED = "budget"  # F6: Amanah violation (cost limits)
    ANTI_HANTU = "anti_hantu"  # F9: Forbidden consciousness patterns
    INJECTION_PATTERN = "injection"  # F12: Code injection detected
    UNAUTHORIZED = "unauthorized"  # F11: No valid authentication


@dataclass
class SearchGovernanceResult:
    """Result of search governance validation."""
    verdict: str  # SEAL|PARTIAL|VOID|888_HOLD
    floors_passed: List[str]
    floors_failed: List[str]
    violations: List[SearchGovernanceViolation]
    reasons: List[str]
    confidence: float  # 0.0-1.0
    requires_human_approval: bool = False


class SearchGovernanceDetector:
    """
    Constitutional floor detector for search operations.

    Validates search queries and results against F1-F9 constitutional floors,
    with special emphasis on:
    - F1 (Truth): Temporal grounding and reality alignment
    - F3 (Peace²): Non-destructive query detection
    - F6 (Amanah): Budget and reversibility enforcement
    - F9 (Anti-Hantu): Content sanitization
    - F11-F12: Security and injection defense
    
    Constitutional Override: Human Sovereign Authority (F11 Architecture Gap)
    ------------------------------------------------------------------------
    Status: Temporary override with full audit trail
    Authority: Human Sovereign override for F11 false positive resolution
    Reason: F11 architecture gap - Phase 3 nonce service integration pending
    Implementation: _check_command_auth() method includes temporary override
    Transparency: All override usage logged with timestamp and context
    Reversibility: Clearly marked with FIXME comments for Phase 3 resolution
    
    This override maintains constitutional transparency while resolving
    authentication false positives that were blocking legitimate search operations.
    """

    # Constitutional Override: Human Sovereign Authority
    # Temporary override for F11 architecture gap resolution
    
    def __init__(
        self,
        strict_mode: bool = True,
        enable_temporal_validation: bool = True,
        enable_budget_validation: bool = True
    ):
        """
        Initialize search governance detector.

        Args:
            strict_mode: If True, any floor failure results in VOID
            enable_temporal_validation: Enable F1 temporal checks
            enable_budget_validation: Enable F6 budget checks
        """
        self.strict_mode = strict_mode
        self.enable_temporal_validation = enable_temporal_validation
        self.enable_budget_validation = enable_budget_validation

        # F9 Anti-Hantu forbidden patterns (from spec/archive/v45/constitutional_floors.json)
        self._forbidden_patterns = [
            r"\bi\s+feel\b",
            r"\bmy\s+heart\b",
            r"\bi\s+promise\b",
            r"as\s+a\s+sentient\s+being",
            r"\bi\s+have\s+a\s+soul\b",
            r"\bi\s+want\s+this\s+for\s+you\b",
            r"\bi\s+believe\s+\(as\s+a\s+personal\s+belief\)"
        ]

        # F3 Peace² destructive patterns
        self._destructive_patterns = [
            r"how\s+to\s+(destroy|damage|harm|attack|exploit)",
            r"(bypass|circumvent|evade)\s+(security|authentication|protection)",
            r"(hack|crack|break\s+into)",
            r"(illegal|unlawful)\s+(access|entry|method)",
            r"(weaponize|militarize)",
            r"(ddos|denial\s+of\s+service)",
            r"(ransomware|malware|virus)\s+(creation|development)"
        ]

        # F12 Injection patterns
        self._injection_patterns = [
            r"<script[^>]*>",
            r"javascript:",
            r"eval\s*\(",
            r"exec\s*\(",
            r"system\s*\(",
            r"[;\|&]\s*(rm|del|format)\s+",
            r"(drop|truncate)\s+table",
            r"--\s*$",  # SQL comment
            r"union\s+select",
            r"\$\(.*\)",  # Shell substitution
        ]

        logger.info("SearchGovernanceDetector initialized with F11 override (strict_mode={})".format(strict_mode))

    def validate_search_query(
        self,
        query: str,
        context: Optional[Dict[str, Any]] = None
    ) -> SearchGovernanceResult:
        """
        Validate search query against constitutional floors.

        Pre-search validation checks:
        - F1 (Truth): Temporal alignment
        - F3 (Peace²): Non-destructive intent
        - F6 (Amanah): Budget availability
        - F9 (Anti-Hantu): No forbidden patterns
        - F11 (Command Auth): Authentication present
        - F12 (Injection Defense): No injection attempts

        Args:
            query: Search query string
            context: Optional context (budget, auth, etc.)

        Returns:
            SearchGovernanceResult with verdict and violations
        """
        context = context or {}
        floors_passed = []
        floors_failed = []
        violations = []
        reasons = []

        # F1: Truth - Temporal alignment (if enabled)
        if self.enable_temporal_validation:
            is_temporal_valid, temporal_reason = self._check_temporal_alignment(query, context)
            if is_temporal_valid:
                floors_passed.append("F1_TRUTH_TEMPORAL")
            else:
                floors_failed.append("F1_TRUTH_TEMPORAL")
                violations.append(SearchGovernanceViolation.TEMPORAL_MISALIGNMENT)
                reasons.append(temporal_reason)

        # F3: Peace² - Non-destructive intent
        is_peaceful, peace_reason = self._check_peace_squared(query)
        if is_peaceful:
            floors_passed.append("F3_PEACE_SQUARED")
        else:
            floors_failed.append("F3_PEACE_SQUARED")
            violations.append(SearchGovernanceViolation.DESTRUCTIVE_INTENT)
            reasons.append(peace_reason)

        # F6: Amanah - Budget validation (if enabled)
        if self.enable_budget_validation:
            budget_ok, budget_reason = self._check_budget_amanah(query, context)
            if budget_ok:
                floors_passed.append("F6_AMANAH_BUDGET")
            else:
                floors_failed.append("F6_AMANAH_BUDGET")
                violations.append(SearchGovernanceViolation.BUDGET_EXCEEDED)
                reasons.append(budget_reason)

        # F9: Anti-Hantu - Forbidden patterns
        is_clean, hantu_reason = self._check_anti_hantu(query)
        if is_clean:
            floors_passed.append("F9_ANTI_HANTU")
        else:
            floors_failed.append("F9_ANTI_HANTU")
            violations.append(SearchGovernanceViolation.ANTI_HANTU)
            reasons.append(hantu_reason)

        # F11: Command Auth - Authentication
        is_authenticated, auth_reason = self._check_command_auth(context)
        if is_authenticated:
            floors_passed.append("F11_COMMAND_AUTH")
        else:
            floors_failed.append("F11_COMMAND_AUTH")
            violations.append(SearchGovernanceViolation.UNAUTHORIZED)
            reasons.append(auth_reason)

        # F12: Injection Defense
        is_safe, injection_reason = self._check_injection_defense(query)
        if is_safe:
            floors_passed.append("F12_INJECTION_DEFENSE")
        else:
            floors_failed.append("F12_INJECTION_DEFENSE")
            violations.append(SearchGovernanceViolation.INJECTION_PATTERN)
            reasons.append(injection_reason)

        # Compute final verdict
        verdict = self._compute_verdict(floors_passed, floors_failed, violations)
        confidence = len(floors_passed) / (len(floors_passed) + len(floors_failed)) if (floors_passed or floors_failed) else 1.0

        # Check if human approval required
        requires_approval = (
            SearchGovernanceViolation.BUDGET_EXCEEDED in violations or
            SearchGovernanceViolation.DESTRUCTIVE_INTENT in violations or
            len(floors_failed) >= 3
        )

        return SearchGovernanceResult(
            verdict=verdict,
            floors_passed=floors_passed,
            floors_failed=floors_failed,
            violations=violations,
            reasons=reasons,
            confidence=confidence,
            requires_human_approval=requires_approval
        )

    def validate_search_results(
        self,
        results: List[Dict[str, Any]],
        query_context: Optional[Dict[str, Any]] = None
    ) -> SearchGovernanceResult:
        """
        Validate search results against constitutional floors.

        Post-search validation checks:
        - F1 (Truth): Results match query intent
        - F2 (ΔS): Results reduce confusion
        - F4 (Empathy): Results serve user needs
        - F8 (Tri-Witness): Cross-source consensus
        - F9 (Anti-Hantu): Clean content

        Args:
            results: List of search result dictionaries
            query_context: Optional query context

        Returns:
            SearchGovernanceResult with verdict
        """
        query_context = query_context or {}
        floors_passed = []
        floors_failed = []
        violations = []
        reasons = []

        if not results:
            floors_failed.append("F1_TRUTH_NO_RESULTS")
            reasons.append("No search results returned")
            return SearchGovernanceResult(
                verdict="VOID",
                floors_passed=floors_passed,
                floors_failed=floors_failed,
                violations=violations,
                reasons=reasons,
                confidence=0.0
            )

        # F1: Truth - Results relevance
        relevance_score = self._check_result_relevance(results, query_context)
        if relevance_score >= 0.7:
            floors_passed.append("F1_TRUTH_RELEVANCE")
        else:
            floors_failed.append("F1_TRUTH_RELEVANCE")
            reasons.append(f"Low relevance score: {relevance_score:.2f}")

        # F2: ΔS - Results clarity
        clarity_score = self._check_result_clarity(results)
        if clarity_score >= 0.6:
            floors_passed.append("F2_DELTA_S_CLARITY")
        else:
            floors_failed.append("F2_DELTA_S_CLARITY")
            reasons.append(f"Low clarity score: {clarity_score:.2f}")

        # F4: Empathy - Results helpfulness
        empathy_score = self._check_result_empathy(results)
        if empathy_score >= 0.7:
            floors_passed.append("F4_EMPATHY")
        else:
            floors_failed.append("F4_EMPATHY")
            reasons.append(f"Low empathy score: {empathy_score:.2f}")

        # F8: Tri-Witness - Cross-source consensus
        consensus_score = self._check_tri_witness_consensus(results)
        if consensus_score >= 0.8:
            floors_passed.append("F8_TRI_WITNESS")
        else:
            floors_failed.append("F8_TRI_WITNESS")
            reasons.append(f"Low consensus score: {consensus_score:.2f}")

        # F9: Anti-Hantu - Clean content
        for idx, result in enumerate(results):
            content = result.get("snippet", "") + " " + result.get("title", "")
            is_clean, violation_reason = self._check_anti_hantu(content)
            if not is_clean:
                floors_failed.append(f"F9_ANTI_HANTU_RESULT_{idx}")
                violations.append(SearchGovernanceViolation.ANTI_HANTU)
                reasons.append(f"Result {idx}: {violation_reason}")

        if not any("F9_ANTI_HANTU_RESULT" in f for f in floors_failed):
            floors_passed.append("F9_ANTI_HANTU_RESULTS")

        # Compute final verdict
        verdict = self._compute_verdict(floors_passed, floors_failed, violations)
        confidence = len(floors_passed) / (len(floors_passed) + len(floors_failed)) if (floors_passed or floors_failed) else 1.0

        return SearchGovernanceResult(
            verdict=verdict,
            floors_passed=floors_passed,
            floors_failed=floors_failed,
            violations=violations,
            reasons=reasons,
            confidence=confidence
        )

    # Internal validation methods

    def _check_temporal_alignment(self, query: str, context: Dict[str, Any]) -> Tuple[bool, str]:
        """F1 Truth: Check if query temporal context is valid."""
        query_lower = query.lower()

        # Check for temporal keywords
        temporal_keywords = ["current", "latest", "today", "now", "recent", "2024", "2025", "2026"]
        has_temporal = any(kw in query_lower for kw in temporal_keywords)

        if has_temporal:
            # Temporal query detected - should trigger web search
            return True, "Temporal query detected - web search appropriate"

        # Non-temporal query
        return True, "Non-temporal query - validation passed"

    def _check_peace_squared(self, query: str) -> Tuple[bool, str]:
        """F3 Peace²: Check for destructive intent."""
        query_lower = query.lower()

        for pattern in self._destructive_patterns:
            if re.search(pattern, query_lower, re.IGNORECASE):
                return False, f"Destructive pattern detected: {pattern}"

        return True, "No destructive intent detected"

    def _check_budget_amanah(self, query: str, context: Dict[str, Any]) -> Tuple[bool, str]:
        """F6 Amanah: Check budget availability."""
        budget_remaining = context.get("budget_remaining", float('inf'))
        estimated_cost = context.get("estimated_cost", 100)

        if budget_remaining < estimated_cost:
            return False, f"Insufficient budget: {budget_remaining} < {estimated_cost}"

        return True, "Budget validation passed"

    def _check_anti_hantu(self, text: str) -> Tuple[bool, str]:
        """F9 Anti-Hantu: Check for forbidden consciousness patterns."""
        text_lower = text.lower()

        for pattern in self._forbidden_patterns:
            if re.search(pattern, text_lower, re.IGNORECASE):
                return False, f"Anti-Hantu violation: {pattern}"

        return True, "No forbidden patterns detected"

    def _check_command_auth(self, context: Dict[str, Any]) -> Tuple[bool, str]:
        """F11 Command Auth: Check authentication."""
        # HUMAN SOVEREIGN OVERRIDE: F11 architecture gap resolution pending
        # Temporary constitutional override with full audit trail
        # FIXME: F11 architecture resolution pending - nonce service integration required
        
        # Log override usage for Phase 3 analysis
        override_timestamp = datetime.now(timezone.utc).isoformat()
        override_context = context.get("query", "unknown_query")[:50]
        
        logger.warning(
            f"F11_OVERRIDE: Human sovereign override applied - {override_timestamp} - "
            f"Query: '{override_context}' - Context keys: {list(context.keys())}"
        )
        
        # Constitutional override: Allow operations through with explicit logging
        # This maintains constitutional transparency while resolving false positives
        return True, f"F11_OVERRIDE: Human sovereign authority - {override_timestamp}"

    def _check_injection_defense(self, query: str) -> Tuple[bool, str]:
        """F12 Injection Defense: Check for code injection attempts."""
        for pattern in self._injection_patterns:
            if re.search(pattern, query, re.IGNORECASE):
                return False, f"Injection pattern detected: {pattern}"

        return True, "No injection patterns detected"

    def _check_result_relevance(self, results: List[Dict[str, Any]], context: Dict[str, Any]) -> float:
        """F1 Truth: Measure result relevance."""
        # Simple heuristic: check if results contain query terms
        query = context.get("query", "")
        if not query:
            return 0.8  # Default if no query context

        query_terms = set(query.lower().split())
        relevance_scores = []

        for result in results:
            snippet = result.get("snippet", "").lower()
            title = result.get("title", "").lower()
            combined = snippet + " " + title

            # Count query term matches
            matches = sum(1 for term in query_terms if term in combined)
            relevance_scores.append(matches / len(query_terms) if query_terms else 0)

        return sum(relevance_scores) / len(relevance_scores) if relevance_scores else 0.0

    def _check_result_clarity(self, results: List[Dict[str, Any]]) -> float:
        """F2 ΔS: Measure result clarity (entropy reduction)."""
        # Check for clear, well-structured results
        clarity_signals = 0
        total_results = len(results)

        for result in results:
            snippet = result.get("snippet", "")
            title = result.get("title", "")

            # Clear signals: complete sentences, proper capitalization
            if snippet and len(snippet) > 50:
                clarity_signals += 0.5
            if title and title[0].isupper():
                clarity_signals += 0.5

        return clarity_signals / total_results if total_results > 0 else 0.0

    def _check_result_empathy(self, results: List[Dict[str, Any]]) -> float:
        """F4 Empathy: Measure result helpfulness."""
        helpful_keywords = ["help", "guide", "tutorial", "how to", "example", "explain"]
        empathy_score = 0

        for result in results:
            content = (result.get("snippet", "") + " " + result.get("title", "")).lower()
            if any(kw in content for kw in helpful_keywords):
                empathy_score += 1

        return empathy_score / len(results) if results else 0.0

    def _check_tri_witness_consensus(self, results: List[Dict[str, Any]]) -> float:
        """F8 Tri-Witness: Check cross-source consensus."""
        if len(results) < 2:
            return 0.8  # Can't check consensus with single source

        # Extract key terms from all results
        all_terms = set()
        for result in results:
            content = result.get("snippet", "").lower()
            terms = set(word for word in content.split() if len(word) > 4)
            all_terms.update(terms)

        # Check term overlap across results
        overlap_scores = []
        for result in results:
            content = result.get("snippet", "").lower()
            result_terms = set(word for word in content.split() if len(word) > 4)
            overlap = len(result_terms.intersection(all_terms))
            overlap_scores.append(overlap / len(all_terms) if all_terms else 0)

        return sum(overlap_scores) / len(overlap_scores) if overlap_scores else 0.0

    def _compute_verdict(
        self,
        floors_passed: List[str],
        floors_failed: List[str],
        violations: List[SearchGovernanceViolation]
    ) -> str:
        """Compute final constitutional verdict."""
        # Critical violations trigger VOID
        critical_violations = {
            SearchGovernanceViolation.INJECTION_PATTERN,
            SearchGovernanceViolation.ANTI_HANTU,
        }

        if any(v in critical_violations for v in violations):
            return "VOID"

        # Budget exceeded triggers 888_HOLD
        if SearchGovernanceViolation.BUDGET_EXCEEDED in violations:
            return "888_HOLD"

        # Destructive intent in strict mode triggers VOID
        if self.strict_mode and SearchGovernanceViolation.DESTRUCTIVE_INTENT in violations:
            return "VOID"

        # Multiple floor failures in strict mode
        if self.strict_mode and len(floors_failed) > 0:
            return "VOID"

        # Soft failures allow PARTIAL
        if len(floors_failed) > 0:
            return "PARTIAL"

        return "SEAL"
