from __future__ import annotations

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

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

from ..types import UNSET, Unset

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


@_attrs_define
class ClusterExecutionCentroid:
    """Centroid information from a clustering execution.

    Represents a single cluster center from a clustering run, including
    LLM-generated semantic labels for human understanding.

    Use Cases:
        - Display cluster information in dashboards
        - Show cluster labels and summaries in UI
        - Group documents by semantic meaning
        - Filter/search by cluster keywords

    Note:
        This model is for execution metadata only (no visualization coordinates).
        For scatter plot coordinates, use ArtifactCentroid from the artifacts endpoint.

        Attributes:
            cluster_id (str): REQUIRED. Unique identifier for this cluster within the execution. Format: 'cl_' prefix
                followed by numeric index (e.g., 'cl_0', 'cl_1'). Used to reference this specific cluster in queries and
                enrichments. Consistent across executions if algorithm deterministic.
            num_members (int): REQUIRED. Number of documents/points assigned to this cluster. Indicates cluster size for
                sizing bubbles in visualizations. Minimum: 1 (K-Means forces assignment). Can be 0 for noise clusters in HDBSCAN
                (cluster_id = -1).
            label (None | str | Unset): OPTIONAL. Human-readable label generated by LLM (e.g., GPT-4o-mini). Automatically
                generated when llm_labeling.enabled = true in cluster config. NOT REQUIRED when LLM labeling disabled. Describes
                the semantic meaning of documents in this cluster. Example: 'Product Reviews', 'Technical Documentation',
                'Customer Support'.
            summary (None | str | Unset): OPTIONAL. Detailed description generated by LLM. Automatically generated when
                llm_labeling.include_summary = true. NOT REQUIRED when LLM labeling disabled or summary not requested. Provides
                context about what types of documents are in this cluster. Useful for tooltips, expanded views, or detailed
                explanations.
            keywords (list[str] | None | Unset): OPTIONAL. List of semantic keywords generated by LLM. Automatically
                generated when llm_labeling.include_keywords = true. NOT REQUIRED when LLM labeling disabled or keywords not
                requested. Useful for search, filtering, and quick cluster understanding. Typically 3-5 keywords per cluster.
    """

    cluster_id: str
    num_members: int
    label: None | str | Unset = UNSET
    summary: None | str | Unset = UNSET
    keywords: list[str] | None | Unset = UNSET
    additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict)

    def to_dict(self) -> dict[str, Any]:
        cluster_id = self.cluster_id

        num_members = self.num_members

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

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

        keywords: list[str] | None | Unset
        if isinstance(self.keywords, Unset):
            keywords = UNSET
        elif isinstance(self.keywords, list):
            keywords = self.keywords

        else:
            keywords = self.keywords

        field_dict: dict[str, Any] = {}
        field_dict.update(self.additional_properties)
        field_dict.update(
            {
                "cluster_id": cluster_id,
                "num_members": num_members,
            }
        )
        if label is not UNSET:
            field_dict["label"] = label
        if summary is not UNSET:
            field_dict["summary"] = summary
        if keywords is not UNSET:
            field_dict["keywords"] = keywords

        return field_dict

    @classmethod
    def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T:
        d = dict(src_dict)
        cluster_id = d.pop("cluster_id")

        num_members = d.pop("num_members")

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

        label = _parse_label(d.pop("label", UNSET))

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

        summary = _parse_summary(d.pop("summary", UNSET))

        def _parse_keywords(data: object) -> list[str] | None | Unset:
            if data is None:
                return data
            if isinstance(data, Unset):
                return data
            try:
                if not isinstance(data, list):
                    raise TypeError()
                keywords_type_0 = cast(list[str], data)

                return keywords_type_0
            except (TypeError, ValueError, AttributeError, KeyError):
                pass
            return cast(list[str] | None | Unset, data)

        keywords = _parse_keywords(d.pop("keywords", UNSET))

        cluster_execution_centroid = cls(
            cluster_id=cluster_id,
            num_members=num_members,
            label=label,
            summary=summary,
            keywords=keywords,
        )

        cluster_execution_centroid.additional_properties = d
        return cluster_execution_centroid

    @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
