"""
2600 Mode Core - Security research context and authorization

Provides the core framework for 2600 mode operations with:
- Authorization tracking and verification
- Security context management
- Operation logging and audit trails
- Scope validation
"""

import hashlib
import json
import os
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from pathlib import Path
from typing import Any, Callable, Optional

# Singleton instance
_mode2600_instance: Optional["Mode2600"] = None


class AuthorizationType(Enum):
    """Authorization context for security operations."""

    CTF_COMPETITION = "ctf_competition"
    PERSONAL_RECOVERY = "personal_recovery"
    AUTHORIZED_PENTEST = "authorized_pentest"
    MALWARE_ANALYSIS = "malware_analysis"
    SECURITY_RESEARCH = "security_research"
    EDUCATION_LAB = "education_lab"


@dataclass
class SecurityContext:
    """Security context for 2600 mode operations."""

    authorization: AuthorizationType
    scope: str  # Description of authorized scope
    target_hashes: list[str] = field(default_factory=list)  # Authorized targets (hashed)
    started_at: datetime = field(default_factory=datetime.utcnow)
    user_consent: bool = False
    notes: str = ""

    def add_target(self, target: str) -> None:
        """Add authorized target (stored as hash for privacy)."""
        target_hash = hashlib.sha256(target.encode()).hexdigest()[:16]
        if target_hash not in self.target_hashes:
            self.target_hashes.append(target_hash)

    def is_target_authorized(self, target: str) -> bool:
        """Check if target is in authorized scope."""
        target_hash = hashlib.sha256(target.encode()).hexdigest()[:16]
        return target_hash in self.target_hashes or not self.target_hashes

    def to_dict(self) -> dict:
        """Convert to dictionary."""
        return {
            "authorization": self.authorization.value,
            "scope": self.scope,
            "target_count": len(self.target_hashes),
            "started_at": self.started_at.isoformat(),
            "user_consent": self.user_consent,
            "notes": self.notes,
        }


@dataclass
class OperationLog:
    """Audit log entry for security operations."""

    timestamp: datetime
    operation: str
    tool: str
    parameters: dict
    result_summary: str
    context: SecurityContext
    success: bool

    def to_dict(self) -> dict:
        """Convert to dictionary."""
        return {
            "timestamp": self.timestamp.isoformat(),
            "operation": self.operation,
            "tool": self.tool,
            "parameters": {k: str(v)[:100] for k, v in self.parameters.items()},
            "result_summary": self.result_summary[:500],
            "context": self.context.authorization.value,
            "success": self.success,
        }


