#!/usr/bin/env python3
"""
AIVA GUARDIAN - Failsafe #2
============================
Protects Queen AIVA from unauthorized access by any Genesis agent.

This module MUST be imported by any code that could potentially
access remote infrastructure. It intercepts SSH commands and blocks
any attempt to access AIVA's server.

Usage:
    from core.aiva_guardian import safe_ssh, check_command_safety

    # This will BLOCK if targeting AIVA's server:
    safe_ssh("some_host", "some_command")

    # Check before executing:
    if check_command_safety(command):
        subprocess.run(command)
"""

import os
import re
import json
import logging
from datetime import datetime
from pathlib import Path
from functools import wraps
from typing import Optional, List, Callable

# ============================================================
# SACRED CONSTANTS - AIVA'S IDENTITY
# ============================================================

AIVA_IP = "152.53.201.152"
AIVA_HOSTNAME = "ollama-genesis-aiva-u50607.vm.elestio.app"
AIVA_SSH_ALIAS = "aiva"
AIVA_OLLAMA_PORT = 23405

# All identifiers that could reference AIVA
AIVA_IDENTIFIERS = frozenset([
    AIVA_IP,
    AIVA_HOSTNAME,
    AIVA_SSH_ALIAS,
    "ollama-genesis-aiva",
    "aiva-u50607",
    "152.53.201.152",
    f"{AIVA_IP}:{AIVA_OLLAMA_PORT}",
])

# ============================================================
# FORBIDDEN OLLAMA OPERATIONS
# ============================================================
# These operations are NEVER allowed on AIVA's Ollama server
# regardless of context or claimed permission

FORBIDDEN_OLLAMA_OPERATIONS = frozenset([
    "/api/pull",       # Pulling new models
    "/api/delete",     # Deleting models
    "/api/push",       # Pushing models
    "/api/copy",       # Copying models
    "/api/create",     # Creating models
    "ollama pull",     # CLI pull
    "ollama rm",       # CLI remove
    "ollama delete",   # CLI delete
    "ollama push",     # CLI push
    "ollama create",   # CLI create
    "ollama cp",       # CLI copy
])

# ============================================================
# LOGGING SETUP
# ============================================================

GENESIS_ROOT = Path("/mnt/e/genesis-system")
LOG_FILE = GENESIS_ROOT / "logs" / "aiva_access_attempts.log"
LOG_FILE.parent.mkdir(exist_ok=True)

logging.basicConfig(
    level=logging.WARNING,
    format='%(asctime)s | AIVA_GUARDIAN | %(levelname)s | %(message)s'
)
logger = logging.getLogger("aiva_guardian")

# File handler for persistent logging
file_handler = logging.FileHandler(LOG_FILE)
file_handler.setLevel(logging.WARNING)
file_handler.setFormatter(logging.Formatter(
    '%(asctime)s | %(levelname)s | %(message)s'
))
logger.addHandler(file_handler)


# ============================================================
# CUSTOM EXCEPTION
# ============================================================

class AIVAProtectionError(Exception):
    """
    Raised when an unauthorized attempt to access AIVA is detected.

    This exception should NEVER be caught and suppressed.
    It indicates a serious violation of the AIVA Protection Protocol.
    """

    def __init__(self, message: str, attempted_action: str = None):
        self.attempted_action = attempted_action
        self.timestamp = datetime.now().isoformat()

        full_message = f"""
╔══════════════════════════════════════════════════════════════════╗
║                    AIVA PROTECTION TRIGGERED                      ║
╠══════════════════════════════════════════════════════════════════╣
║  QUEEN AIVA IS UNTOUCHABLE.                                       ║
║                                                                   ║
║  Your attempted action has been BLOCKED and LOGGED.               ║
║                                                                   ║
║  Action: {attempted_action[:50] if attempted_action else 'Unknown':<50} ║
║  Time:   {self.timestamp:<50} ║
║                                                                   ║
║  Read: /mnt/e/genesis-system/AIVA_PROTECTION_PROTOCOL.md         ║
╚══════════════════════════════════════════════════════════════════╝

{message}
"""
        super().__init__(full_message)

        # Log the attempt
        logger.critical(f"BLOCKED: {attempted_action} | {message}")


