import json
import os
import socket
import subprocess
import sys
import time


def _install_network_guard():
    class _DeniedSocket(socket.socket):
        def connect(self, *args, **kwargs):  # pragma: no cover - defensive
            raise PermissionError("sandbox network access denied")

        def connect_ex(self, *args, **kwargs):  # pragma: no cover - defensive
            raise PermissionError("sandbox network access denied")

    socket.socket = _DeniedSocket
    socket.create_connection = _deny_callable("sandbox network access denied")
    socket.getaddrinfo = _deny_callable("sandbox dns resolution denied")


def _install_readonly_fs_guard():
    import builtins

    original_open = builtins.open

    def guarded_open(file, mode="r", *args, **kwargs):
        write_tokens = ("w", "a", "x", "+")
        if any(token in mode for token in write_tokens):
            raise PermissionError("sandbox filesystem write denied")
        return original_open(file, mode, *args, **kwargs)

    builtins.open = guarded_open
    if hasattr(os, "open"):
        original_os_open = os.open

        def guarded_os_open(path, flags, *args, **kwargs):
            write_flags = (
                getattr(os, "O_WRONLY", 0)
                | getattr(os, "O_RDWR", 0)
                | getattr(os, "O_CREAT", 0)
                | getattr(os, "O_TRUNC", 0)
                | getattr(os, "O_APPEND", 0)
            )
            if flags & write_flags:
                raise PermissionError("sandbox filesystem write denied")
            return original_os_open(path, flags, *args, **kwargs)

        os.open = guarded_os_open

    for name in (
        "remove",
        "unlink",
        "rename",
        "replace",
        "mkdir",
        "makedirs",
        "rmdir",
        "removedirs",
        "chmod",
        "chown",
    ):
        if hasattr(os, name):
            setattr(os, name, _deny_callable("sandbox filesystem mutation denied"))


def _install_subprocess_guard():
    if hasattr(subprocess, "Popen"):
        subprocess.Popen = _deny_callable("sandbox subprocess denied")
    for name in ("run", "call", "check_call", "check_output"):
        if hasattr(subprocess, name):
            setattr(subprocess, name, _deny_callable("sandbox subprocess denied"))
    if hasattr(os, "system"):
        os.system = _deny_callable("sandbox os.system denied")
    if hasattr(os, "popen"):
        os.popen = _deny_callable("sandbox os.popen denied")


def _deny_callable(message):
    def _denied(*_args, **_kwargs):  # pragma: no cover - defensive
        raise PermissionError(message)

    return _denied


def _apply_resource_limits(constraints):
    applied = {}
    unsupported = []
    try:
        import resource  # type: ignore
    except Exception:
        return {"applied": applied, "unsupported": ["resource_module_unavailable"]}

    max_memory_mb = int(constraints.get("max_memory_mb", 256))
    max_cpu_seconds = int(constraints.get("max_cpu_seconds", 5))
    try:
        resource.setrlimit(resource.RLIMIT_AS, (max_memory_mb * 1024 * 1024, max_memory_mb * 1024 * 1024))
        applied["RLIMIT_AS"] = max_memory_mb
    except Exception:
        unsupported.append("RLIMIT_AS")
    try:
        resource.setrlimit(resource.RLIMIT_CPU, (max_cpu_seconds, max_cpu_seconds))
        applied["RLIMIT_CPU"] = max_cpu_seconds
    except Exception:
        unsupported.append("RLIMIT_CPU")
    return {"applied": applied, "unsupported": unsupported}


def run_sandbox_spec(spec):
    """
    Process-isolated sandbox worker.
    Contract: emits artifact-only payload; never touches live core runtime.
    """
    started = time.monotonic()
    objective = str(spec.get("objective") or "").strip()
    constraints = spec.get("constraints") or {}
    if constraints.get("no_network", True):
        _install_network_guard()
    if constraints.get("read_only_fs", True):
        _install_readonly_fs_guard()
    _install_subprocess_guard()
    limit_info = _apply_resource_limits(constraints)

    simulate_ms = constraints.get("simulate_work_ms", 0)
    try:
        simulate_ms = int(simulate_ms)
    except Exception:
        simulate_ms = 0
    if simulate_ms > 0:
        time.sleep(min(simulate_ms / 1000.0, 0.5))

    proposal_candidates = []
    if objective:
        proposal_candidates.append(
            {
                "title": f"Candidate flow for: {objective[:80]}",
                "artifact_type": "task_draft",
                "confidence": 0.5,
                "notes": "sandbox_candidate_stub",
            }
        )

    elapsed_ms = int((time.monotonic() - started) * 1000)
    return {
        "artifact_only": True,
        "message": "sandbox worker completed",
        "proposal_candidates": proposal_candidates,
        "metrics": {"latency_ms": elapsed_ms, "candidate_count": len(proposal_candidates)},
        "traces": [{"step": "sandbox_worker", "objective": objective, "latency_ms": elapsed_ms}],
        "meta": {
            "worker": "hiveos.intelligence.sandbox_worker",
            "version": 2,
            "limits": limit_info,
            "sandbox_env": bool(os.environ.get("HIVE_SANDBOX")),
            "guards": {
                "network": bool(constraints.get("no_network", True)),
                "read_only_fs": bool(constraints.get("read_only_fs", True)),
                "subprocess_blocked": True,
            },
        },
    }


def main():
    raw = sys.stdin.read().strip()
    spec = json.loads(raw or "{}")
    result = run_sandbox_spec(spec)
    print(json.dumps(result, separators=(",", ":")))


if __name__ == "__main__":
    main()