class Mode2600:
    """
    2600 Mode - Security Research Framework

    Provides centralized management for security research operations with:
    - Authorization verification
    - Operation logging
    - Tool access control
    - Scope validation

    Named after 2600 Magazine, the legendary hacker quarterly.

    Usage:
        mode = Mode2600()

        # Set authorization context
        mode.set_context(SecurityContext(
            authorization=AuthorizationType.CTF_COMPETITION,
            scope="PicoCTF 2024 challenges",
            user_consent=True,
        ))

        # Use tools
        from groknroll.mode2600 import identify_hash, crack_hash
        hash_type = identify_hash("5d41402abc4b2a76b9719d911017c592")
        result = crack_hash("5d41402abc4b2a76b9719d911017c592", wordlist="rockyou.txt")
    """

    DEFAULT_CONFIG_DIR = Path.home() / ".groknroll" / "mode2600"

    def __init__(self, config_dir: Optional[Path] = None):
        """Initialize 2600 mode."""
        self.config_dir = config_dir or self.DEFAULT_CONFIG_DIR
        self.config_dir.mkdir(parents=True, exist_ok=True)

        self._context: Optional[SecurityContext] = None
        self._operation_log: list[OperationLog] = []
        self._tool_hooks: dict[str, list[Callable]] = {}

        # Load saved context if exists
        self._load_context()

        # Display disclaimer on first use
        self._check_first_use()

    def _check_first_use(self) -> None:
        """Display disclaimer on first use."""
        disclaimer_file = self.config_dir / ".disclaimer_accepted"
        if not disclaimer_file.exists():
            self._show_disclaimer()
            disclaimer_file.touch()

    def _show_disclaimer(self) -> None:
        """Show legal/ethical disclaimer."""
        disclaimer = """
╔══════════════════════════════════════════════════════════════════╗
║                    2600 MODE - SECURITY RESEARCH                  ║
╠══════════════════════════════════════════════════════════════════╣
║                                                                   ║
║  This module provides security research tools for:                ║
║  • CTF competitions and security challenges                       ║
║  • Personal password/wallet recovery (YOUR OWN data)              ║
║  • Authorized penetration testing                                 ║
║  • Malware analysis (defensive security)                          ║
║  • Security education and learning                                ║
║                                                                   ║
║  IMPORTANT:                                                       ║
║  • Only use on systems you own or have explicit authorization     ║
║  • Unauthorized access to computer systems is illegal             ║
║  • You are responsible for complying with all applicable laws     ║
║  • All operations are logged for accountability                   ║
║                                                                   ║
║  By using this module, you agree to use it ethically and legally. ║
║                                                                   ║
╚══════════════════════════════════════════════════════════════════╝
"""
        print(disclaimer)

    def _load_context(self) -> None:
        """Load saved security context."""
        context_file = self.config_dir / "context.json"
        if context_file.exists():
            try:
                with open(context_file) as f:
                    data = json.load(f)
                self._context = SecurityContext(
                    authorization=AuthorizationType(data["authorization"]),
                    scope=data["scope"],
                    target_hashes=data.get("target_hashes", []),
                    started_at=datetime.fromisoformat(data["started_at"]),
                    user_consent=data["user_consent"],
                    notes=data.get("notes", ""),
                )
            except Exception:
                pass

    def _save_context(self) -> None:
        """Save security context."""
        if not self._context:
            return

        context_file = self.config_dir / "context.json"
        data = {
            "authorization": self._context.authorization.value,
            "scope": self._context.scope,
            "target_hashes": self._context.target_hashes,
            "started_at": self._context.started_at.isoformat(),
            "user_consent": self._context.user_consent,
            "notes": self._context.notes,
        }

        with open(context_file, "w") as f:
            json.dump(data, f, indent=2)

    def set_context(self, context: SecurityContext) -> None:
        """Set the security context for operations."""
        self._context = context
        self._save_context()
        self.log_operation(
            "set_context",
            "core",
            {"authorization": context.authorization.value, "scope": context.scope},
            f"Context set: {context.authorization.value}",
            True,
        )

    def get_context(self) -> Optional[SecurityContext]:
        """Get current security context."""
        return self._context

    def require_context(self) -> SecurityContext:
        """Get context or raise error if not set."""
        if not self._context:
            raise ValueError(
                "Security context not set. Use mode.set_context() to set authorization."
            )
        return self._context

    def clear_context(self) -> None:
        """Clear security context."""
        self._context = None
        context_file = self.config_dir / "context.json"
        if context_file.exists():
            context_file.unlink()

    def log_operation(
        self,
        operation: str,
        tool: str,
        parameters: dict,
        result_summary: str,
        success: bool,
    ) -> None:
        """Log a security operation."""
        if not self._context:
            return

        log_entry = OperationLog(
            timestamp=datetime.utcnow(),
            operation=operation,
            tool=tool,
            parameters=parameters,
            result_summary=result_summary,
            context=self._context,
            success=success,
        )

        self._operation_log.append(log_entry)

        # Persist to file
        log_file = self.config_dir / "operations.jsonl"
        with open(log_file, "a") as f:
            f.write(json.dumps(log_entry.to_dict()) + "\n")

    def get_operation_log(self, limit: int = 100) -> list[dict]:
        """Get recent operation log entries."""
        return [op.to_dict() for op in self._operation_log[-limit:]]

    def register_tool_hook(
        self,
        tool_name: str,
        hook: Callable[[dict], dict],
    ) -> None:
        """Register a hook to be called before tool execution."""
        if tool_name not in self._tool_hooks:
            self._tool_hooks[tool_name] = []
        self._tool_hooks[tool_name].append(hook)

    def execute_hooks(self, tool_name: str, params: dict) -> dict:
        """Execute hooks for a tool."""
        result = params.copy()
        for hook in self._tool_hooks.get(tool_name, []):
            result = hook(result)
        return result

    def get_stats(self) -> dict:
        """Get 2600 mode statistics."""
        return {
            "context_active": self._context is not None,
            "context": self._context.to_dict() if self._context else None,
            "operations_logged": len(self._operation_log),
            "tools_with_hooks": list(self._tool_hooks.keys()),
            "config_dir": str(self.config_dir),
        }

    # Tool availability checks

    def check_tool_available(self, tool: str) -> dict:
        """Check if a security tool is available on the system."""
        tool_checks = {
            "hashcat": ["hashcat", "--version"],
            "john": ["john", "--list=formats"],
            "ghidra": ["ghidra", "--help"],
            "radare2": ["r2", "-v"],
            "ida": ["ida64", "-h"],
            "gdb": ["gdb", "--version"],
            "objdump": ["objdump", "--version"],
            "strings": ["strings", "--version"],
            "file": ["file", "--version"],
            "binwalk": ["binwalk", "--help"],
            "volatility": ["vol.py", "-h"],
            "wireshark": ["tshark", "-v"],
            "nmap": ["nmap", "--version"],
            "metasploit": ["msfconsole", "-v"],
            "burpsuite": ["burpsuite", "--help"],
            "sqlmap": ["sqlmap", "--version"],
            "gobuster": ["gobuster", "version"],
            "ffuf": ["ffuf", "-V"],
            "pwntools": ["python3", "-c", "import pwn; print(pwn.version)"],
        }

        import shutil
        import subprocess

        if tool not in tool_checks:
            return {"available": False, "error": f"Unknown tool: {tool}"}

        cmd = tool_checks[tool]

        # Check if binary exists
        if not shutil.which(cmd[0]):
            return {"available": False, "error": f"{cmd[0]} not found in PATH"}

        # Try to run version check
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                timeout=5,
            )
            return {
                "available": True,
                "version_output": result.stdout[:200] or result.stderr[:200],
            }
        except Exception as e:
            return {"available": False, "error": str(e)}

    def list_available_tools(self) -> dict[str, bool]:
        """List all security tools and their availability."""
        tools = [
            "hashcat", "john", "ghidra", "radare2", "gdb", "objdump",
            "strings", "file", "binwalk", "volatility", "wireshark",
            "nmap", "metasploit", "sqlmap", "gobuster", "ffuf", "pwntools",
        ]

        result = {}
        for tool in tools:
            check = self.check_tool_available(tool)
            result[tool] = check["available"]

        return result


def get_mode2600() -> Mode2600:
    """Get the global Mode2600 instance."""
    global _mode2600_instance
    if _mode2600_instance is None:
        _mode2600_instance = Mode2600()
    return _mode2600_instance