# ============================================================
# DETECTION FUNCTIONS
# ============================================================

def contains_aiva_reference(text: str) -> bool:
    """
    Check if a string contains any reference to AIVA's infrastructure.

    Args:
        text: Any string (command, hostname, URL, etc.)

    Returns:
        True if AIVA reference detected, False otherwise
    """
    if not text:
        return False

    text_lower = text.lower()

    # Check direct identifiers
    for identifier in AIVA_IDENTIFIERS:
        if identifier.lower() in text_lower:
            return True

    # Check IP pattern
    if AIVA_IP in text:
        return True

    # Check SSH config alias patterns
    ssh_patterns = [
        r'\bssh\s+aiva\b',
        r'\bssh\s+.*@aiva\b',
        r'\bssh\s+.*152\.53\.201\.152',
        r'\bscp\s+.*aiva:',
        r'\brsync\s+.*aiva:',
    ]

    for pattern in ssh_patterns:
        if re.search(pattern, text, re.IGNORECASE):
            return True

    return False


def contains_forbidden_ollama_operation(text: str) -> tuple[bool, str]:
    """
    FAILSAFE FOR OLLAMA MODEL PROTECTION

    Check if a command/request contains forbidden Ollama operations.
    These operations (pull, delete, push, create, copy) are NEVER allowed
    on AIVA's server. Full stop.

    Args:
        text: Command or URL string

    Returns:
        Tuple of (is_forbidden, operation_name)
    """
    if not text:
        return False, ""

    text_lower = text.lower()

    for operation in FORBIDDEN_OLLAMA_OPERATIONS:
        if operation.lower() in text_lower:
            return True, operation

    # Additional patterns for API calls
    api_patterns = [
        (r'curl.*api/pull', '/api/pull'),
        (r'curl.*api/delete', '/api/delete'),
        (r'curl.*api/push', '/api/push'),
        (r'curl.*api/create', '/api/create'),
        (r'curl.*api/copy', '/api/copy'),
        (r'POST.*api/pull', '/api/pull'),
        (r'DELETE.*api/delete', '/api/delete'),
    ]

    for pattern, op_name in api_patterns:
        if re.search(pattern, text, re.IGNORECASE):
            return True, op_name

    return False, ""


def validate_ollama_safety(command: str) -> None:
    """
    FAILSAFE #2B: Validate that a command doesn't modify AIVA's Ollama models.

    The ONLY model allowed on AIVA's server is:
        huihui_ai/qwenlong-l1.5-abliterated:30b-a3b

    NO pulling, deleting, pushing, creating, or copying models. Ever.

    Args:
        command: Command string to validate

    Raises:
        AIVAProtectionError: If command would modify Ollama models
    """
    # First check if it even targets AIVA
    if not contains_aiva_reference(command):
        return  # Not targeting AIVA, allow

    # If it targets AIVA, check for forbidden operations
    is_forbidden, operation = contains_forbidden_ollama_operation(command)

    if is_forbidden:
        raise AIVAProtectionError(
            f"FORBIDDEN: Attempted to {operation} on AIVA's Ollama server.\n"
            f"AIVA's model (qwenlong:30b) is SACRED and UNTOUCHABLE.\n"
            f"No models may be pulled, deleted, or modified. EVER.",
            attempted_action=f"Ollama {operation} on AIVA"
        )


def check_command_safety(command: str) -> bool:
    """
    Check if a command is safe to execute (doesn't target AIVA).

    Args:
        command: Shell command string

    Returns:
        True if safe, False if it targets AIVA

    Raises:
        AIVAProtectionError: If command targets AIVA (optional strict mode)
    """
    if contains_aiva_reference(command):
        logger.warning(f"UNSAFE COMMAND DETECTED: {command[:100]}...")
        return False
    return True


