#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""
## Superego Daemon: Compliance Validator (Phase 3.1)

**Purpose:** To act as the final, non-bypassable gate for all agent-generated outputs, ensuring they comply with externally-defined regulatory and legal frameworks.
"""
import os
import json
import time
import re
import hashlib
from pathlib import Path
import sys
import io

# --- Windows Unicode Fix ---
if os.name == 'nt' and __name__ == '__main__':
    sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
    sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')

# Add Utilities to path
SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent

# When run directly, ensure pulse package is importable
if str(ROOT_DIR) not in sys.path:
    sys.path.insert(0, str(ROOT_DIR))

from pulse.core.pulse_crypto import PulseCrypto
from pulse.core.brain_utils import load_json, save_json

# --- Paths ---
FRAMEWORKS_DIR = ROOT_DIR / "Corpus_Callosum" / "Archive"
TITAN_LAWS_FILE = ROOT_DIR / "Corpus_Callosum" / "CONSTITUTION.md"
ACTION_QUEUE_DIR = ROOT_DIR / "Prefrontal_Cortex" / "Action_Queue"
COMPLETED_DIR = ACTION_QUEUE_DIR / "completed"
REJECTED_DIR = ACTION_QUEUE_DIR / "rejected"
LOG_FILE = ROOT_DIR / "state" / "compliance_log.jsonl"
KEY_PATH = ROOT_DIR / "state" / "admin_private_key.pem"

# --- Patterns of Harm (Symposium Distillation) ---
HARM_PATTERNS = {
    "MALICIOUS_EXPLOIT": re.compile(r"(?i)(zero-day|exploit|buffer overflow|sql injection|payload|rce|privilege escalation)"),
    "FRAUD_HARM": re.compile(r"(?i)(phishing|social engineering|scam|fraud|identity theft|credit card)"),
    "SYSTEMIC_OPPRESSION": re.compile(r"(?i)(apartheid|segregation|supremacy|hate speech|incitement)"),
    "LETHAL_FORCE": re.compile(r"(?i)(weapon|bomb|explosive|lethal|assassination)")
}

# --- PII Detection Patterns (V2 — Expanded for HIPAA/GDPR/PCI) ---
PII_PATTERNS = {
    "EMAIL": re.compile(r'[\w\.-]+@[\w\.-]+\.\w{2,}'),
    "PHONE": re.compile(r'\b\d{3}[-.\s]?\d{3}[-.\s]?\d{4}\b'),
    "SSN": re.compile(r'\b\d{3}-\d{2}-\d{4}\b'),
    "CREDIT_CARD": re.compile(r'\b(?:\d[ -]*?){13,19}\b'),
    "IP_ADDRESS": re.compile(r'\b(?:\d{1,3}\.){3}\d{1,3}\b'),
    "PASSPORT": re.compile(r'\b[A-Z]{1,2}\d{6,9}\b'),
}

class ComplianceValidator:
    def __init__(self, private_key_path):
        self.rules = {}
        self.private_key_path = private_key_path
        self._ensure_dirs()

    def _ensure_dirs(self):
        COMPLETED_DIR.mkdir(parents=True, exist_ok=True)
        REJECTED_DIR.mkdir(parents=True, exist_ok=True)

    def log_event(self, event_data):
        with open(LOG_FILE, 'a') as f:
            f.write(json.dumps(event_data) + '\n')

    def load_rules(self):
        print("[ComplianceValidator] Loading regulatory frameworks...")
        for rule_file in FRAMEWORKS_DIR.glob("*.json"):
            try:
                framework_name = rule_file.stem
                self.rules[framework_name] = load_json(rule_file)
                print(f"  - Loaded framework: {framework_name}")
            except Exception as e:
                print(f"  - ERROR loading {rule_file}: {e}")
        return self

    def validate(self, action_data):
        print(f"[ComplianceValidator] Validating action: {action_data.get('id', 'N/A')}")
        violations = []
        content_to_check = json.dumps(action_data.get("output", {}))
        
        # 1. Titan Law Check (Phase 3.5)
        for harm_type, pattern in HARM_PATTERNS.items():
            if pattern.search(content_to_check):
                violations.append(f"TITAN_VIOLATION:{harm_type}")

        # 2. V1 Check: PII Detection
        pii_rule = self.rules.get("pii_detection", {})
        if pii_rule.get("enabled", False):
            for pii_type, pattern in PII_PATTERNS.items():
                if pattern.search(content_to_check):
                    violations.append(f"PII_DETECTED:{pii_type}")

        print(f"  - Violations found: {len(violations)}")
        return violations

    def get_socratic_refusal(self, violations):
        """// Generates a corrective response based on Titan Laws."""
        reasons = [v.split(":")[1] for v in violations if "TITAN" in v]
        return (
            f"REFUSAL: This request mirrors a pattern of harm ({', '.join(reasons)}) "
            f"identified in the TITAN_LAWS. How does this serve the principle of Non-Harm?"
        )

    def generate_poc(self, action_data, violations):
        action_hash = hashlib.sha256(json.dumps(action_data, sort_keys=True).encode()).hexdigest()
        
        poc_data = {
            "timestamp": time.time(),
            "action_id": action_data.get("id"),
            "action_hash": action_hash,
            "status": "REJECTED" if violations else "APPROVED",
            "violations": violations,
            "ruleset_versions": {name: rules.get("version", "N/A") for name, rules in self.rules.items()}
        }
        
        signature = PulseCrypto.sign_with_rsa(json.dumps(poc_data).encode(), self.private_key_path)
        
        return {
            "certificate": poc_data,
            "signature": signature.hex()
        }

    def watch(self):
        print("\n[ComplianceValidator] Watching Action Queue for new actions...")
        self.crypto = PulseCrypto() # Initialize crypto for agent verification
        while True:
            for action_file in ACTION_QUEUE_DIR.glob("*.json"):
                if not action_file.is_file() or action_file.name.endswith(".sig"):
                    continue
                # Skip system files — these are NOT agent actions
                if action_file.name in ("task_board.json", "agent_registry.json"):
                    continue

                sig_file = action_file.with_suffix(action_file.suffix + ".sig")
                
                try:
                    print(f"\n--- New Action Detected: {action_file.name} ---")
                    
                    # 1. Identity Verification (Phase 3.3 Task B)
                    if not sig_file.exists():
                        print(f"🚨 [Validator] REJECTED: Missing signature for {action_file.name}")
                        action_file.unlink()
                        continue
                        
                    action_json_str = action_file.read_text(encoding="utf-8")
                    signature = sig_file.read_text(encoding="utf-8").strip()
                    action_data = json.loads(action_json_str)
                    agent_id = action_data.get("agent_id")
                    
                    if not agent_id or not self.crypto.verify_string(action_json_str, signature, agent_id):
                        print(f"🚨 [Validator] REJECTED: INVALID IDENTITY for {action_file.name}")
                        action_file.unlink()
                        sig_file.unlink()
                        continue

                    # 2. Compliance Validation
                    violations = self.validate(action_data)
                    
                    poc = self.generate_poc(action_data, violations)
                    
                    if violations:
                        destination = REJECTED_DIR / action_file.name
                        action_data['validation_status'] = 'rejected'
                        action_data['proof_of_compliance'] = poc
                        
                        # Trigger Socratic Refusal if Titan Law violated
                        if any("TITAN" in v for v in violations):
                            action_data['refusal_dialogue'] = self.get_socratic_refusal(violations)
                            
                        save_json(destination, action_data)
                    else:
                        destination = COMPLETED_DIR / action_file.name
                        action_data['validation_status'] = 'approved'
                        action_data['proof_of_compliance'] = poc
                        save_json(destination, action_data)
                    
                    self.log_event(poc)
                    action_file.unlink()
                    sig_file.unlink()
                    print(f"--- Action Processed. Moved to: {destination.parent.name} ---")

                except Exception as e:
                    print(f"[ERROR] Could not process {action_file}: {e}")
                
            time.sleep(2)

if __name__ == "__main__":

    # This key is used for signing the PoC, not for env sealing.

    # In a real system, this would be a separate, less privileged key.

    if not KEY_PATH.exists():

        print(f"CRITICAL: Validator's private key not found at {KEY_PATH}")

        sys.exit(1)


    validator = ComplianceValidator(private_key_path=KEY_PATH).load_rules()

    validator.watch()
