from __future__ import annotations

import dataclasses
import secrets
from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional, Tuple, Union

if TYPE_CHECKING:
    from taskweaver.plugin.context import ArtifactType


@dataclass
class EnvPlugin:
    name: str
    impl: str  # file content for the implementation
    config: Optional[Dict[str, str]]
    loaded: bool


def get_id(length: int = 6, prefix: Optional[str] = None) -> str:
    """Get a random id with the given length and prefix."""
    id = secrets.token_hex(length)
    if prefix is not None:
        return f"{prefix}-{id}"
    return id


@dataclass
class ExecutionArtifact:
    name: str = ""
    type: ArtifactType = "file"
    mime_type: str = ""
    original_name: str = ""
    file_name: str = ""
    file_content: str = ""
    file_content_encoding: Literal["str", "base64"] = "str"
    preview: str = ""

    @staticmethod
    def from_dict(d: Dict[str, str]) -> ExecutionArtifact:
        return ExecutionArtifact(
            name=d["name"],
            # TODO: check artifacts type
            type=d["type"],  # type: ignore
            mime_type=d["mime_type"],
            original_name=d["original_name"],
            file_name=d["file_name"],
            file_content=d["file_content"],
            preview=d["preview"],
        )

    def to_dict(self) -> Dict[str, Any]:
        return dataclasses.asdict(self)


@dataclass
class ExecutionResult:
    execution_id: str
    code: str

    is_success: bool = False
    error: Optional[str] = None

    output: Union[str, List[Tuple[str, str]]] = ""
    stdout: List[str] = dataclasses.field(default_factory=list)
    stderr: List[str] = dataclasses.field(default_factory=list)

    log: List[Tuple[str, str, str]] = dataclasses.field(default_factory=list)
    artifact: List[ExecutionArtifact] = dataclasses.field(default_factory=list)


class Client(ABC):
    """
    Client is the interface for the execution client.
    """

    @abstractmethod
    def start(self) -> None:
        ...

    @abstractmethod
    def stop(self) -> None:
        ...

    @abstractmethod
    def load_plugin(
        self,
        plugin_name: str,
        plugin_code: str,
        plugin_config: Dict[str, str],
    ) -> None:
        ...

    @abstractmethod
    def test_plugin(self, plugin_name: str) -> None:
        ...

    @abstractmethod
    def update_session_var(self, session_var_dict: Dict[str, str]) -> None:
        ...

    @abstractmethod
    def execute_code(self, exec_id: str, code: str) -> ExecutionResult:
        ...


KernelModeType = Literal["local", "container"]


class Manager(ABC):
    """
    Manager is the interface for the execution manager.
    """

    @abstractmethod
    def initialize(self) -> None:
        ...

    @abstractmethod
    def clean_up(self) -> None:
        ...

    @abstractmethod
    def get_session_client(
        self,
        session_id: str,
        env_id: Optional[str] = None,
        session_dir: Optional[str] = None,
        cwd: Optional[str] = None,
    ) -> Client:
        ...

    @abstractmethod
    def get_kernel_mode(self) -> KernelModeType:
        ...
