from __future__ import annotations
import asyncio, textwrap
from dataclasses import dataclass
from typing import Any, Dict, Optional

@dataclass
class SandboxResult:
    ok: bool
    stdout: str = ""
    stderr: str = ""
    result_repr: str = ""

class PythonSandbox:
    def __init__(self, timeout_s: float = 5.0):
        self.timeout_s = timeout_s

    async def run(self, code: str, globals_dict: Optional[Dict[str, Any]] = None) -> SandboxResult:
        globals_dict = globals_dict or {}
        local_vars: Dict[str, Any] = {}
        wrapped = textwrap.dedent(code)

        def _exec():
            exec(wrapped, globals_dict, local_vars)  # noqa: S102

        try:
            await asyncio.wait_for(asyncio.to_thread(_exec), timeout=self.timeout_s)
            out = local_vars.get("result", None)
            return SandboxResult(ok=True, result_repr=repr(out))
        except Exception as e:
            return SandboxResult(ok=False, stderr=str(e))
