from __future__ import annotations

from collections.abc import Mapping
from typing import TYPE_CHECKING, Any, TypeVar, cast

from attrs import define as _attrs_define
from attrs import field as _attrs_field

from ..models.cluster_type import ClusterType
from ..types import UNSET, Unset

if TYPE_CHECKING:
    from ..models.attribute_based_config import AttributeBasedConfig
    from ..models.llm_labeling import LLMLabeling
    from ..models.logical_operator import LogicalOperator
    from ..models.source_enrichment_config import SourceEnrichmentConfig
    from ..models.vector_based_config import VectorBasedConfig


T = TypeVar("T", bound="CreateClusterRequest")


@_attrs_define
class CreateClusterRequest:
    """Create a clustering job for one or more collections.

    Attributes:
        collection_ids (list[str]): Collections to cluster together
        cluster_name (None | str | Unset): Optional human-friendly name for the clustering job
        cluster_type (ClusterType | Unset): Type of clustering to perform.

            Determines the clustering approach:
            - vector: Cluster documents by embedding similarity (semantic clustering)
            - attribute: Cluster documents by metadata attributes (business logic clustering)

            Use Cases:
                vector:
                    - Group semantically similar content
                    - Find content with similar meaning
                    - Organize by topic/theme
                    - Requires vector embeddings

                attribute:
                    - Group by business attributes (category, brand, status, etc.)
                    - Organize by explicit metadata
                    - Create hierarchical groupings
                    - No embeddings required
        vector_config (None | Unset | VectorBasedConfig): Required when cluster_type is 'vector'
        attribute_config (AttributeBasedConfig | None | Unset): Required when cluster_type is 'attribute'
        filters (LogicalOperator | None | Unset): Optional filters to pre-filter documents before clustering (same
            format as list documents). Applied during Qdrant scroll before parquet export. Useful for clustering subsets
            like: status='active', category='electronics', etc.
        llm_labeling (LLMLabeling | None | Unset): Optional configuration for LLM-based cluster labeling. When provided
            with enabled=True, clusters will have semantic labels generated by LLM instead of generic labels like 'Cluster
            0'. When not provided or enabled=False, uses fallback labels.
        enrich_source_collection (bool | Unset): If True, cluster results are written back to source collection(s) in-
            place instead of creating new output collections. Documents will be enriched with cluster_id, cluster_label,
            distance_to_centroid, and optionally other metadata. Similar to taxonomy enrichment pattern. Default: False.
        source_enrichment_config (None | SourceEnrichmentConfig | Unset): Configuration for source collection enrichment
            (only used if enrich_source_collection=True). Controls which fields are added to source documents and field
            naming conventions.
    """

    collection_ids: list[str]
    cluster_name: None | str | Unset = UNSET
    cluster_type: ClusterType | Unset = UNSET
    vector_config: None | Unset | VectorBasedConfig = UNSET
    attribute_config: AttributeBasedConfig | None | Unset = UNSET
    filters: LogicalOperator | None | Unset = UNSET
    llm_labeling: LLMLabeling | None | Unset = UNSET
    enrich_source_collection: bool | Unset = False
    source_enrichment_config: None | SourceEnrichmentConfig | Unset = UNSET
    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)

    def to_dict(self) -> dict[str, Any]:
        from ..models.attribute_based_config import AttributeBasedConfig
        from ..models.llm_labeling import LLMLabeling
        from ..models.logical_operator import LogicalOperator
        from ..models.source_enrichment_config import SourceEnrichmentConfig
        from ..models.vector_based_config import VectorBasedConfig

        collection_ids = self.collection_ids

        cluster_name: None | str | Unset
        if isinstance(self.cluster_name, Unset):
            cluster_name = UNSET
        else:
            cluster_name = self.cluster_name

        cluster_type: str | Unset = UNSET
        if not isinstance(self.cluster_type, Unset):
            cluster_type = self.cluster_type.value

        vector_config: dict[str, Any] | None | Unset
        if isinstance(self.vector_config, Unset):
            vector_config = UNSET
        elif isinstance(self.vector_config, VectorBasedConfig):
            vector_config = self.vector_config.to_dict()
        else:
            vector_config = self.vector_config

        attribute_config: dict[str, Any] | None | Unset
        if isinstance(self.attribute_config, Unset):
            attribute_config = UNSET
        elif isinstance(self.attribute_config, AttributeBasedConfig):
            attribute_config = self.attribute_config.to_dict()
        else:
            attribute_config = self.attribute_config

        filters: dict[str, Any] | None | Unset
        if isinstance(self.filters, Unset):
            filters = UNSET
        elif isinstance(self.filters, LogicalOperator):
            filters = self.filters.to_dict()
        else:
            filters = self.filters

        llm_labeling: dict[str, Any] | None | Unset
        if isinstance(self.llm_labeling, Unset):
            llm_labeling = UNSET
        elif isinstance(self.llm_labeling, LLMLabeling):
            llm_labeling = self.llm_labeling.to_dict()
        else:
            llm_labeling = self.llm_labeling

        enrich_source_collection = self.enrich_source_collection

        source_enrichment_config: dict[str, Any] | None | Unset
        if isinstance(self.source_enrichment_config, Unset):
            source_enrichment_config = UNSET
        elif isinstance(self.source_enrichment_config, SourceEnrichmentConfig):
            source_enrichment_config = self.source_enrichment_config.to_dict()
        else:
            source_enrichment_config = self.source_enrichment_config

        field_dict: dict[str, Any] = {}
        field_dict.update(self.additional_properties)
        field_dict.update(
            {
                "collection_ids": collection_ids,
            }
        )
        if cluster_name is not UNSET:
            field_dict["cluster_name"] = cluster_name
        if cluster_type is not UNSET:
            field_dict["cluster_type"] = cluster_type
        if vector_config is not UNSET:
            field_dict["vector_config"] = vector_config
        if attribute_config is not UNSET:
            field_dict["attribute_config"] = attribute_config
        if filters is not UNSET:
            field_dict["filters"] = filters
        if llm_labeling is not UNSET:
            field_dict["llm_labeling"] = llm_labeling
        if enrich_source_collection is not UNSET:
            field_dict["enrich_source_collection"] = enrich_source_collection
        if source_enrichment_config is not UNSET:
            field_dict["source_enrichment_config"] = source_enrichment_config

        return field_dict

    @classmethod
    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
        from ..models.attribute_based_config import AttributeBasedConfig
        from ..models.llm_labeling import LLMLabeling
        from ..models.logical_operator import LogicalOperator
        from ..models.source_enrichment_config import SourceEnrichmentConfig
        from ..models.vector_based_config import VectorBasedConfig

        d = dict(src_dict)
        collection_ids = cast(list[str], d.pop("collection_ids"))

        def _parse_cluster_name(data: object) -> None | str | Unset:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            return cast(None | str | Unset, data)

        cluster_name = _parse_cluster_name(d.pop("cluster_name", UNSET))

        _cluster_type = d.pop("cluster_type", UNSET)
        cluster_type: ClusterType | Unset
        if isinstance(_cluster_type, Unset):
            cluster_type = UNSET
        else:
            cluster_type = ClusterType(_cluster_type)

        def _parse_vector_config(data: object) -> None | Unset | VectorBasedConfig:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            try:
                if not isinstance(data, dict):
                    raise TypeError()
                vector_config_type_0 = VectorBasedConfig.from_dict(data)

                return vector_config_type_0
            except (TypeError, ValueError, AttributeError, KeyError):
                pass
            return cast(None | Unset | VectorBasedConfig, data)

        vector_config = _parse_vector_config(d.pop("vector_config", UNSET))

        def _parse_attribute_config(data: object) -> AttributeBasedConfig | None | Unset:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            try:
                if not isinstance(data, dict):
                    raise TypeError()
                attribute_config_type_0 = AttributeBasedConfig.from_dict(data)

                return attribute_config_type_0
            except (TypeError, ValueError, AttributeError, KeyError):
                pass
            return cast(AttributeBasedConfig | None | Unset, data)

        attribute_config = _parse_attribute_config(d.pop("attribute_config", UNSET))

        def _parse_filters(data: object) -> LogicalOperator | None | Unset:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            try:
                if not isinstance(data, dict):
                    raise TypeError()
                filters_type_0 = LogicalOperator.from_dict(data)

                return filters_type_0
            except (TypeError, ValueError, AttributeError, KeyError):
                pass
            return cast(LogicalOperator | None | Unset, data)

        filters = _parse_filters(d.pop("filters", UNSET))

        def _parse_llm_labeling(data: object) -> LLMLabeling | None | Unset:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            try:
                if not isinstance(data, dict):
                    raise TypeError()
                llm_labeling_type_0 = LLMLabeling.from_dict(data)

                return llm_labeling_type_0
            except (TypeError, ValueError, AttributeError, KeyError):
                pass
            return cast(LLMLabeling | None | Unset, data)

        llm_labeling = _parse_llm_labeling(d.pop("llm_labeling", UNSET))

        enrich_source_collection = d.pop("enrich_source_collection", UNSET)

        def _parse_source_enrichment_config(data: object) -> None | SourceEnrichmentConfig | Unset:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            try:
                if not isinstance(data, dict):
                    raise TypeError()
                source_enrichment_config_type_0 = SourceEnrichmentConfig.from_dict(data)

                return source_enrichment_config_type_0
            except (TypeError, ValueError, AttributeError, KeyError):
                pass
            return cast(None | SourceEnrichmentConfig | Unset, data)

        source_enrichment_config = _parse_source_enrichment_config(d.pop("source_enrichment_config", UNSET))

        create_cluster_request = cls(
            collection_ids=collection_ids,
            cluster_name=cluster_name,
            cluster_type=cluster_type,
            vector_config=vector_config,
            attribute_config=attribute_config,
            filters=filters,
            llm_labeling=llm_labeling,
            enrich_source_collection=enrich_source_collection,
            source_enrichment_config=source_enrichment_config,
        )

        create_cluster_request.additional_properties = d
        return create_cluster_request

    @property
    def additional_keys(self) -> list[str]:
        return list(self.additional_properties.keys())

    def __getitem__(self, key: str) -> Any:
        return self.additional_properties[key]

    def __setitem__(self, key: str, value: Any) -> None:
        self.additional_properties[key] = value

    def __delitem__(self, key: str) -> None:
        del self.additional_properties[key]

    def __contains__(self, key: str) -> bool:
        return key in self.additional_properties
