from typing import Annotated as A
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field
from pydantic.types import StringConstraints
from typing_extensions import NotRequired, Self

from netlab.throttle import Throttler

from ..datatypes.pod import PCCloneSpec


class BuilderPCCloneSpec(PCCloneSpec):
    clone_vh_name: NotRequired[str]
    "The runtime host of cloned VM. ()"


class Spec(BaseModel):
    model_config = ConfigDict(extra="forbid")


class ThrottlerConfig(Spec):
    buckets: int = 1
    refresh: float = 1.0
    stagger: float = 0.0

    def make(self) -> Throttler:
        return Throttler(
            buckets=self.buckets,
            refresh=self.refresh,
            stagger=self.stagger,
        )


# class CloneSpecs(Spec):
#     clone_datastore: str
#     clone_role: str = "NORMAL"
#     clone_type: str = "LINKED"


class DeploymentThrottlerConfigs(Spec):
    EXPERIMENTAL_I_know_what_I_am_doing: Literal[True]

    delete: ThrottlerConfig = Field(default_factory=lambda: ThrottlerConfig(buckets=5, refresh=0, stagger=0.5))
    create: ThrottlerConfig = Field(default_factory=lambda: ThrottlerConfig(buckets=1, refresh=0, stagger=1))
    updown: ThrottlerConfig = Field(default_factory=lambda: ThrottlerConfig(buckets=20, refresh=0, stagger=0))
    fetch: ThrottlerConfig = Field(default_factory=lambda: ThrottlerConfig(buckets=20, refresh=0, stagger=0))

    @classmethod
    def default(cls) -> Self:
        return cls(EXPERIMENTAL_I_know_what_I_am_doing=True)


class DeploymentPlan(Spec):
    # vh_uuid: UUID

    pod_online: bool = False

    delete: bool = False

    source_pod_id: int

    start: int
    number: int
    step: int = 1

    clone_prefix: A[str, StringConstraints(min_length=1)] | Literal[False] = False
    clone_suffix: A[str, StringConstraints(min_length=1)] | Literal[False] = False
    clone_name_template: str = "{default}"

    pc_clone_specs: BuilderPCCloneSpec | None = None

    def get_range(self) -> range:
        range_to = self.start + self.number * self.step
        return range(self.start, range_to, self.step)


class SystemSpec(Spec):
    config: str


class SystemSpec1(Spec):
    name: str


class Manifest(Spec):
    system: SystemSpec | SystemSpec1 | dict[str, Any] | None = None
    range: list[DeploymentPlan]
    throttles: DeploymentThrottlerConfigs = Field(default_factory=DeploymentThrottlerConfigs.default)
