# Copyright (c) Microsoft. All rights reserved.
# WARNING: This is an auto-generated file. DO NOT EDIT THIS FILE DIRECTLY.


from dataclasses import dataclass, field
from typing import Any, ClassVar, Optional

from ._context import LoadContext, SaveContext


@dataclass
class Property:
    """Represents a single property.

    - This model defines the structure of properties that can be used in prompts,
    including their type, description, whether they are required, and other attributes.
    - It allows for the definition of dynamic inputs that can be filled with data
    and processed to generate prompts for AI models.

    Attributes
    ----------
    name : str
        Name of the property
    kind : str
        The data type of the input property
    description : Optional[str]
        A short description of the input property
    required : Optional[bool]
        Whether the property is required
    default : Optional[Any]
        The default value of the property - this represents the default value if none is provided
    example : Optional[Any]
        Example value used for either initialization or tooling
    enumValues : list[Any]
        Allowed enumeration values for the property
    """

    _shorthand_property: ClassVar[Optional[str]] = "example"

    name: str = field(default="")
    kind: str = field(default="")
    description: Optional[str] = None
    required: Optional[bool] = None
    default: Optional[Any] = None
    example: Optional[Any] = None
    enumValues: list[Any] = field(default_factory=list)

    @staticmethod
    def load(data: Any, context: Optional[LoadContext] = None) -> "Property":
        """Load a Property instance.
        Args:
            data (Any): The data to load the instance from.
            context (Optional[LoadContext]): Optional context with pre/post processing callbacks.
        Returns:
            Property: The loaded Property instance.

        """

        if context is not None:
            data = context.process_input(data)

        # handle alternate representations
        if isinstance(data, bool):
            data = {"kind": "boolean", "example": data}
        if isinstance(data, float):
            data = {"kind": "float", "example": data}
        if isinstance(data, int):
            data = {"kind": "integer", "example": data}
        if isinstance(data, str):
            data = {"kind": "string", "example": data}

        if not isinstance(data, dict):
            raise ValueError(f"Invalid data for Property: {data}")

        # load polymorphic Property instance
        instance = Property.load_kind(data, context)

        if data is not None and "name" in data:
            instance.name = data["name"]
        if data is not None and "kind" in data:
            instance.kind = data["kind"]
        if data is not None and "description" in data:
            instance.description = data["description"]
        if data is not None and "required" in data:
            instance.required = data["required"]
        if data is not None and "default" in data:
            instance.default = data["default"]
        if data is not None and "example" in data:
            instance.example = data["example"]
        if data is not None and "enumValues" in data:
            instance.enumValues = data["enumValues"]
        if context is not None:
            instance = context.process_output(instance)
        return instance

    @staticmethod
    def load_kind(data: dict, context: Optional[LoadContext]) -> "Property":
        # load polymorphic Property instance
        if data is not None and "kind" in data:
            discriminator_value = str(data["kind"]).lower()
            if discriminator_value == "array":
                return ArrayProperty.load(data, context)
            elif discriminator_value == "object":
                return ObjectProperty.load(data, context)

            else:

                # create new instance (stop recursion)
                return Property()

        else:

            # create new instance
            return Property()

    def save(self, context: Optional[SaveContext] = None) -> dict[str, Any]:
        """Save the Property instance to a dictionary.
        Args:
            context (Optional[SaveContext]): Optional context with pre/post processing callbacks.
        Returns:
            dict[str, Any]: The dictionary representation of this instance.

        """
        obj = self
        if context is not None:
            obj = context.process_object(obj)

        result: dict[str, Any] = {}

        if obj.name is not None:
            result["name"] = obj.name
        if obj.kind is not None:
            result["kind"] = obj.kind
        if obj.description is not None:
            result["description"] = obj.description
        if obj.required is not None:
            result["required"] = obj.required
        if obj.default is not None:
            result["default"] = obj.default
        if obj.example is not None:
            result["example"] = obj.example
        if obj.enumValues is not None:
            result["enumValues"] = obj.enumValues

        if context is not None:
            result = context.process_dict(result)
        return result

    def to_yaml(self, context: Optional[SaveContext] = None) -> str:
        """Convert the Property instance to a YAML string.
        Args:
            context (Optional[SaveContext]): Optional context with pre/post processing callbacks.
        Returns:
            str: The YAML string representation of this instance.

        """
        if context is None:
            context = SaveContext()
        return context.to_yaml(self.save(context))

    def to_json(self, context: Optional[SaveContext] = None, indent: int = 2) -> str:
        """Convert the Property instance to a JSON string.
        Args:
            context (Optional[SaveContext]): Optional context with pre/post processing callbacks.
            indent (int): Number of spaces for indentation. Defaults to 2.
        Returns:
            str: The JSON string representation of this instance.

        """
        if context is None:
            context = SaveContext()
        return context.to_json(self.save(context), indent)