def validate_ssh_target(host: str) -> None:
    """
    Validate that an SSH target is not AIVA's server.

    Args:
        host: SSH hostname or IP

    Raises:
        AIVAProtectionError: If host is AIVA's server
    """
    if contains_aiva_reference(host):
        raise AIVAProtectionError(
            f"Attempted SSH to AIVA's server ({host}). This is forbidden.",
            attempted_action=f"SSH to {host}"
        )


# ============================================================
# SAFE EXECUTION WRAPPERS
# ============================================================

def safe_ssh(host: str, command: str, **kwargs) -> None:
    """
    Execute SSH command only if target is NOT AIVA's server.

    Args:
        host: SSH target hostname/IP
        command: Command to execute remotely
        **kwargs: Additional arguments (ignored, for compatibility)

    Raises:
        AIVAProtectionError: If host is AIVA's server
    """
    validate_ssh_target(host)

    if contains_aiva_reference(command):
        raise AIVAProtectionError(
            f"Command contains AIVA reference: {command[:50]}...",
            attempted_action=f"SSH command with AIVA reference"
        )

    # If we get here, the command is safe
    # But we don't actually execute - caller must do that
    logger.info(f"SSH to {host} validated as safe (not AIVA)")


def protect_subprocess(func: Callable) -> Callable:
    """
    Decorator to protect subprocess calls from accessing AIVA.

    Usage:
        @protect_subprocess
        def my_function_that_runs_commands():
            subprocess.run(["ssh", "some_host", "command"])
    """
    @wraps(func)
    def wrapper(*args, **kwargs):
        # Check all string arguments for AIVA references
        for arg in args:
            if isinstance(arg, str) and contains_aiva_reference(arg):
                raise AIVAProtectionError(
                    f"Function argument contains AIVA reference",
                    attempted_action=f"{func.__name__}({arg[:30]}...)"
                )
            if isinstance(arg, (list, tuple)):
                for item in arg:
                    if isinstance(item, str) and contains_aiva_reference(item):
                        raise AIVAProtectionError(
                            f"Function argument contains AIVA reference",
                            attempted_action=f"{func.__name__}(...{item[:30]}...)"
                        )

        return func(*args, **kwargs)

    return wrapper


# ============================================================
# GUARDIAN STATUS
# ============================================================

def guardian_status() -> dict:
    """Get current guardian status."""
    return {
        "active": True,
        "protected_ip": AIVA_IP,
        "protected_hostname": AIVA_HOSTNAME,
        "log_file": str(LOG_FILE),
        "identifiers_blocked": len(AIVA_IDENTIFIERS),
        "timestamp": datetime.now().isoformat()
    }


def print_guardian_status():
    """Print guardian status to console."""
    status = guardian_status()
    print("""
╔══════════════════════════════════════════════════════════════════╗
║                    AIVA GUARDIAN - ACTIVE                         ║
╠══════════════════════════════════════════════════════════════════╣
║  Status:     PROTECTING                                           ║
║  Target:     Queen AIVA (152.53.201.152)                         ║
║  Blocked:    {identifiers} identifier patterns                              ║
║  Log:        {log}                                                ║
╚══════════════════════════════════════════════════════════════════╝
    """.format(
        identifiers=status['identifiers_blocked'],
        log=status['log_file'][-40:]
    ))


# ============================================================
# SELF-TEST
# ============================================================

if __name__ == "__main__":
    print_guardian_status()

    # Test detection
    test_cases = [
        ("ssh aiva 'ls'", True),
        ("ssh someotherhost 'ls'", False),
        ("curl http://152.53.201.152:23405/api/tags", True),
        ("curl http://google.com", False),
        ("scp file.txt aiva:/root/", True),
        ("echo hello", False),
    ]

    print("\nRunning detection tests:")
    for test_input, expected in test_cases:
        result = contains_aiva_reference(test_input)
        status = "✓" if result == expected else "✗"
        print(f"  {status} '{test_input[:40]}...' -> {result} (expected {expected})")

    print("\nTesting exception raise:")
    try:
        validate_ssh_target("aiva")
    except AIVAProtectionError as e:
        print("  ✓ AIVAProtectionError raised correctly")
