# 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 datetime import datetime
from pydantic import BaseModel, ConfigDict, Field, StrictFloat, StrictInt, StrictStr
from typing import Any, ClassVar, Dict, List, Optional, Union
from typing_extensions import Annotated
from mixpeek.models.batch_error_detail import BatchErrorDetail
from mixpeek.models.extractor_job_info import ExtractorJobInfo
from mixpeek.models.task_status_enum import TaskStatusEnum
from typing import Optional, Set
from typing_extensions import Self

class TierTaskInfo(BaseModel):
    """
    Tracking information for a single collection processing tier's task.  In multi-tier collection processing, each tier represents a processing stage in the decomposition pipeline (e.g., bucket → chunks → frames → scenes). Each tier gets its own independent task for granular monitoring, status tracking, and retry capabilities.  Use Cases:     - Monitor progress of individual tiers in a multi-tier batch     - Retry failed tiers without reprocessing successful ones     - Track lineage of processing through parent_task_id linkage     - Query task status for specific processing stages  Tier Definitions:     - Tier 0: Bucket → Collection (bucket objects as source)     - Tier N (N > 0): Collection → Collection (upstream collection documents as source)  Lifecycle:     1. Created with status=PENDING when tier is scheduled     2. Updated to status=IN_PROGRESS when processing starts (task_id assigned)     3. Finalized to status=COMPLETED or status=FAILED when tier completes  Requirements:     - tier_num: REQUIRED. Zero-based tier number indicating processing stage     - task_id: OPTIONAL. None until tier processing starts     - status: REQUIRED. Defaults to PENDING     - collection_ids: REQUIRED. Collections being processed in this tier     - source_type: REQUIRED. \"bucket\" for tier 0, \"collection\" for tier N+     - source_collection_ids: OPTIONAL. Required for tier N+ (collection sources)     - parent_task_id: OPTIONAL. Links to previous tier for audit trail     - started_at: OPTIONAL. Set when tier processing begins     - completed_at: OPTIONAL. Set when tier processing finishes     - error: OPTIONAL. Set if tier fails with error details
    """ # noqa: E501
    tier_num: Annotated[int, Field(strict=True, ge=0)] = Field(description="REQUIRED. Zero-based tier number indicating the processing stage. Tier 0 = initial bucket-to-collection processing (bucket objects as source). Tier N (N > 0) = collection-to-collection processing (upstream documents as source). Used to determine processing order and identify which stage a task represents. Example: In a 5-tier pipeline (bucket → chunks → frames → scenes → summaries), chunks=tier 0, frames=tier 1, scenes=tier 2, summaries=tier 3.")
    task_id: Optional[StrictStr] = Field(default=None, description="OPTIONAL. Unique task identifier for this tier's processing task. None if tier has not yet started (status=PENDING). Assigned when tier processing begins (status=IN_PROGRESS). Used to query task status via GET /v1/tasks/{task_id}. Format: 'task_' prefix followed by secure token. Generated using generate_secure_token() from shared.utilities.helpers.")
    status: Optional[TaskStatusEnum] = Field(default=None, description="REQUIRED. Current processing status of this tier's task. Lifecycle: PENDING → IN_PROGRESS → COMPLETED/FAILED. PENDING: Tier scheduled but not yet started. IN_PROGRESS: Tier currently processing documents. COMPLETED: Tier successfully processed all documents. FAILED: Tier encountered an error and stopped processing. Used to determine overall batch status and whether to proceed to next tier.")
    collection_ids: Optional[Annotated[List[StrictStr], Field(min_length=1)]] = Field(default=None, description="REQUIRED. List of collection IDs being processed in this tier. Flattened from extractor_jobs for convenience. Each tier can process one or more collections in parallel. Collections in the same tier have no dependencies on each other. Format: Collection IDs as defined when collections were created. Minimum 1 collection per tier. Example: Tier 1 might process ['col_frames_30fps', 'col_frames_60fps'] in parallel.")
    extractor_jobs: Optional[List[ExtractorJobInfo]] = Field(default=None, description="List of extractor jobs for this tier (one per unique feature_extractor_type). NEW as of 2025-12-31: Tiers now support multiple Ray jobs. Empty list for backwards compatibility with old batches. Tier completes when ALL extractor_jobs reach COMPLETED status.")
    source_type: StrictStr = Field(description="REQUIRED. Type of data source for this tier's processing. 'bucket': Tier 0 processing where source is bucket objects from the objects table. 'collection': Tier N+ processing where source is documents from upstream collection(s). Determines how the API prepares the input dataset manifest for the Engine. Bucket sources query the objects table and include file blobs. Collection sources query the documents table and include processed features.")
    source_collection_ids: Optional[List[StrictStr]] = Field(default=None, description="OPTIONAL for tier 0 (must be None). REQUIRED for tier N+ (N > 0). List of upstream collection IDs that provide documents as input to this tier. Typically contains collection IDs from the previous tier (tier_num - 1). Used by the API to query documents from these collections for processing. These upstream documents are converted to a Parquet manifest for the Engine. Example: If tier 1 processes 'col_frames' and sources from tier 0's 'col_chunks', then source_collection_ids=['col_chunks'].")
    parent_task_id: Optional[StrictStr] = Field(default=None, description="OPTIONAL. Task ID of the previous tier (tier_num - 1) that processed before this tier. Used to link tiers together for audit trail and lineage tracking. None for tier 0 (no parent). Enables queries like 'show all tiers that processed after tier 0' or 'trace back through all parent tiers to find the original batch'. Format: Same as task_id (e.g., 'task_tier0_abc123').")
    started_at: Optional[datetime] = Field(default=None, description="OPTIONAL. ISO 8601 timestamp when this tier began processing. None if tier has not yet started (status=PENDING). Set using current_time() from shared.utilities.helpers when tier starts. Used to calculate tier processing duration and identify long-running tiers. Example: '2025-11-03T10:00:00Z'.")
    completed_at: Optional[datetime] = Field(default=None, description="OPTIONAL. ISO 8601 timestamp when this tier finished processing (success or failure). None if tier has not yet completed (status=PENDING or IN_PROGRESS). Set using current_time() from shared.utilities.helpers when tier completes. Used to calculate tier processing duration (completed_at - started_at). Set for both COMPLETED and FAILED statuses. Example: '2025-11-03T10:05:00Z'.")
    duration_ms: Optional[Union[StrictFloat, StrictInt]] = Field(default=None, description="OPTIONAL. Processing duration in milliseconds for this tier. Calculated as (completed_at - started_at) when tier completes. None if tier has not yet completed or if started_at was not set. Provides a pre-computed duration for easy querying without timestamp math. Set for both COMPLETED and FAILED statuses.")
    errors: Optional[List[BatchErrorDetail]] = Field(default=None, description="OPTIONAL. List of detailed errors that occurred during tier processing. Empty list if tier succeeded or has not yet completed. Each error includes: error_type, message, component, stage, traceback, timestamp. Multiple errors may occur if different documents fail with different issues. Used for detailed error analysis, debugging, and intelligent retry logic. Example: Multiple documents failing with different errors (dependency vs auth). For backward compatibility, check if list is empty for success/in-progress status.")
    error_summary: Optional[Dict[str, StrictInt]] = Field(default=None, description="OPTIONAL. Aggregated summary of errors by error type. None if tier succeeded or has not yet completed. Maps error_type (category) to count of affected documents. Provides quick overview of error distribution without parsing full error list. Example: {'dependency': 5, 'authentication': 10, 'validation': 3} means 5 documents failed with dependency errors, 10 with auth errors, 3 with validation. Automatically generated from errors list for convenience. Used for batch health monitoring and error trend analysis.")
    performance: Optional[Dict[str, Any]] = Field(default=None, description="OPTIONAL. Performance metrics summary for this tier's execution. Automatically populated after tier completion by collecting data from ClickHouse analytics. Contains: total_time_ms (total execution time), avg_latency_ms (average operation latency), bottlenecks (list of slowest operations), stage_count (number of profiled stages). Used for troubleshooting performance issues and identifying bottlenecks. None if tier has not completed or performance data collection failed. Populated asynchronously via Celery task (non-blocking, best-effort).")
    ray_job_id: Optional[StrictStr] = Field(default=None, description="OPTIONAL. Ray/Anyscale job ID for tracking the infrastructure-level processing job. None if tier has not yet started or if the job ID was not returned by the engine. Set when tier processing is submitted to Ray/Anyscale via the Engine. Used for cancelling running jobs and monitoring infrastructure-level status. Format: 'raysubmit_' prefix followed by job identifier (e.g., 'raysubmit_9pDAyZbd5MN281TB'). This is the job ID that appears in the Ray/Anyscale dashboard.")
    celery_task_id: Optional[StrictStr] = Field(default=None, description="OPTIONAL. Celery task ID for tracking the Celery worker processing this tier. None if tier has not yet started or is not processed via Celery. Set when process_tier Celery task is triggered. Used for revoking pending/running Celery tasks during batch cancellation or deletion. Format: UUID string (e.g., 'a1b2c3d4-e5f6-7890-abcd-ef1234567890').")
    source_documents_fetched: Optional[StrictInt] = Field(default=None, description="OPTIONAL. Number of documents fetched from source collection(s) for Tier N processing. For Tier 0 (bucket source), this is the number of objects from the bucket. For Tier N+ (collection source), this is the count of documents from upstream collection(s). Set at the start of tier artifact building in build_tier_n_artifacts(). If 0, the source collection is empty - check upstream tier completion.")
    documents_after_source_filter: Optional[StrictInt] = Field(default=None, description="OPTIONAL. Number of documents remaining after applying source_filters. source_filters are optional conditions that exclude documents from processing. If this is 0 but source_documents_fetched > 0, your source_filters are too restrictive. Check that filter fields exist in source documents and conditions match expected values.")
    documents_missing_input_fields: Optional[StrictInt] = Field(default=None, description="OPTIONAL. Number of documents missing required input_mapping fields. input_mappings define which fields from source documents map to extractor inputs. If this equals documents_after_source_filter, ALL documents are missing required fields. Common cause: upstream extractor didn't produce expected output (e.g., video_segment_url). Check upstream extractor configuration and verify output field names.")
    documents_submitted_to_engine: Optional[StrictInt] = Field(default=None, description="OPTIONAL. Number of documents actually submitted to the Ray/Engine for processing. This is documents_after_source_filter minus documents_missing_input_fields. If 0, no documents were sent to the engine - check source_filters and input_mappings. If > 0 but documents_written = 0, the engine failed to process documents.")
    documents_written: Optional[StrictInt] = Field(default=None, description="OPTIONAL. Final count of documents written to the target collection(s). Set after tier completion by querying the collection document count. If 0 but documents_submitted_to_engine > 0, check tier errors for processing failures. If less than documents_submitted_to_engine, some documents failed during processing.")
    __properties: ClassVar[List[str]] = ["tier_num", "task_id", "status", "collection_ids", "extractor_jobs", "source_type", "source_collection_ids", "parent_task_id", "started_at", "completed_at", "duration_ms", "errors", "error_summary", "performance", "ray_job_id", "celery_task_id", "source_documents_fetched", "documents_after_source_filter", "documents_missing_input_fields", "documents_submitted_to_engine", "documents_written"]

    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 TierTaskInfo 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 extractor_jobs (list)
        _items = []
        if self.extractor_jobs:
            for _item_extractor_jobs in self.extractor_jobs:
                if _item_extractor_jobs:
                    _items.append(_item_extractor_jobs.to_dict())
            _dict['extractor_jobs'] = _items
        # override the default output from pydantic by calling `to_dict()` of each item in errors (list)
        _items = []
        if self.errors:
            for _item_errors in self.errors:
                if _item_errors:
                    _items.append(_item_errors.to_dict())
            _dict['errors'] = _items
        return _dict

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

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

        _obj = cls.model_validate({
            "tier_num": obj.get("tier_num"),
            "task_id": obj.get("task_id"),
            "status": obj.get("status"),
            "collection_ids": obj.get("collection_ids"),
            "extractor_jobs": [ExtractorJobInfo.from_dict(_item) for _item in obj["extractor_jobs"]] if obj.get("extractor_jobs") is not None else None,
            "source_type": obj.get("source_type"),
            "source_collection_ids": obj.get("source_collection_ids"),
            "parent_task_id": obj.get("parent_task_id"),
            "started_at": obj.get("started_at"),
            "completed_at": obj.get("completed_at"),
            "duration_ms": obj.get("duration_ms"),
            "errors": [BatchErrorDetail.from_dict(_item) for _item in obj["errors"]] if obj.get("errors") is not None else None,
            "error_summary": obj.get("error_summary"),
            "performance": obj.get("performance"),
            "ray_job_id": obj.get("ray_job_id"),
            "celery_task_id": obj.get("celery_task_id"),
            "source_documents_fetched": obj.get("source_documents_fetched"),
            "documents_after_source_filter": obj.get("documents_after_source_filter"),
            "documents_missing_input_fields": obj.get("documents_missing_input_fields"),
            "documents_submitted_to_engine": obj.get("documents_submitted_to_engine"),
            "documents_written": obj.get("documents_written")
        })
        return _obj