@dataclass
class ArrayProperty(Property):
    """Represents an array property.
    This extends the base Property model to represent an array of items.

    Attributes
    ----------
    kind : str

    items : Property
        The type of items contained in the array
    """

    _shorthand_property: ClassVar[Optional[str]] = None

    kind: str = field(default="array")
    items: Property = field(default_factory=Property)

    @staticmethod
    def load(data: Any, context: Optional[LoadContext] = None) -> "ArrayProperty":
        """Load a ArrayProperty instance.
        Args:
            data (Any): The data to load the instance from.
            context (Optional[LoadContext]): Optional context with pre/post processing callbacks.
        Returns:
            ArrayProperty: The loaded ArrayProperty instance.

        """

        if context is not None:
            data = context.process_input(data)

        if not isinstance(data, dict):
            raise ValueError(f"Invalid data for ArrayProperty: {data}")

        # create new instance
        instance = ArrayProperty()

        if data is not None and "kind" in data:
            instance.kind = data["kind"]
        if data is not None and "items" in data:
            instance.items = Property.load(data["items"], context)
        if context is not None:
            instance = context.process_output(instance)
        return instance

    def save(self, context: Optional[SaveContext] = None) -> dict[str, Any]:
        """Save the ArrayProperty instance to a dictionary.
        Args:
            context (Optional[SaveContext]): Optional context with pre/post processing callbacks.
        Returns:
            dict[str, Any]: The dictionary representation of this instance.

        """
        obj = self
        if context is not None:
            obj = context.process_object(obj)

        # Start with parent class properties
        result = super().save(context)

        if obj.kind is not None:
            result["kind"] = obj.kind
        if obj.items is not None:
            result["items"] = obj.items.save(context)

        return result

    def to_yaml(self, context: Optional[SaveContext] = None) -> str:
        """Convert the ArrayProperty instance to a YAML string.
        Args:
            context (Optional[SaveContext]): Optional context with pre/post processing callbacks.
        Returns:
            str: The YAML string representation of this instance.

        """
        if context is None:
            context = SaveContext()
        return context.to_yaml(self.save(context))

    def to_json(self, context: Optional[SaveContext] = None, indent: int = 2) -> str:
        """Convert the ArrayProperty instance to a JSON string.
        Args:
            context (Optional[SaveContext]): Optional context with pre/post processing callbacks.
            indent (int): Number of spaces for indentation. Defaults to 2.
        Returns:
            str: The JSON string representation of this instance.

        """
        if context is None:
            context = SaveContext()
        return context.to_json(self.save(context), indent)


@dataclass
class ObjectProperty(Property):
    """Represents an object property.
    This extends the base Property model to represent a structured object.

    Attributes
    ----------
    kind : str

    properties : list[Property]
        The properties contained in the object
    """

    _shorthand_property: ClassVar[Optional[str]] = None

    kind: str = field(default="object")
    properties: list[Property] = field(default_factory=list)

    @staticmethod
    def load(data: Any, context: Optional[LoadContext] = None) -> "ObjectProperty":
        """Load a ObjectProperty instance.
        Args:
            data (Any): The data to load the instance from.
            context (Optional[LoadContext]): Optional context with pre/post processing callbacks.
        Returns:
            ObjectProperty: The loaded ObjectProperty instance.

        """

        if context is not None:
            data = context.process_input(data)

        if not isinstance(data, dict):
            raise ValueError(f"Invalid data for ObjectProperty: {data}")

        # create new instance
        instance = ObjectProperty()

        if data is not None and "kind" in data:
            instance.kind = data["kind"]
        if data is not None and "properties" in data:
            instance.properties = ObjectProperty.load_properties(
                data["properties"], context
            )
        if context is not None:
            instance = context.process_output(instance)
        return instance

    @staticmethod
    def load_properties(
        data: dict | list, context: Optional[LoadContext]
    ) -> list[Property]:
        if isinstance(data, dict):
            # convert simple named properties to list of Property
            result = []
            for k, v in data.items():
                if isinstance(v, dict):
                    # value is an object, spread its properties
                    result.append({"name": k, **v})
                else:
                    # value is a scalar, use it as the primary property
                    result.append({"name": k, "": v})
            data = result
        return [Property.load(item, context) for item in data]

    @staticmethod
    def save_properties(
        items: list[Property], context: Optional[SaveContext]
    ) -> dict[str, Any] | list[dict[str, Any]]:
        if context is None:
            context = SaveContext()

        if context.collection_format == "array":
            return [item.save(context) for item in items]

        # Object format: use name as key
        result: dict[str, Any] = {}
        for item in items:
            item_data = item.save(context)
            name = item_data.pop("name", None)
            if name:
                # Check if we can use shorthand (only primary property set)
                if context.use_shorthand and hasattr(item, "_shorthand_property"):
                    shorthand_prop = item._shorthand_property
                    if (
                        shorthand_prop
                        and len(item_data) == 1
                        and shorthand_prop in item_data
                    ):
                        result[name] = item_data[shorthand_prop]
                        continue
                result[name] = item_data
            else:
                # No name, fall back to array format for this item
                if "_unnamed" not in result:
                    result["_unnamed"] = []
                result["_unnamed"].append(item_data)
        return result

    def save(self, context: Optional[SaveContext] = None) -> dict[str, Any]:
        """Save the ObjectProperty instance to a dictionary.
        Args:
            context (Optional[SaveContext]): Optional context with pre/post processing callbacks.
        Returns:
            dict[str, Any]: The dictionary representation of this instance.

        """
        obj = self
        if context is not None:
            obj = context.process_object(obj)

        # Start with parent class properties
        result = super().save(context)

        if obj.kind is not None:
            result["kind"] = obj.kind
        if obj.properties is not None:
            result["properties"] = ObjectProperty.save_properties(
                obj.properties, context
            )

        return result

    def to_yaml(self, context: Optional[SaveContext] = None) -> str:
        """Convert the ObjectProperty instance to a YAML string.
        Args:
            context (Optional[SaveContext]): Optional context with pre/post processing callbacks.
        Returns:
            str: The YAML string representation of this instance.

        """
        if context is None:
            context = SaveContext()
        return context.to_yaml(self.save(context))

    def to_json(self, context: Optional[SaveContext] = None, indent: int = 2) -> str:
        """Convert the ObjectProperty instance to a JSON string.
        Args:
            context (Optional[SaveContext]): Optional context with pre/post processing callbacks.
            indent (int): Number of spaces for indentation. Defaults to 2.
        Returns:
            str: The JSON string representation of this instance.

        """
        if context is None:
            context = SaveContext()
        return context.to_json(self.save(context), indent)
