"""Base types and internal models."""

from abc import ABC
from dataclasses import dataclass
from enum import StrEnum
from typing import Any, Literal

from mashumaro import DataClassDictMixin
from mashumaro.config import BaseConfig
from mashumaro.mixins.orjson import DataClassORJSONMixin


class SentinelMeta(type):
    """Metaclass for sentinel to improve representation and make falsy.

    Credit to https://stackoverflow.com/a/69243488 .
    """

    def __repr__(cls) -> str:
        """Represent class more like an enum."""
        return f"<{cls.__name__}>"

    def __bool__(cls) -> Literal[False]:
        """Return false as a sentinel is akin to an empty value."""
        return False


class DEFAULT(metaclass=SentinelMeta):
    """Sentinel for default value when None is valid."""


class RequestConfig(BaseConfig):
    """Default Mashumaro config for all request models."""

    omit_default = True


@dataclass(frozen=True)
class Request(ABC, DataClassDictMixin):
    """Omit default in requests to allow Supervisor to set default.

    If None is a valid value, the default value should be the sentinel
    DEFAULT for optional fields.
    """

    class Config(RequestConfig):
        """Mashumaro config."""


@dataclass(frozen=True)
class Options(ABC, DataClassDictMixin):
    """Superclass for Options models to ensure a field is present.

    All fields should be optional. If None is a valid value, use the DEFAULT
    sentinel. Client should only pass changed fields to Supervisor.
    """

    def __post_init__(self) -> None:
        """Validate at least one field is present."""
        if not self.to_dict():
            raise ValueError("At least one field must have a value")

    class Config(RequestConfig):
        """Mashumaro config."""


@dataclass(frozen=True)
class ResponseData(ABC, DataClassDictMixin):
    """Superclass for all response data objects."""


class ResultType(StrEnum):
    """ResultType type."""

    OK = "ok"
    ERROR = "error"


@dataclass(frozen=True, slots=True)
class Response(DataClassORJSONMixin):
    """Response model for all JSON based endpoints."""

    result: ResultType
    data: Any | None = None
    message: str | None = None
    job_id: str | None = None
    error_key: str | None = None
    extra_fields: dict[str, Any] | None = None


@dataclass(frozen=True, slots=True)
class ContainerStats(ResponseData):
    """ContainerStats model."""

    cpu_percent: float
    memory_usage: int
    memory_limit: int
    memory_percent: float
    network_rx: int
    network_tx: int
    blk_read: int
    blk_write: int
