# 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, StrictBool, StrictStr
from typing import Any, ClassVar, Dict, List, Optional
from typing_extensions import Annotated
from mixpeek.models.file_filters import FileFilters
from mixpeek.models.schema_mapping_output import SchemaMappingOutput
from mixpeek.models.sync_mode import SyncMode
from mixpeek.models.task_status_enum import TaskStatusEnum
from typing import Optional, Set
from typing_extensions import Self

class SyncConfigurationModel(BaseModel):
    """
    Bucket-scoped configuration for automated storage synchronization.  Defines how files are synced from external storage providers to a Mixpeek bucket. Includes configuration, status, metrics, and robustness control fields.  **Supported Providers:** google_drive, s3, snowflake, sharepoint, tigris  **Built-in Robustness:** - Distributed locking (locked_by_worker_id, lock_expires_at) - Pause/resume control (paused, pause_reason, paused_at) - Safety limits (max_objects_per_run, batch_chunk_size) - Resume checkpointing (resume_cursor, resume_objects_processed) - Batch tracking (batch_ids, task_ids, batches_created)  **Metrics Fields:** - total_files_discovered: Files found in source - total_files_synced: Successfully synced files - total_files_failed: Files that failed (check DLQ) - total_bytes_synced: Total data transferred - consecutive_failures: Failure count for auto-suspend
    """ # noqa: E501
    sync_config_id: Optional[StrictStr] = Field(default=None, description="Unique identifier for the sync configuration.")
    bucket_id: StrictStr = Field(description="Target bucket identifier (e.g. 'bkt_marketing_assets').")
    connection_id: StrictStr = Field(description="Storage connection identifier (e.g. 'conn_abc123').")
    internal_id: StrictStr = Field(description="Organization internal identifier (multi-tenancy scope).")
    namespace_id: StrictStr = Field(description="Namespace identifier owning the bucket.")
    source_path: StrictStr = Field(description="Source path in the external storage provider. Format varies by provider: s3/tigris='bucket/prefix', google_drive='folder_id', sharepoint='/sites/Name/Documents', snowflake='DB.SCHEMA.TABLE'.")
    file_filters: Optional[FileFilters] = Field(default=None, description="Optional filter rules limiting which files are synced.")
    schema_mapping: Optional[SchemaMappingOutput] = Field(default=None, description="Schema mapping defining how source data maps to bucket schema fields. Maps external storage attributes (tags, metadata, columns, filenames) to bucket schema fields and blob properties. When provided, enables structured extraction of metadata from the sync source. See SchemaMapping for detailed configuration options.")
    sync_mode: Optional[SyncMode] = Field(default=None, description="Sync mode controlling lifecycle (initial_only or continuous).")
    polling_interval_seconds: Optional[Annotated[int, Field(le=900, strict=True, ge=30)]] = Field(default=300, description="Polling interval in seconds (continuous mode).")
    batch_size: Optional[Annotated[int, Field(le=100, strict=True, ge=1)]] = Field(default=50, description="Number of files processed per sync batch.")
    create_object_on_confirm: Optional[StrictBool] = Field(default=True, description="Whether objects should be created immediately after confirmation.")
    skip_duplicates: Optional[StrictBool] = Field(default=True, description="Skip files whose hashes already exist in the bucket.")
    skip_batch_submission: Optional[StrictBool] = Field(default=False, description="If True, sync objects to the bucket without creating/submitting batches for processing.")
    status: Optional[TaskStatusEnum] = Field(default=None, description="Current lifecycle status for the sync configuration. PENDING: Not yet started. ACTIVE: Currently running/polling. SUSPENDED: Temporarily paused. COMPLETED: Initial sync completed (for initial_only mode). FAILED: Sync encountered errors.")
    is_active: Optional[StrictBool] = Field(default=True, description="Convenience flag used for filtering active syncs.")
    total_files_discovered: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, description="Cumulative count of files found in source across all runs.")
    total_files_synced: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, description="Cumulative count of successfully synced files.")
    total_files_failed: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, description="Cumulative count of failed files (sent to DLQ after 3 retries).")
    total_bytes_synced: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, description="Cumulative bytes transferred across all runs.")
    created_at: Optional[datetime] = Field(default=None, description="When sync configuration was created.")
    updated_at: Optional[datetime] = Field(default=None, description="Last modification timestamp.")
    last_sync_at: Optional[datetime] = Field(default=None, description="When last successful sync completed. Used for incremental syncs.")
    next_sync_at: Optional[datetime] = Field(default=None, description="Scheduled time for next sync (continuous/scheduled modes).")
    created_by_user_id: StrictStr = Field(description="User identifier that created the sync configuration.")
    last_error: Optional[Annotated[str, Field(strict=True, max_length=1000)]] = Field(default=None, description="Most recent error message if sync attempts failed.")
    consecutive_failures: Optional[Annotated[int, Field(strict=True, ge=0)]] = 0
    metadata: Optional[Dict[str, Any]] = Field(default=None, description="Arbitrary metadata supplied by the user.")
    locked_by_worker_id: Optional[StrictStr] = Field(default=None, description="Worker ID that currently holds the lock for this sync")
    locked_at: Optional[datetime] = Field(default=None, description="Timestamp when lock was acquired")
    lock_expires_at: Optional[datetime] = Field(default=None, description="Timestamp when lock expires (for stale lock recovery)")
    paused: Optional[StrictBool] = Field(default=False, description="Whether sync is currently paused (user-controlled)")
    pause_reason: Optional[StrictStr] = Field(default=None, description="Reason for pause")
    paused_at: Optional[datetime] = Field(default=None, description="Timestamp when paused")
    paused_by_user_id: Optional[StrictStr] = Field(default=None, description="User who paused the sync")
    max_objects_per_run: Optional[Annotated[int, Field(strict=True, ge=1)]] = Field(default=100000, description="Hard cap on objects per sync run (prevents runaway syncs)")
    max_batch_chunk_size: Optional[Annotated[int, Field(le=1000, strict=True, ge=1)]] = Field(default=1000, description="Maximum objects per batch chunk")
    batch_chunk_size: Optional[Annotated[int, Field(le=1000, strict=True, ge=1)]] = Field(default=100, description="Number of objects per batch chunk (for concurrent processing)")
    current_sync_run_id: Optional[StrictStr] = Field(default=None, description="UUID for current/last sync run")
    sync_run_counter: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, description="Increments on each sync execution")
    batch_ids: Optional[List[StrictStr]] = Field(default=None, description="List of batch IDs created by this sync")
    task_ids: Optional[List[StrictStr]] = Field(default=None, description="List of task IDs for batches")
    batches_created: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, description="Total number of batches created")
    resume_enabled: Optional[StrictBool] = Field(default=True, description="Whether resuming partial runs is enabled")
    resume_cursor: Optional[StrictStr] = Field(default=None, description="Last page/cursor processed (for paginated APIs like Google Drive)")
    resume_last_primary_key: Optional[StrictStr] = Field(default=None, description="Last primary key processed (for database syncs with stable ordering)")
    resume_objects_processed: Optional[Annotated[int, Field(strict=True, ge=0)]] = Field(default=0, description="Count of objects processed in current/last run")
    resume_checkpoint_frequency: Optional[Annotated[int, Field(le=10000, strict=True, ge=100)]] = Field(default=1000, description="How often to checkpoint (in objects). Default: every 1000 objects")
    __properties: ClassVar[List[str]] = ["sync_config_id", "bucket_id", "connection_id", "internal_id", "namespace_id", "source_path", "file_filters", "schema_mapping", "sync_mode", "polling_interval_seconds", "batch_size", "create_object_on_confirm", "skip_duplicates", "skip_batch_submission", "status", "is_active", "total_files_discovered", "total_files_synced", "total_files_failed", "total_bytes_synced", "created_at", "updated_at", "last_sync_at", "next_sync_at", "created_by_user_id", "last_error", "consecutive_failures", "metadata", "locked_by_worker_id", "locked_at", "lock_expires_at", "paused", "pause_reason", "paused_at", "paused_by_user_id", "max_objects_per_run", "max_batch_chunk_size", "batch_chunk_size", "current_sync_run_id", "sync_run_counter", "batch_ids", "task_ids", "batches_created", "resume_enabled", "resume_cursor", "resume_last_primary_key", "resume_objects_processed", "resume_checkpoint_frequency"]

    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 SyncConfigurationModel 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 file_filters
        if self.file_filters:
            _dict['file_filters'] = self.file_filters.to_dict()
        # override the default output from pydantic by calling `to_dict()` of schema_mapping
        if self.schema_mapping:
            _dict['schema_mapping'] = self.schema_mapping.to_dict()
        return _dict

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

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

        _obj = cls.model_validate({
            "sync_config_id": obj.get("sync_config_id"),
            "bucket_id": obj.get("bucket_id"),
            "connection_id": obj.get("connection_id"),
            "internal_id": obj.get("internal_id"),
            "namespace_id": obj.get("namespace_id"),
            "source_path": obj.get("source_path"),
            "file_filters": FileFilters.from_dict(obj["file_filters"]) if obj.get("file_filters") is not None else None,
            "schema_mapping": SchemaMappingOutput.from_dict(obj["schema_mapping"]) if obj.get("schema_mapping") is not None else None,
            "sync_mode": obj.get("sync_mode"),
            "polling_interval_seconds": obj.get("polling_interval_seconds") if obj.get("polling_interval_seconds") is not None else 300,
            "batch_size": obj.get("batch_size") if obj.get("batch_size") is not None else 50,
            "create_object_on_confirm": obj.get("create_object_on_confirm") if obj.get("create_object_on_confirm") is not None else True,
            "skip_duplicates": obj.get("skip_duplicates") if obj.get("skip_duplicates") is not None else True,
            "skip_batch_submission": obj.get("skip_batch_submission") if obj.get("skip_batch_submission") is not None else False,
            "status": obj.get("status"),
            "is_active": obj.get("is_active") if obj.get("is_active") is not None else True,
            "total_files_discovered": obj.get("total_files_discovered") if obj.get("total_files_discovered") is not None else 0,
            "total_files_synced": obj.get("total_files_synced") if obj.get("total_files_synced") is not None else 0,
            "total_files_failed": obj.get("total_files_failed") if obj.get("total_files_failed") is not None else 0,
            "total_bytes_synced": obj.get("total_bytes_synced") if obj.get("total_bytes_synced") is not None else 0,
            "created_at": obj.get("created_at"),
            "updated_at": obj.get("updated_at"),
            "last_sync_at": obj.get("last_sync_at"),
            "next_sync_at": obj.get("next_sync_at"),
            "created_by_user_id": obj.get("created_by_user_id"),
            "last_error": obj.get("last_error"),
            "consecutive_failures": obj.get("consecutive_failures") if obj.get("consecutive_failures") is not None else 0,
            "metadata": obj.get("metadata"),
            "locked_by_worker_id": obj.get("locked_by_worker_id"),
            "locked_at": obj.get("locked_at"),
            "lock_expires_at": obj.get("lock_expires_at"),
            "paused": obj.get("paused") if obj.get("paused") is not None else False,
            "pause_reason": obj.get("pause_reason"),
            "paused_at": obj.get("paused_at"),
            "paused_by_user_id": obj.get("paused_by_user_id"),
            "max_objects_per_run": obj.get("max_objects_per_run") if obj.get("max_objects_per_run") is not None else 100000,
            "max_batch_chunk_size": obj.get("max_batch_chunk_size") if obj.get("max_batch_chunk_size") is not None else 1000,
            "batch_chunk_size": obj.get("batch_chunk_size") if obj.get("batch_chunk_size") is not None else 100,
            "current_sync_run_id": obj.get("current_sync_run_id"),
            "sync_run_counter": obj.get("sync_run_counter") if obj.get("sync_run_counter") is not None else 0,
            "batch_ids": obj.get("batch_ids"),
            "task_ids": obj.get("task_ids"),
            "batches_created": obj.get("batches_created") if obj.get("batches_created") is not None else 0,
            "resume_enabled": obj.get("resume_enabled") if obj.get("resume_enabled") is not None else True,
            "resume_cursor": obj.get("resume_cursor"),
            "resume_last_primary_key": obj.get("resume_last_primary_key"),
            "resume_objects_processed": obj.get("resume_objects_processed") if obj.get("resume_objects_processed") is not None else 0,
            "resume_checkpoint_frequency": obj.get("resume_checkpoint_frequency") if obj.get("resume_checkpoint_frequency") is not None else 1000
        })
        return _obj


