##########################################
# WARNING: This is an auto-generated file.
# DO NOT EDIT THIS FILE DIRECTLY
# ANY EDITS WILL BE LOST
##########################################

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

from ._context import LoadContext, SaveContext
from ._Model import Model
from ._Property import Property
from ._Template import Template
from ._Tool import Tool


@dataclass
class Prompty:
    """A Prompty is a markdown file format for LLM prompts. The frontmatter defines
    structured metadata including model configuration, input/output schemas, tools,
    and template settings. The markdown body becomes the instructions.

    This is the single root type for the Prompty schema — there is no abstract base
    class or kind discriminator. A .prompty file always produces a Prompty instance.

    Attributes
    ----------
    name : str
        Human-readable name of the prompt
    displayName : Optional[str]
        Display name for UI purposes
    description : Optional[str]
        Description of the prompt's purpose
    metadata : Optional[dict[str, Any]]
        Additional metadata including authors, tags, and other arbitrary properties
    inputs : list[Property]
        Input parameters that participate in template rendering
    outputs : list[Property]
        Expected output format and structure
    model : Model
        AI model configuration
    tools : list[Tool]
        Tools available for extended functionality
    template : Optional[Template]
        Template configuration for prompt rendering
    instructions : Optional[str]
        Clear directions on what the prompt should do. In .prompty files, this comes from the markdown body.
    """

    _shorthand_property: ClassVar[str | None] = None

    name: str = field(default="")
    displayName: str | None = None
    description: str | None = None
    metadata: dict[str, Any] | None = None
    inputs: list[Property] = field(default_factory=list)
    outputs: list[Property] = field(default_factory=list)
    model: Model = field(default_factory=Model)
    tools: list[Tool] = field(default_factory=list)
    template: Template | None = None
    instructions: str | None = None

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

        """

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

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

        # create new instance
        instance = Prompty()

        if data is not None and "name" in data:
            instance.name = data["name"]
        if data is not None and "displayName" in data:
            instance.displayName = data["displayName"]
        if data is not None and "description" in data:
            instance.description = data["description"]
        if data is not None and "metadata" in data:
            instance.metadata = data["metadata"]
        if data is not None and "inputs" in data:
            instance.inputs = Prompty.load_inputs(data["inputs"], context)
        if data is not None and "outputs" in data:
            instance.outputs = Prompty.load_outputs(data["outputs"], context)
        if data is not None and "model" in data:
            instance.model = Model.load(data["model"], context)
        if data is not None and "tools" in data:
            instance.tools = Prompty.load_tools(data["tools"], context)
        if data is not None and "template" in data:
            instance.template = Template.load(data["template"], context)
        if data is not None and "instructions" in data:
            instance.instructions = data["instructions"]
        if context is not None:
            instance = context.process_output(instance)
        return instance

    @staticmethod
    def load_inputs(data: dict | list, context: LoadContext | None) -> list[Property]:
        if isinstance(data, dict):
            result: list[Property] = []
            for k, v in data.items():
                if isinstance(v, dict):
                    prop = Property.load({"name": k, **v}, context)
                else:
                    # Scalar — let Property.load() infer kind from value
                    prop = Property.load(v, context)
                    prop.name = k
                result.append(prop)
            return result
        return [Property.load(item, context) for item in data]

    @staticmethod
    def save_inputs(items: list[Property], context: SaveContext | None) -> 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

    @staticmethod
    def load_outputs(data: dict | list, context: LoadContext | None) -> list[Property]:
        if isinstance(data, dict):
            result: list[Property] = []
            for k, v in data.items():
                if isinstance(v, dict):
                    prop = Property.load({"name": k, **v}, context)
                else:
                    # Scalar — let Property.load() infer kind from value
                    prop = Property.load(v, context)
                    prop.name = k
                result.append(prop)
            return result
        return [Property.load(item, context) for item in data]

    @staticmethod
    def save_outputs(items: list[Property], context: SaveContext | None) -> 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

    @staticmethod
    def load_tools(data: dict | list, context: LoadContext | None) -> list[Tool]:
        if isinstance(data, dict):
            result: list[Tool] = []
            for k, v in data.items():
                if isinstance(v, dict):
                    prop = Tool.load({"name": k, **v}, context)
                else:
                    # Scalar — let Tool.load() infer kind from value
                    prop = Tool.load(v, context)
                    prop.name = k
                result.append(prop)
            return result
        return [Tool.load(item, context) for item in data]

    @staticmethod
    def save_tools(items: list[Tool], context: SaveContext | None) -> 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: SaveContext | None = None) -> dict[str, Any]:
        """Save the Prompty 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.displayName is not None:
            result["displayName"] = obj.displayName
        if obj.description is not None:
            result["description"] = obj.description
        if obj.metadata is not None:
            result["metadata"] = obj.metadata
        if obj.inputs is not None:
            result["inputs"] = Prompty.save_inputs(obj.inputs, context)
        if obj.outputs is not None:
            result["outputs"] = Prompty.save_outputs(obj.outputs, context)
        if obj.model is not None:
            result["model"] = obj.model.save(context)
        if obj.tools is not None:
            result["tools"] = Prompty.save_tools(obj.tools, context)
        if obj.template is not None:
            result["template"] = obj.template.save(context)
        if obj.instructions is not None:
            result["instructions"] = obj.instructions

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

    def to_yaml(self, context: SaveContext | None = None) -> str:
        """Convert the Prompty 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: SaveContext | None = None, indent: int = 2) -> str:
        """Convert the Prompty 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)
