# coding: utf-8

"""
    BioT Settings Service

    APIs document description for Settings service

    The version of the OpenAPI document: 2.44.1
    Generated by OpenAPI Generator (https://openapi-generator.tech)

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


from __future__ import annotations
import pprint
import re  # noqa: F401
import json

from datetime import datetime
from pydantic import BaseModel, ConfigDict, Field, StrictBool, StrictStr, field_validator
from typing import Any, ClassVar, Dict, List, Optional
from typing_extensions import Annotated
from biotmed_settings_sdk.models.plugin_cors_config import PluginCorsConfig
from biotmed_settings_sdk.models.subscription_dto import SubscriptionDto

from typing import Optional, Set
from typing_extensions import Self

# BioT Code Start: extra/custom attributes support imports
from typing import Any, Dict, ClassVar, List
from pydantic import Field, model_validator
# BioT Code End

class PluginResponse(BaseModel):
    """
    PluginResponse

    Note: This model supports custom attributes. Recommended access:
      - Pass custom fields via dict unpacking: Model(..., **{"custom_field": "value"})
      - Read custom fields via model.get("custom_field") or model["custom_field"]
    """ # noqa: E501
    name: Annotated[str, Field(min_length=0, strict=True, max_length=57)] = Field(description="The plugin's name, the prefix 'Plugin-' will be prepended to it")
    display_name: Annotated[str, Field(min_length=0, strict=True, max_length=255)] = Field(description="The plugins display name, it will be added to as the value of environment variable BIOT_APP_NAME", alias="displayName")
    version: Annotated[int, Field(le=32767, strict=True, ge=1)] = Field(description="If code is updated this number must be changed, and be larger than the previous number. Note, for zip deployment package type a code is considered to be updated if you provided a file for the code parameter, but for image deployment package type a code is considered to be updated if the imageTag field was provided (the server will not check if the imageTag is the same as the current imageTag that this plugin is using, so you can just not providing this field if you didn't change anything, in order not to be forced to increment the version field)")
    runtime: Optional[Annotated[str, Field(min_length=0, strict=True, max_length=50)]] = Field(default=None, description="For deploymentPackageType = ZIP this field is mandatory, and for deploymentPackageType = IMAGE it's mandatory if imageTag was not provided, otherwise it will be ignored. The runtime that the plugin will run on. Only certain values are supported and only certain values are supported without sending a code file/imageTag, check the documentation for specifics.")
    handler: Optional[Annotated[str, Field(min_length=0, strict=True, max_length=1024)]] = Field(default=None, description="Relevant only for deploymentPackageType = ZIP(mandatory), it will be ignored for IMAGE type. The entry point to the plugin, E.g. 'index.handler' means method 'handler' in file 'index' on the root of the plugin. The default is index.handler")
    timeout: Annotated[int, Field(le=900, strict=True, ge=1)] = Field(description="The max time in seconds that the plugin may run, after which it will be aborted. The default is 3")
    memory_size: Annotated[int, Field(le=10240, strict=True, ge=128)] = Field(description="The max amount of memory in MB that the plugin will have. The default is 128", alias="memorySize")
    environment_variables: Dict[str, StrictStr] = Field(alias="environmentVariables")
    subscriptions: Optional[SubscriptionDto] = None
    enabled_state: StrictStr = Field(description="The activation state of the plugin. The default is ENABLED", alias="enabledState")
    cron_schedule: Optional[Annotated[str, Field(min_length=0, strict=True, max_length=250)]] = Field(default=None, description="A six field cron expression separated with whitespaces, for fields: minutes,hours,day-of-month,month,day-of-week,year. Further details in https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-cron-expressions.html", alias="cronSchedule")
    deployment_package_type: StrictStr = Field(description="The deployment package type of the plugin. Cannot be changed after the creation. The default is ZIP.", alias="deploymentPackageType")
    analytics_db_access: StrictBool = Field(description="Flag that if true allows direct Analytics DB access from the plugin.", alias="analyticsDbAccess")
    cors: Optional[PluginCorsConfig] = Field(default=None, description="CORS configuration for the plugin. When not provided, default values will be used. These settings affect which browser origins are allowed to call the plugin and what request/response properties are permitted. They impact browser-enforced access (CORS), not server-to-server calls.")
    ephemeral_storage: Optional[Annotated[int, Field(le=10240, strict=True, ge=512)]] = Field(default=512, description="The ephemeral storage size in MB that the plugin will have", alias="ephemeralStorage")
    endpoint_url: StrictStr = Field(description="The URL from which the plugin is accessible", alias="endpointUrl")
    link_to_console: StrictStr = Field(description="A link to the cloud providers console", alias="linkToConsole")
    last_modified_time: datetime = Field(description="A UTC date-time in the ISO-8601 format", alias="lastModifiedTime")
    creation_time: datetime = Field(description="A UTC date-time in the ISO-8601 format", alias="creationTime")
    # BioT Code Start: Store custom attributes in a dedicated dict (safe default)
    additional_properties: Dict[str, Any] = Field(default_factory=dict)
    # BioT Code End
    __properties: ClassVar[List[str]] = ["name", "displayName", "version", "runtime", "handler", "timeout", "memorySize", "environmentVariables", "subscriptions", "enabledState", "cronSchedule", "deploymentPackageType", "analyticsDbAccess", "cors", "ephemeralStorage", "endpointUrl", "linkToConsole", "lastModifiedTime", "creationTime"]

    @field_validator('name')
    def name_validate_regular_expression(cls, value):
        """Validates the regular expression"""
        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('enabled_state')
    def enabled_state_validate_enum(cls, value):
        """Validates the enum"""
        if value not in set(['DISABLED', 'ENABLED']):
            raise ValueError("must be one of enum values ('DISABLED', 'ENABLED')")
        return value

    @field_validator('cron_schedule')
    def cron_schedule_validate_regular_expression(cls, value):
        """Validates the regular expression"""
        if value is None:
            return value

        if not re.match(r"^(?:(?:(?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[\/][0-9]+))?(?:[,](?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[\/][0-9]+))?)*)[ ]+(?:(?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[\/][0-9]+))?(?:[,](?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[\/][0-9]+))?)*)[ ]+(?:(?:[?][ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[\/][0-9]+)?)|(?:[\/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[\/][0-9]+)?)|(?:[\/][0-9]+))?)*)[ ]+(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))[#][0-5])|(?:(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*]))|(?:[\/][0-9]+))?(?:[,](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*]))|(?:[\/][0-9]+))?)*)))|(?:(?:(?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*]))|(?:[\/][0-9]+))?(?:[,](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*]))|(?:[\/][0-9]+))?)*)[ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[\/][0-9]+)?)|(?:[\/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[\/][0-9]+)?)|(?:[\/][0-9]+))?)*)[ ]+[?]))[ ]+(?:(?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|(?:[\/][0-9]+))?(?:[,](?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|(?:[\/][0-9]+))?)*))$", value):
            raise ValueError(r"must validate the regular expression /^(?:(?:(?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[\/][0-9]+))?(?:[,](?:(?:[0-5]?[0-9])|[*])(?:(?:[-](?:(?:[0-5]?[0-9])|[*]))|(?:[\/][0-9]+))?)*)[ ]+(?:(?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[\/][0-9]+))?(?:[,](?:(?:[0-2]?[0-9])|[*])(?:(?:[-](?:(?:[0-2]?[0-9])|[*]))|(?:[\/][0-9]+))?)*)[ ]+(?:(?:[?][ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[\/][0-9]+)?)|(?:[\/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[\/][0-9]+)?)|(?:[\/][0-9]+))?)*)[ ]+(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))[#][0-5])|(?:(?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*]))|(?:[\/][0-9]+))?(?:[,](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*])(?:(?:[-](?:(?:(?:[1-7]|(?:SUN|MON|TUE|WED|THU|FRI|SAT))L?)|[L*]))|(?:[\/][0-9]+))?)*)))|(?:(?:(?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*]))|(?:[\/][0-9]+))?(?:[,](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*])(?:(?:[-](?:(?:(?:[1-3]?[0-9])W?)|LW|[L*]))|(?:[\/][0-9]+))?)*)[ ]+(?:(?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[\/][0-9]+)?)|(?:[\/][0-9]+))?(?:[,](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:(?:[-](?:(?:[1]?[0-9])|(?:JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)|[*])(?:[\/][0-9]+)?)|(?:[\/][0-9]+))?)*)[ ]+[?]))[ ]+(?:(?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|(?:[\/][0-9]+))?(?:[,](?:(?:[12][0-9]{3})|[*])(?:(?:[-](?:(?:[12][0-9]{3})|[*]))|(?:[\/][0-9]+))?)*))$/")
        return value

    @field_validator('deployment_package_type')
    def deployment_package_type_validate_enum(cls, value):
        """Validates the enum"""
        if value not in set(['ZIP', 'IMAGE']):
            raise ValueError("must be one of enum values ('ZIP', 'IMAGE')")
        return value

    model_config = ConfigDict(
        populate_by_name=True,
        validate_assignment=True,
        protected_namespaces=(),
        # BioT Code Start: Allow extra fields for custom attributes
        extra='allow',
        # BioT Code End
    )

    # BioT Code Start: Sync Pydantic v2 extras into additional_properties
    @model_validator(mode='after')
    def _sync_additional_properties(self):
        """
        Sync unknown fields from Pydantic v2 `model_extra` into additional_properties.

        - When `extra='allow'`, unknown fields are stored in `model_extra`.
        - We copy them into additional_properties for OpenAPI Generator compatibility.
        """
        if self.model_extra:
            self.additional_properties.update(self.model_extra)
        return self
    # BioT Code End

    # BioT Code Start: Make dot-access to custom attributes type-check as Any (pyright-friendly)
    def __getattr__(self, item: str) -> Any:
        """
        Fallback for dynamic/custom attributes.

        This makes `model.some_custom_attr` resolve to `Any` in static type checkers
        instead of raising `reportUnknownMemberType`.
        """
        if item in self.additional_properties:
            return self.additional_properties[item]
        # also check model_extra directly (in case sync didn't run yet)
        if self.model_extra and item in self.model_extra:
            return self.model_extra[item]
        raise AttributeError(f"'{self.__class__.__name__}' has no field or custom property '{item}'")
    # BioT Code End

    # BioT Code Start: Dict-style access for custom attributes (and fields)
    def __getitem__(self, key: str) -> Any:
        """
        Support dict-style access:
          - defined fields by python name (e.g. 'name')
          - defined fields by alias (e.g. '_name') via to_dict fallback
          - custom attributes stored in additional_properties
        """
        if key in self.additional_properties:
            return self.additional_properties[key]
        if self.model_extra and key in self.model_extra:
            return self.model_extra[key]

        # Try direct attribute (python field name)
        if hasattr(self, key):
            try:
                return getattr(self, key)
            except AttributeError:
                pass

        # Try alias / serialized keys through to_dict()
        d = self.to_dict()
        if key in d:
            return d[key]

        raise KeyError(f"'{self.__class__.__name__}' has no field or custom property '{key}'")

    def get(self, key: str, default: Any = None) -> Any:
        """dict.get() style safe access."""
        try:
            return self[key]
        except KeyError:
            return default
    # BioT Code End

    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())

    # BioT Code Start: Override model_dump_json to flatten custom attributes to top level
    def model_dump_json(self, *, indent: Optional[int] = None, by_alias: bool = True, **kwargs: Any) -> str:
        """Return JSON representation with custom attributes flattened to top level."""
        json_kwargs = {k: v for k, v in kwargs.items()
                      if k not in ['by_alias', 'exclude', 'exclude_none', 'exclude_unset',
                                   'exclude_defaults', 'include', 'mode', 'round_trip',
                                   'warnings', 'serialize_as_any']}
        return json.dumps(self.to_dict(), indent=indent, default=str, **json_kwargs)
    # BioT Code End

    @classmethod
    def from_json(cls, json_str: str) -> Optional[Self]:
        """Create an instance of PluginResponse 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.
        * Fields in `self.additional_properties` are added to the output dict.
        """
        excluded_fields: Set[str] = set([
            "additional_properties",
        ])

        _dict = self.model_dump(
            by_alias=True,
            exclude=excluded_fields,
            exclude_none=True,
        )
        # override the default output from pydantic by calling `to_dict()` of subscriptions
        if self.subscriptions:
            _dict['subscriptions'] = self.subscriptions.to_dict()
        # override the default output from pydantic by calling `to_dict()` of cors
        if self.cors:
            _dict['cors'] = self.cors.to_dict()
        # BioT Code Start: puts key-value pairs in additional_properties in the top level
        if self.additional_properties is not None:
            for _key, _value in self.additional_properties.items():
                _dict[_key] = _value
        # BioT Code End
        return _dict

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

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

        _obj = cls.model_validate({
            "name": obj.get("name"),
            "displayName": obj.get("displayName"),
            "version": obj.get("version"),
            "runtime": obj.get("runtime"),
            "handler": obj.get("handler"),
            "timeout": obj.get("timeout") if obj.get("timeout") is not None else 60,
            "memorySize": obj.get("memorySize") if obj.get("memorySize") is not None else 128,
            "environmentVariables": obj.get("environmentVariables"),
            "subscriptions": SubscriptionDto.from_dict(obj["subscriptions"]) if obj.get("subscriptions") is not None else None,
            "enabledState": obj.get("enabledState") if obj.get("enabledState") is not None else 'ENABLED',
            "cronSchedule": obj.get("cronSchedule"),
            "deploymentPackageType": obj.get("deploymentPackageType"),
            "analyticsDbAccess": obj.get("analyticsDbAccess") if obj.get("analyticsDbAccess") is not None else False,
            "cors": PluginCorsConfig.from_dict(obj["cors"]) if obj.get("cors") is not None else None,
            "ephemeralStorage": obj.get("ephemeralStorage") if obj.get("ephemeralStorage") is not None else 512,
            "endpointUrl": obj.get("endpointUrl"),
            "linkToConsole": obj.get("linkToConsole"),
            "lastModifiedTime": obj.get("lastModifiedTime"),
            "creationTime": obj.get("creationTime")
        })
        # store additional fields in additional_properties
        for _key in obj.keys():
            if _key not in cls.__properties:
                _obj.additional_properties[_key] = obj.get(_key)

        return _obj


