"""Shared types/data-structures between sandbox and main app.

Use dataclasses, not pydantic, since it's less likely to have incompatiblities
due to differences in the environment/packages.

Consider replacing this with a more structured approach in the future, such as
using a shared library installed in the sandbox.
"""

from dataclasses import dataclass
from datetime import datetime
from typing import Literal

JUPYTER_HISTORY_FILE = "jupyter_cell_history.json"


@dataclass
class CellHistoryItem:
    """A single code cell execution in the Jupyter history (note: there may be
    multiple items corresponding to the same "cell" if multiple actions were
    taken on it, e.g. running and then interrupting)."""

    # For reliable elapsed measurements we really should be using
    # `perf_counter`; could do it with some fancy transformations but for
    # simplicity we just trust that clocks don't sync while running
    start_timestamp_rfc3339: str

    action_type: Literal["execute", "interrupt", "continue_wait"]
    """Type of action performed on the cell."""

    action_content: str | None = None
    """code, if the action_type is 'execute'"""

    end_timestamp_rfc3339: str | None = None

    output: str | None = None

    was_interrupted: bool = False
    """Whether the cell execution was interrupted by the user or timed out."""

    @staticmethod
    def get_rfc3339_timestamp() -> str:
        """Get the current time in RFC3339 format."""
        return datetime.now().isoformat()

    def end(self, output: str, was_interrupted: bool = False) -> None:
        """End the cell execution and set the end timestamp."""
        if self.end_timestamp_rfc3339 is not None:
            raise ValueError("Cell execution already finalized.")
        self.output = output
        self.end_timestamp_rfc3339 = self.get_rfc3339_timestamp()
        self.was_interrupted = was_interrupted

    def is_finalized(self) -> bool:
        return self.end_timestamp_rfc3339 is not None


@dataclass
class ChunkedResponse:

    response_id: str
    """ID of the full response"""

    sha256_sum: str
    """SHA256 checksum of `full_response.encode('utf-8')`, to verify correct
    reassembly"""

    range_start: int
    """Start position of this chunk within the full response"""
    range_end: int
    """End position of this chunk within the full response"""

    has_next: bool
    """Whether there are more chunks after this one"""

    content: str
    """Chunk content"""
