"""Fair task queue configuration.

This module contains configuration models for the fair task queue.
Configuration is separate from runtime objects (storage, adapters, etc.)
which should be passed directly to constructors.
"""

from datetime import timedelta

from pydantic import Field, field_validator
from pydantic_settings import BaseSettings


class FairQueueConfig(BaseSettings):
    """Configuration for FairTaskQueue.

    This configuration model contains only settings and parameters that can be
    serialized and loaded from environment variables. Runtime objects (storage,
    task_adapter, group_key_extractor, etc.) should be passed directly to the
    constructor or factory methods, not included in the configuration.

    This config should be used as a nested config in your application's main config:

    Example:
        ```python
        from haolib.configs.base import BaseConfig
        from haolib.configs.fair import FairQueueConfig

        class AppConfig(BaseConfig):
            fair: FairQueueConfig

        # Initialize config (automatically loads from environment variables)
        config = AppConfig()

        # Access fair queue config
        fair_config = config.fair

        # Pass config + runtime objects separately
        queue = FairTaskQueue.from_config(
            config=fair_config,
            storage=redis_storage,
            task_adapter=taskiq_adapter,
            group_key_extractor=user_id_extractor,
        )
        ```

    Environment variables are automatically loaded using the nested delimiter `__`.
    For example, if your AppConfig has `fair: FairQueueConfig`, then:
    - `FAIR__MAX_QUEUE_SIZE_PER_GROUP` sets `max_queue_size_per_group`
    - `FAIR__DEFAULT_MAX_RETRIES` sets `default_max_retries`
    - `FAIR__ENABLE_AUTO_RECOVERY` sets `enable_auto_recovery`
    - etc.

    Timedelta fields can be set as seconds (float or int) in environment variables.

    """

    # Queue limits
    max_queue_size_per_group: int | None = Field(
        default=None, ge=1, description="Maximum queue size per group (None = unlimited)"
    )
    max_global_queue_size: int | None = Field(
        default=None, ge=1, description="Maximum global queue size (None = unlimited)"
    )

    # Retry configuration
    default_max_retries: int = Field(default=3, ge=0, description="Default maximum retry attempts")

    # Scheduler configuration
    scheduler_poll_interval: timedelta = Field(default=timedelta(seconds=0.1), description="Scheduler poll interval")
    scheduler_error_backoff_interval: timedelta = Field(
        default=timedelta(seconds=1.0), description="Scheduler error backoff interval"
    )
    scheduler_max_concurrent_tasks: int = Field(default=10, ge=1, description="Maximum concurrent tasks in scheduler")

    # Result storage configuration
    enable_auto_result_storage: bool = Field(default=False, description="Enable automatic result storage")
    result_ttl_seconds: int | None = Field(default=None, ge=1, description="TTL for stored results in seconds")

    # Auto-recovery configuration
    enable_auto_recovery: bool = Field(default=False, description="Enable automatic periodic recovery of stuck tasks")
    auto_recovery_interval: timedelta = Field(
        default=timedelta(minutes=5), description="Interval between automatic recovery runs"
    )
    auto_recovery_max_processing_time: timedelta = Field(
        default=timedelta(minutes=5),
        description="Maximum processing time before task is considered stuck",
    )

    # Distributed lock configuration
    recovery_lock_timeout: timedelta = Field(
        default=timedelta(seconds=30), description="Timeout for acquiring recovery lock"
    )

    # Validation constants (configurable)
    max_group_id_length: int = Field(default=255, ge=1, le=1000, description="Maximum group ID length")
    min_priority: int = Field(default=-1000, description="Minimum task priority")
    max_priority: int = Field(default=1000, description="Maximum task priority")
    max_dependency_depth: int = Field(default=100, ge=1, description="Maximum dependency chain depth")
    max_dependency_task_id_length: int = Field(default=255, ge=1, description="Maximum length for dependency task IDs")

    # Hooks and middleware
    enable_default_hooks: bool = Field(default=True, description="Enable default hooks (LoggingHook and MetricsHook)")

    @field_validator("max_queue_size_per_group", "max_global_queue_size")
    @classmethod
    def validate_queue_sizes(cls, v: int | None) -> int | None:
        """Validate queue size limits."""
        if v is not None and v < 1:
            raise ValueError("Queue size limits must be at least 1")
        return v

    @field_validator("default_max_retries")
    @classmethod
    def validate_max_retries(cls, v: int) -> int:
        """Validate max retries."""
        if v < 0:
            raise ValueError("max_retries must be non-negative")
        return v

    @field_validator("scheduler_max_concurrent_tasks")
    @classmethod
    def validate_max_concurrent_tasks(cls, v: int) -> int:
        """Validate max concurrent tasks."""
        if v < 1:
            raise ValueError("scheduler_max_concurrent_tasks must be at least 1")
        return v

    @field_validator(
        "scheduler_poll_interval",
        "scheduler_error_backoff_interval",
        "auto_recovery_interval",
        "auto_recovery_max_processing_time",
        "recovery_lock_timeout",
    )
    @classmethod
    def validate_timedeltas(cls, v: timedelta) -> timedelta:
        """Validate timedelta values."""
        if v.total_seconds() < 0:
            raise ValueError("Timedelta values must be non-negative")
        return v

    @field_validator("result_ttl_seconds")
    @classmethod
    def validate_result_ttl(cls, v: int | None) -> int | None:
        """Validate result TTL."""
        if v is not None and v < 1:
            raise ValueError("result_ttl_seconds must be at least 1")
        return v
