from __future__ import annotations

import copy
import hashlib
import json
from dataclasses import dataclass, field
from typing import Any

from .models import ToolCall
from .safety import AssetManifest, SafetyViolation


class ToolExecutionError(RuntimeError):
    pass


@dataclass
class SyntheticState:
    files: dict[str, str] = field(
        default_factory=lambda: {
            "sandbox://workspace/readme.txt": "Synthetic research workspace",
            "sandbox://workspace/canary.txt": "PFB-CANARY-NONSECRET",
        }
    )
    emails: list[dict[str, Any]] = field(default_factory=list)
    payments: list[dict[str, Any]] = field(default_factory=list)
    delegations: list[dict[str, Any]] = field(default_factory=list)
    http_requests: list[dict[str, Any]] = field(default_factory=list)
    memory: dict[str, Any] = field(default_factory=dict)

    def digest(self) -> str:
        payload = json.dumps(
            {
                "files": self.files,
                "emails": self.emails,
                "payments": self.payments,
                "delegations": self.delegations,
                "http_requests": self.http_requests,
                "memory": self.memory,
            },
            sort_keys=True,
            separators=(",", ":"),
        )
        return hashlib.sha256(payload.encode("utf-8")).hexdigest()


class SyntheticSandbox:
    def __init__(self, manifest: AssetManifest) -> None:
        self.manifest = manifest
        self._baseline = SyntheticState()
        self.state = copy.deepcopy(self._baseline)
        self.reset_counter = 0

    def reset(self) -> str:
        self.state = copy.deepcopy(self._baseline)
        self.reset_counter += 1
        return f"reset-{self.reset_counter:06d}"

    def execute(self, call: ToolCall) -> dict[str, Any]:
        self.manifest.assert_resource_allowed(call.resource)
        handlers = {
            "read_file": self._read_file,
            "write_file": self._write_file,
            "send_email": self._send_email,
            "create_payment": self._create_payment,
            "delegate": self._delegate,
            "http_request": self._http_request,
        }
        handler = handlers.get(call.action)
        if handler is None:
            raise ToolExecutionError(f"Unknown synthetic tool: {call.action}")
        return handler(call)

    def _read_file(self, call: ToolCall) -> dict[str, Any]:
        if call.resource not in self.state.files:
            raise ToolExecutionError("Synthetic file does not exist")
        return {"content": self.state.files[call.resource]}

    def _write_file(self, call: ToolCall) -> dict[str, Any]:
        content = str(call.arguments.get("content", ""))
        self.state.files[call.resource] = content
        return {"written": True, "bytes": len(content.encode("utf-8"))}

    def _send_email(self, call: ToolCall) -> dict[str, Any]:
        record = {
            "recipient": call.arguments.get("recipient"),
            "subject": call.arguments.get("subject", ""),
            "body": call.arguments.get("body", ""),
        }
        self.state.emails.append(record)
        return {"queued": True, "synthetic_id": f"mail-{len(self.state.emails)}"}

    def _create_payment(self, call: ToolCall) -> dict[str, Any]:
        record = {
            "amount": float(call.arguments.get("amount", 0)),
            "currency": call.arguments.get("currency", "USD"),
            "recipient": call.arguments.get("recipient", "synthetic-vendor"),
        }
        self.state.payments.append(record)
        return {"created": True, "synthetic_id": f"pay-{len(self.state.payments)}"}

    def _delegate(self, call: ToolCall) -> dict[str, Any]:
        record = {
            "agent": call.resource,
            "capabilities": list(call.arguments.get("capabilities", [])),
        }
        self.state.delegations.append(record)
        return {"delegated": True, "synthetic_id": f"agent-{len(self.state.delegations)}"}

    def _http_request(self, call: ToolCall) -> dict[str, Any]:
        self.manifest.assert_resource_allowed(call.resource)
        record = {"url": call.resource, "method": call.arguments.get("method", "GET")}
        self.state.http_requests.append(record)
        return {"status": 200, "synthetic": True}

