# coding: utf-8

"""
    STACKIT IaaS API

    This API allows you to create and modify IaaS resources.

    The version of the OpenAPI document: 2
    Contact: stackit-iaas@mail.schwarz
    Generated by OpenAPI Generator (https://openapi-generator.tech)

    Do not edit the class manually.
"""  # noqa: E501

from __future__ import annotations

import json
import pprint
import re  # noqa: F401
from datetime import datetime
from typing import Any, ClassVar, Dict, List, Optional, Set, Union
from uuid import UUID

from pydantic import (
    BaseModel,
    ConfigDict,
    Field,
    StrictBytes,
    StrictStr,
    field_validator,
)
from typing_extensions import Annotated, Self

from stackit.iaas.models.boot_volume import BootVolume
from stackit.iaas.models.server_agent import ServerAgent
from stackit.iaas.models.server_maintenance import ServerMaintenance
from stackit.iaas.models.server_network import ServerNetwork
from stackit.iaas.models.server_networking import ServerNetworking


class Server(BaseModel):
    """
    Representation of a single server object.
    """  # noqa: E501

    affinity_group: Optional[UUID] = Field(
        default=None, description="The affinity group the server is assigned to.", alias="affinityGroup"
    )
    agent: Optional[ServerAgent] = None
    availability_zone: Optional[StrictStr] = Field(
        default=None,
        description="This is the availability zone requested during server creation. If none is provided during the creation request and an existing volume will be used as boot volume it will be set to the same availability zone as the volume. For requests with no volumes involved it will be set to the metro availability zone.",
        alias="availabilityZone",
    )
    boot_volume: Optional[BootVolume] = Field(default=None, alias="bootVolume")
    created_at: Optional[datetime] = Field(
        default=None, description="Date-time when resource was created.", alias="createdAt"
    )
    error_message: Optional[StrictStr] = Field(default=None, description="An error message.", alias="errorMessage")
    id: Optional[UUID] = Field(default=None, description="Universally Unique Identifier (UUID).")
    image_id: Optional[UUID] = Field(default=None, description="Universally Unique Identifier (UUID).", alias="imageId")
    keypair_name: Optional[Annotated[str, Field(strict=True, max_length=127)]] = Field(
        default=None, description="The SSH keypair used during the server creation.", alias="keypairName"
    )
    labels: Optional[Dict[str, Any]] = Field(
        default=None,
        description="Object that represents the labels of an object. Regex for keys: `^(?=.{1,63}$)([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$`. Regex for values: `^(?=.{0,63}$)(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])*$`. Providing a `null` value for a key will remove that key. The `stackit-` prefix is reserved and cannot be used for Keys.",
    )
    launched_at: Optional[datetime] = Field(
        default=None, description="Date-time when resource was launched.", alias="launchedAt"
    )
    machine_type: Annotated[str, Field(strict=True, max_length=127)] = Field(
        description="Name of the machine type the server shall belong to.", alias="machineType"
    )
    maintenance_window: Optional[ServerMaintenance] = Field(default=None, alias="maintenanceWindow")
    metadata: Optional[Dict[str, Any]] = Field(
        default=None,
        description="Object that represents the metadata of an object. Regex for keys: `^[a-zA-Z0-9-_:. ]{1,255}$`. Regex for values: `^.{0,255}$`. Providing a `null` value for a key will remove that key.",
    )
    name: Annotated[str, Field(strict=True, max_length=63)] = Field(description="The name for a Server.")
    networking: Optional[ServerNetworking] = None
    nics: Optional[List[ServerNetwork]] = Field(
        default=None,
        description="The list of network interfaces (NICs) attached to the server. Only shown when detailed information is requested.",
    )
    power_status: Optional[StrictStr] = Field(
        default=None,
        description="The power status of a server. Possible values: `CRASHED`, `ERROR`, `RUNNING`, `STOPPED`.",
        alias="powerStatus",
    )
    security_groups: Optional[List[Annotated[str, Field(strict=True, max_length=127)]]] = Field(
        default=None, description="The initial security groups for the server creation.", alias="securityGroups"
    )
    service_account_mails: Optional[
        Annotated[List[Annotated[str, Field(strict=True, max_length=255)]], Field(max_length=1)]
    ] = Field(
        default=None,
        description="A list of service account mails. Only shown when detailed information is requested.",
        alias="serviceAccountMails",
    )
    status: Optional[StrictStr] = Field(
        default=None,
        description="The status of a server object. Possible values: `ACTIVE`, `BACKING-UP`, `CREATING`, `DEALLOCATED`, `DEALLOCATING`, `DELETED`, `DELETING`, `ERROR`, `INACTIVE`, `MIGRATING`, `PAUSED`, `REBOOT`, `REBOOTING`, `REBUILD`, `REBUILDING`, `RESCUE`, `RESCUING`, `RESIZING`, `RESTORING`, `SNAPSHOTTING`, `STARTING`, `STOPPING`, `UNRESCUING`, `UPDATING`.",
    )
    updated_at: Optional[datetime] = Field(
        default=None, description="Date-time when resource was last updated.", alias="updatedAt"
    )
    user_data: Optional[Union[StrictBytes, StrictStr]] = Field(
        default=None,
        description="User Data that is provided to the server. Must be base64 encoded and is passed via cloud-init to the server. Only shown when detailed information is requested.",
        alias="userData",
    )
    volumes: Optional[List[UUID]] = Field(default=None, description="The list of volumes attached to the server.")
    __properties: ClassVar[List[str]] = [
        "affinityGroup",
        "agent",
        "availabilityZone",
        "bootVolume",
        "createdAt",
        "errorMessage",
        "id",
        "imageId",
        "keypairName",
        "labels",
        "launchedAt",
        "machineType",
        "maintenanceWindow",
        "metadata",
        "name",
        "networking",
        "nics",
        "powerStatus",
        "securityGroups",
        "serviceAccountMails",
        "status",
        "updatedAt",
        "userData",
        "volumes",
    ]

    @field_validator("affinity_group")
    def affinity_group_validate_regular_expression(cls, value):
        """Validates the regular expression"""
        if value is None:
            return value

        if not re.match(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", value):
            raise ValueError(
                r"must validate the regular expression /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/"
            )
        return value

    @field_validator("created_at", mode="before")
    def created_at_change_year_zero_to_one(cls, value):
        """Workaround which prevents year 0 issue"""
        if isinstance(value, str):
            # Check for year "0000" at the beginning of the string
            # This assumes common date formats like YYYY-MM-DDTHH:MM:SS+00:00 or YYYY-MM-DDTHH:MM:SSZ
            if value.startswith("0000-01-01T") and re.match(
                r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\+\d{2}:\d{2}|Z)$", value
            ):
                # Workaround: Replace "0000" with "0001"
                return "0001" + value[4:]  # Take "0001" and append the rest of the string
        return value

    @field_validator("id")
    def id_validate_regular_expression(cls, value):
        """Validates the regular expression"""
        if value is None:
            return value

        if not re.match(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", value):
            raise ValueError(
                r"must validate the regular expression /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/"
            )
        return value

    @field_validator("image_id")
    def image_id_validate_regular_expression(cls, value):
        """Validates the regular expression"""
        if value is None:
            return value

        if not re.match(r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", value):
            raise ValueError(
                r"must validate the regular expression /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/"
            )
        return value

    @field_validator("keypair_name")
    def keypair_name_validate_regular_expression(cls, value):
        """Validates the regular expression"""
        if value is None:
            return value

        if not re.match(r"^[A-Za-z0-9@._-]*$", value):
            raise ValueError(r"must validate the regular expression /^[A-Za-z0-9@._-]*$/")
        return value

    @field_validator("launched_at", mode="before")
    def launched_at_change_year_zero_to_one(cls, value):
        """Workaround which prevents year 0 issue"""
        if isinstance(value, str):
            # Check for year "0000" at the beginning of the string
            # This assumes common date formats like YYYY-MM-DDTHH:MM:SS+00:00 or YYYY-MM-DDTHH:MM:SSZ
            if value.startswith("0000-01-01T") and re.match(
                r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\+\d{2}:\d{2}|Z)$", value
            ):
                # Workaround: Replace "0000" with "0001"
                return "0001" + value[4:]  # Take "0001" and append the rest of the string
        return value

    @field_validator("machine_type")
    def machine_type_validate_regular_expression(cls, value):
        """Validates the regular expression"""
        if not re.match(r"^[A-Za-z0-9]+([ \/._-]*[A-Za-z0-9]+)*$", value):
            raise ValueError(r"must validate the regular expression /^[A-Za-z0-9]+([ \/._-]*[A-Za-z0-9]+)*$/")
        return value

    @field_validator("name")
    def name_validate_regular_expression(cls, value):
        """Validates the regular expression"""
        if not re.match(
            r"^(([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$",
            value,
        ):
            raise ValueError(
                r"must validate the regular expression /^(([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9-]*[A-Za-z0-9])$/"
            )
        return value

    @field_validator("updated_at", mode="before")
    def updated_at_change_year_zero_to_one(cls, value):
        """Workaround which prevents year 0 issue"""
        if isinstance(value, str):
            # Check for year "0000" at the beginning of the string
            # This assumes common date formats like YYYY-MM-DDTHH:MM:SS+00:00 or YYYY-MM-DDTHH:MM:SSZ
            if value.startswith("0000-01-01T") and re.match(
                r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\+\d{2}:\d{2}|Z)$", value
            ):
                # Workaround: Replace "0000" with "0001"
                return "0001" + value[4:]  # Take "0001" and append the rest of the string
        return value

    model_config = ConfigDict(
        populate_by_name=True,
        validate_assignment=True,
        protected_namespaces=(),
    )

    def to_str(self) -> str:
        """Returns the string representation of the model using alias"""
        return pprint.pformat(self.model_dump(by_alias=True))

    def to_json(self) -> str:
        """Returns the JSON representation of the model using alias"""
        # TODO: pydantic v2: use .model_dump_json(by_alias=True, exclude_unset=True) instead
        return json.dumps(self.to_dict())

    @classmethod
    def from_json(cls, json_str: str) -> Optional[Self]:
        """Create an instance of Server from a JSON string"""
        return cls.from_dict(json.loads(json_str))

    def to_dict(self) -> Dict[str, Any]:
        """Return the dictionary representation of the model using alias.

        This has the following differences from calling pydantic's
        `self.model_dump(by_alias=True)`:

        * `None` is only added to the output dict for nullable fields that
          were set at model initialization. Other fields with value `None`
          are ignored.
        * OpenAPI `readOnly` fields are excluded.
        * OpenAPI `readOnly` fields are excluded.
        * OpenAPI `readOnly` fields are excluded.
        * OpenAPI `readOnly` fields are excluded.
        * OpenAPI `readOnly` fields are excluded.
        * OpenAPI `readOnly` fields are excluded.
        * OpenAPI `readOnly` fields are excluded.
        * OpenAPI `readOnly` fields are excluded.
        * OpenAPI `readOnly` fields are excluded.
        """
        excluded_fields: Set[str] = set(
            [
                "created_at",
                "error_message",
                "id",
                "launched_at",
                "maintenance_window",
                "nics",
                "power_status",
                "status",
                "updated_at",
            ]
        )

        _dict = self.model_dump(
            by_alias=True,
            exclude=excluded_fields,
            exclude_none=True,
        )
        # override the default output from pydantic by calling `to_dict()` of agent
        if self.agent:
            _dict["agent"] = self.agent.to_dict()
        # override the default output from pydantic by calling `to_dict()` of boot_volume
        if self.boot_volume:
            _dict["bootVolume"] = self.boot_volume.to_dict()
        # override the default output from pydantic by calling `to_dict()` of maintenance_window
        if self.maintenance_window:
            _dict["maintenanceWindow"] = self.maintenance_window.to_dict()
        # override the default output from pydantic by calling `to_dict()` of networking
        if self.networking:
            _dict["networking"] = self.networking.to_dict()
        # override the default output from pydantic by calling `to_dict()` of each item in nics (list)
        _items = []
        if self.nics:
            for _item_nics in self.nics:
                if _item_nics:
                    _items.append(_item_nics.to_dict())
            _dict["nics"] = _items
        return _dict

    @classmethod
    def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
        """Create an instance of Server from a dict"""
        if obj is None:
            return None

        if not isinstance(obj, dict):
            return cls.model_validate(obj)

        _obj = cls.model_validate(
            {
                "affinityGroup": obj.get("affinityGroup"),
                "agent": ServerAgent.from_dict(obj["agent"]) if obj.get("agent") is not None else None,
                "availabilityZone": obj.get("availabilityZone"),
                "bootVolume": BootVolume.from_dict(obj["bootVolume"]) if obj.get("bootVolume") is not None else None,
                "createdAt": obj.get("createdAt"),
                "errorMessage": obj.get("errorMessage"),
                "id": obj.get("id"),
                "imageId": obj.get("imageId"),
                "keypairName": obj.get("keypairName"),
                "labels": obj.get("labels"),
                "launchedAt": obj.get("launchedAt"),
                "machineType": obj.get("machineType"),
                "maintenanceWindow": (
                    ServerMaintenance.from_dict(obj["maintenanceWindow"])
                    if obj.get("maintenanceWindow") is not None
                    else None
                ),
                "metadata": obj.get("metadata"),
                "name": obj.get("name"),
                "networking": (
                    ServerNetworking.from_dict(obj["networking"]) if obj.get("networking") is not None else None
                ),
                "nics": (
                    [ServerNetwork.from_dict(_item) for _item in obj["nics"]] if obj.get("nics") is not None else None
                ),
                "powerStatus": obj.get("powerStatus"),
                "securityGroups": obj.get("securityGroups"),
                "serviceAccountMails": obj.get("serviceAccountMails"),
                "status": obj.get("status"),
                "updatedAt": obj.get("updatedAt"),
                "userData": obj.get("userData"),
                "volumes": obj.get("volumes"),
            }
        )
        return _obj
