# coding: utf-8

"""
    Mixpeek API

    This is the Mixpeek API, providing access to various endpoints for data processing and retrieval.

    The version of the OpenAPI document: 1.3.0
    Contact: info@mixpeek.com
    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 pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr, field_validator
from typing import Any, ClassVar, Dict, List, Optional, Union
from mixpeek.models.hierarchical_node_output import HierarchicalNodeOutput
from mixpeek.models.hierarchy_inference_strategy import HierarchyInferenceStrategy
from mixpeek.models.input_mapping import InputMapping
from mixpeek.models.step_analytics_config_output import StepAnalyticsConfigOutput
from typing import Optional, Set
from typing_extensions import Self

class HierarchicalTaxonomyConfigOutput(BaseModel):
    """
    Hybrid hierarchical taxonomy configuration supporting inference with manual additions.  All hierarchical taxonomies are hybrid: - Base hierarchy can be inferred via schema, clustering, or LLM - Additional collections can be explicitly added with specific retrievers - Supports mixing inference strategies with manual additions/overrides  Examples: 1. Pure inference: Set inference_strategy + inference_collections 2. Pure manual: Set hierarchical_nodes only 3. Hybrid: Set inference_strategy + inference_collections + hierarchical_nodes    (infers base from collections, adds/overrides with explicit nodes)
    """ # noqa: E501
    taxonomy_type: Optional[StrictStr] = Field(default='hierarchical', description="Discriminator identifying this as a hierarchical taxonomy.")
    retriever_id: Optional[StrictStr] = Field(default=None, description="Default retriever to use for all nodes unless overridden per-node.")
    input_mappings: Optional[List[InputMapping]] = Field(default=None, description="Default input mappings for all nodes unless overridden per-node.")
    inference_strategy: Optional[HierarchyInferenceStrategy] = Field(default=None, description="Strategy for inferring hierarchy structure from collections. Can be 'schema' (overlap-based), 'cluster' (clustering-based), or 'llm' (AI-based). When set, will infer relationships from inference_collections.")
    inference_collections: Optional[List[StrictStr]] = Field(default=None, description="Collection IDs to use for hierarchy inference. The inference_strategy will analyze these collections to discover relationships. Can be combined with hierarchical_nodes for hybrid configuration.")
    llm_provider: Optional[StrictStr] = Field(default=None, description="LLM provider to use for hierarchy inference (default openai_chat_v1)")
    llm_model: Optional[StrictStr] = Field(default=None, description="LLM model name (e.g., gpt-4o-mini)")
    llm_prompt_template: Optional[StrictStr] = Field(default=None, description="Optional prompt template. Variables available: {collection_id}, {collection_name}.")
    llm_sample_size: Optional[StrictInt] = Field(default=0, description="Optional number of sample docs to include in prompts (0 = disabled).")
    cluster_ids: Optional[List[StrictStr]] = Field(default=None, description="Cluster IDs to use for CLUSTER inference strategy")
    cluster_overlap_threshold: Optional[Union[StrictFloat, StrictInt]] = Field(default=0.7, description="Minimum overlap ratio to establish parent-child relationship between clusters")
    hierarchical_nodes: Optional[List[HierarchicalNodeOutput]] = Field(default=None, description="Explicit node definitions that either: 1) Define the entire hierarchy (when inference_strategy is None), 2) Add additional nodes to an inferred hierarchy, or 3) Override specific relationships in an inferred hierarchy. Supports true hybrid: infer from some collections, manually add others.")
    step_analytics: Optional[StepAnalyticsConfigOutput] = Field(default=None, description="Optional configuration for step transition analytics. Enables tracking how documents progress through hierarchical taxonomy nodes over time (e.g., content workflow tracking from 'draft' to 'published'). If not provided, only basic assignment events are logged.")
    __properties: ClassVar[List[str]] = ["taxonomy_type", "retriever_id", "input_mappings", "inference_strategy", "inference_collections", "llm_provider", "llm_model", "llm_prompt_template", "llm_sample_size", "cluster_ids", "cluster_overlap_threshold", "hierarchical_nodes", "step_analytics"]

    @field_validator('taxonomy_type')
    def taxonomy_type_validate_enum(cls, value):
        """Validates the enum"""
        if value is None:
            return value

        if value not in set(['hierarchical']):
            raise ValueError("must be one of enum values ('hierarchical')")
        return value

    @field_validator('llm_provider')
    def llm_provider_validate_enum(cls, value):
        """Validates the enum"""
        if value is None:
            return value

        if value not in set(['openai_chat_v1']):
            raise ValueError("must be one of enum values ('openai_chat_v1')")
        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 HierarchicalTaxonomyConfigOutput 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.
        """
        excluded_fields: Set[str] = set([
        ])

        _dict = self.model_dump(
            by_alias=True,
            exclude=excluded_fields,
            exclude_none=True,
        )
        # override the default output from pydantic by calling `to_dict()` of each item in input_mappings (list)
        _items = []
        if self.input_mappings:
            for _item_input_mappings in self.input_mappings:
                if _item_input_mappings:
                    _items.append(_item_input_mappings.to_dict())
            _dict['input_mappings'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in hierarchical_nodes (list)
        _items = []
        if self.hierarchical_nodes:
            for _item_hierarchical_nodes in self.hierarchical_nodes:
                if _item_hierarchical_nodes:
                    _items.append(_item_hierarchical_nodes.to_dict())
            _dict['hierarchical_nodes'] = _items
        # override the default output from pydantic by calling `to_dict()` of step_analytics
        if self.step_analytics:
            _dict['step_analytics'] = self.step_analytics.to_dict()
        return _dict

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

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

        _obj = cls.model_validate({
            "taxonomy_type": obj.get("taxonomy_type") if obj.get("taxonomy_type") is not None else 'hierarchical',
            "retriever_id": obj.get("retriever_id"),
            "input_mappings": [InputMapping.from_dict(_item) for _item in obj["input_mappings"]] if obj.get("input_mappings") is not None else None,
            "inference_strategy": obj.get("inference_strategy"),
            "inference_collections": obj.get("inference_collections"),
            "llm_provider": obj.get("llm_provider"),
            "llm_model": obj.get("llm_model"),
            "llm_prompt_template": obj.get("llm_prompt_template"),
            "llm_sample_size": obj.get("llm_sample_size") if obj.get("llm_sample_size") is not None else 0,
            "cluster_ids": obj.get("cluster_ids"),
            "cluster_overlap_threshold": obj.get("cluster_overlap_threshold") if obj.get("cluster_overlap_threshold") is not None else 0.7,
            "hierarchical_nodes": [HierarchicalNodeOutput.from_dict(_item) for _item in obj["hierarchical_nodes"]] if obj.get("hierarchical_nodes") is not None else None,
            "step_analytics": StepAnalyticsConfigOutput.from_dict(obj["step_analytics"]) if obj.get("step_analytics") is not None else None
        })
        return _obj


