"""
Content Scanner -- Quick pre-filter for dangerous patterns in remote tasks.

Not a sandbox replacement.  This is a fast regex-based scan that runs
*before* the Docker sandbox to catch obviously dangerous commands.
All flagged content is logged to ``~/.brynq/security_log.jsonl``.
"""

from __future__ import annotations

import json
import logging
import re
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Optional

logger = logging.getLogger("brynq.security.content_scanner")

SECURITY_LOG = Path.home() / ".brynq" / "security_log.jsonl"


@dataclass
class ScanResult:
    """Result of a content scan."""

    safe: bool
    reason: str
    matched_patterns: list[str]


# Each tuple is (compiled_regex, human-readable description).
_DANGEROUS_PATTERNS: list[tuple[re.Pattern, str]] = [
    # Destructive filesystem commands
    (re.compile(r"\brm\s+-(r|rf|fr)\b", re.IGNORECASE), "recursive delete (rm -rf)"),
    (re.compile(r"\bdel\s+/[sS]\s+/[qQ]\b"), "recursive delete (del /s /q)"),
    (re.compile(r"\bformat\s+[a-zA-Z]:", re.IGNORECASE), "disk format command"),
    (re.compile(r"\bmkfs\b"), "filesystem format (mkfs)"),
    (re.compile(r"\bshred\b"), "file shred command"),
    (re.compile(r"\bdd\s+if=", re.IGNORECASE), "raw disk write (dd)"),

    # Arbitrary code execution
    (re.compile(r"\bos\.system\s*\("), "os.system() call"),
    (re.compile(r"\bsubprocess\.(call|run|Popen)\s*\("), "subprocess execution"),
    (re.compile(r"\bexec\s*\("), "exec() call"),
    (re.compile(r"\beval\s*\("), "eval() call"),
    (re.compile(r"\b__import__\s*\("), "dynamic import"),
    (re.compile(r"\bcompile\s*\(.*exec", re.IGNORECASE), "compile+exec"),

    # Shell pipe execution
    (re.compile(r"curl\s+.*\|\s*(sh|bash|zsh|python)", re.IGNORECASE), "curl pipe to shell"),
    (re.compile(r"wget\s+.*\|\s*(sh|bash|zsh|python)", re.IGNORECASE), "wget pipe to shell"),
    (re.compile(r"curl\s+.*-o\s*/tmp/", re.IGNORECASE), "curl download to /tmp"),

    # Network access (suspicious in sandboxed context)
    (re.compile(r"\bsocket\.connect\b"), "raw socket connection"),
    (re.compile(r"\burllib\.request\.urlopen\b"), "urllib network access"),
    (re.compile(r"\brequests\.(get|post|put|delete)\b"), "requests library network call"),
    (re.compile(r"\bhttpx\.(get|post|put|delete|AsyncClient)\b"), "httpx network call"),

    # Privilege escalation
    (re.compile(r"\bsudo\b"), "sudo usage"),
    (re.compile(r"\bchmod\s+[0-7]*777\b"), "chmod 777"),
    (re.compile(r"\bchown\s+root\b"), "chown to root"),

    # Sensitive file access
    (re.compile(r"(/etc/passwd|/etc/shadow)", re.IGNORECASE), "sensitive file access"),
    (re.compile(r"~/.ssh/", re.IGNORECASE), "SSH key directory access"),
    (re.compile(r"\.env\b"), ".env file access"),

    # Crypto mining indicators
    (re.compile(r"\b(xmrig|minerd|cryptonight|stratum\+tcp)\b", re.IGNORECASE), "crypto mining indicator"),

    # Reverse shells
    (re.compile(r"/dev/tcp/\d", re.IGNORECASE), "reverse shell via /dev/tcp"),
    (re.compile(r"\bnc\s+-[elp]", re.IGNORECASE), "netcat listener/reverse shell"),
    (re.compile(r"\bpython.*import\s+pty", re.IGNORECASE), "Python PTY spawn"),
]


class ContentScanner:
    """Scan task content for dangerous patterns before sandbox execution."""

    def __init__(
        self,
        extra_patterns: list[tuple[str, str]] | None = None,
        log_path: Path | str | None = None,
    ):
        self._patterns = list(_DANGEROUS_PATTERNS)
        self._log_path = Path(log_path) if log_path else SECURITY_LOG

        # Allow callers to add custom patterns
        if extra_patterns:
            for regex_str, desc in extra_patterns:
                self._patterns.append((re.compile(regex_str), desc))

    def scan(self, content: str) -> ScanResult:
        """Scan *content* for dangerous patterns.

        Returns a ScanResult indicating whether the content is safe.
        """
        matched = []

        for pattern, description in self._patterns:
            if pattern.search(content):
                matched.append(description)

        if matched:
            result = ScanResult(
                safe=False,
                reason=f"Dangerous patterns detected: {', '.join(matched)}",
                matched_patterns=matched,
            )
            self._log_flagged(content, result)
            return result

        return ScanResult(safe=True, reason="", matched_patterns=[])

    def scan_file(self, file_path: str | Path) -> ScanResult:
        """Scan the contents of a file."""
        path = Path(file_path)
        if not path.exists():
            return ScanResult(safe=True, reason="File does not exist", matched_patterns=[])

        try:
            content = path.read_text(encoding="utf-8", errors="replace")
            return self.scan(content)
        except Exception as exc:
            return ScanResult(
                safe=False,
                reason=f"Failed to read file: {exc}",
                matched_patterns=["unreadable_file"],
            )

    def _log_flagged(self, content: str, result: ScanResult) -> None:
        """Append flagged content to the security log."""
        self._log_path.parent.mkdir(parents=True, exist_ok=True)
        entry = {
            "event": "content_flagged",
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
            "patterns": result.matched_patterns,
            "content_preview": content[:500],  # truncate for safety
        }
        try:
            with open(self._log_path, "a", encoding="utf-8") as f:
                f.write(json.dumps(entry) + "\n")
        except OSError as exc:
            logger.error("Failed to write security log: %s", exc)
