# 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, StrictBool, StrictStr, field_validator
from typing import Any, ClassVar, Dict, List, Optional
from typing_extensions import Annotated
from mixpeek.models.aggregation_operation import AggregationOperation
from mixpeek.models.group_by_field_config import GroupByFieldConfig
from typing import Optional, Set
from typing_extensions import Self

class StageParamsAggregate(BaseModel):
    """
    Configuration for the aggregate REDUCE stage.  ┌─────────────────────────────────────────────────────────────────────────────┐ │ Stage Category: REDUCE                                                      │ │                                                                             │ │ Transformation: N documents → M aggregation results                         │ │                                                                             │ │ This stage operates on IN-MEMORY pipeline results, NOT the database.        │ │ Use this for analytics on already-retrieved documents.                      │ └─────────────────────────────────────────────────────────────────────────────┘  Purpose:     Compute aggregations (counts, sums, averages, etc.) on pipeline results.     Useful for analytics, summaries, and metadata extraction from search results.  When to Use:     - Compute statistics on retrieved documents (avg score, total count)     - Group results by a field and count per group     - Extract summary metrics for display (e.g., \"45 results in 3 categories\")     - Post-search analytics that don't need database queries  When NOT to Use:     - For faceted search (use feature_search.facets instead - queries Qdrant)     - For full-collection analytics (use aggregation API directly)     - The aggregate stage only sees pipeline results, not full filtered dataset  Performance:     - Fast: In-memory Python operations on already-fetched documents     - No database queries     - Suitable for up to ~10K documents  Common Pipeline Position:     FILTER → SORT → REDUCE (this stage)  Example Use Cases:     - \"Show average relevance score of top 100 results\"     - \"Count results per category from search results\"     - \"Get min/max prices from product search\"     - \"List unique authors in search results\"
    """ # noqa: E501
    aggregations: Optional[Annotated[List[AggregationOperation], Field(min_length=1)]] = Field(default=None, description="List of aggregation operations to compute. At least one aggregation is required. Multiple aggregations can be computed in a single stage.")
    group_by: Optional[List[GroupByFieldConfig]] = Field(default=None, description="OPTIONAL. Fields to group by before aggregating. When specified, aggregations are computed per-group. When None, aggregations are computed across all documents (global). Multiple fields create composite groups (e.g., category + year).")
    sort_by: Optional[StrictStr] = Field(default='null', description="OPTIONAL. Metric alias to sort aggregation results by. Must match an alias from the aggregations list. Only applies when group_by is specified.")
    sort_order: Optional[StrictStr] = Field(default='desc', description="OPTIONAL. Sort order for aggregation results. 'desc': Highest values first (default). 'asc': Lowest values first.")
    limit: Optional[Annotated[int, Field(le=1000, strict=True, ge=1)]] = Field(default=null, description="OPTIONAL. Maximum number of aggregation results to return. Only applies when group_by is specified. Useful for 'top N' queries (e.g., top 10 categories by count).")
    include_documents: Optional[StrictBool] = Field(default=False, description="OPTIONAL. Whether to include the original documents in output. False (default): Only return aggregation results in metadata. True: Pass through documents and add aggregation results to metadata. Set to True when aggregations are supplementary to search results.")
    __properties: ClassVar[List[str]] = ["aggregations", "group_by", "sort_by", "sort_order", "limit", "include_documents"]

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

        if value not in set(['asc', 'desc']):
            raise ValueError("must be one of enum values ('asc', 'desc')")
        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 StageParamsAggregate 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 aggregations (list)
        _items = []
        if self.aggregations:
            for _item_aggregations in self.aggregations:
                if _item_aggregations:
                    _items.append(_item_aggregations.to_dict())
            _dict['aggregations'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in group_by (list)
        _items = []
        if self.group_by:
            for _item_group_by in self.group_by:
                if _item_group_by:
                    _items.append(_item_group_by.to_dict())
            _dict['group_by'] = _items
        return _dict

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

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

        _obj = cls.model_validate({
            "aggregations": [AggregationOperation.from_dict(_item) for _item in obj["aggregations"]] if obj.get("aggregations") is not None else None,
            "group_by": [GroupByFieldConfig.from_dict(_item) for _item in obj["group_by"]] if obj.get("group_by") is not None else None,
            "sort_by": obj.get("sort_by") if obj.get("sort_by") is not None else 'null',
            "sort_order": obj.get("sort_order") if obj.get("sort_order") is not None else 'desc',
            "limit": obj.get("limit") if obj.get("limit") is not None else null,
            "include_documents": obj.get("include_documents") if obj.get("include_documents") is not None else False
        })
        return _obj


