"""
Celery configuration schemas from celery.yaml.

These schemas define the structure for Celery worker settings,
schedule intervals, and beat schedules.

SST File: lightwave/schema/definitions/config/django/celery.yaml
Database Model: None (config-driven, not DB-backed)

Usage:
    from lightwave.schema.pydantic.models.celery import (
        CeleryConfigSchema,
        CeleryScheduleSchema,
    )

    # Load all celery config
    config = CeleryConfigSchema.get_from_sst()
    print(config.worker.soft_time_limit_seconds)  # 300

    # Get beat schedule as Django format
    beat_schedule = config.get_beat_schedule()
"""

from pathlib import Path
from typing import Any, ClassVar

import yaml
from pydantic import Field

from lightwave.schema.pydantic.core.base import SSTSchema


class CeleryWorkerSchema(SSTSchema):
    """Celery worker configuration."""

    soft_time_limit_seconds: int = Field(..., ge=0, description="Soft time limit (raises exception)")
    hard_time_limit_seconds: int = Field(..., ge=0, description="Hard time limit (worker killed)")
    result_expires_seconds: int = Field(..., ge=0, description="How long to keep results")
    max_tasks_per_child: int = Field(..., ge=1, description="Max tasks before worker restart")
    acks_late: bool = Field(True, description="Acknowledge after task completes")
    reject_on_worker_lost: bool = Field(True, description="Reject if worker dies")
    prefetch_multiplier: int = Field(4, ge=0, description="Task prefetch multiplier")


class CeleryIntervalSchema(SSTSchema):
    """Named interval preset."""

    seconds: int = Field(..., ge=1, description="Interval duration in seconds")
    description: str | None = Field(None, description="Human-readable description")


class CeleryIntervalsSchema(SSTSchema):
    """All interval presets."""

    every_minute: CeleryIntervalSchema | None = None
    every_5_minutes: CeleryIntervalSchema | None = None
    every_10_minutes: CeleryIntervalSchema | None = None
    every_15_minutes: CeleryIntervalSchema | None = None
    every_30_minutes: CeleryIntervalSchema | None = None
    hourly: CeleryIntervalSchema | None = None
    every_4_hours: CeleryIntervalSchema | None = None
    every_6_hours: CeleryIntervalSchema | None = None
    every_12_hours: CeleryIntervalSchema | None = None
    daily: CeleryIntervalSchema | None = None
    weekly: CeleryIntervalSchema | None = None


class CeleryScheduleSchema(SSTSchema):
    """Individual beat schedule entry."""

    task: str = Field(..., min_length=1, description="Task path (e.g., apps.module.tasks.task_name)")
    interval: str = Field(..., min_length=1, description="Interval preset name")
    enabled: bool = Field(True, description="Whether this schedule is enabled")
    kwargs: dict[str, Any] = Field(default_factory=dict, description="Task kwargs")
    description: str | None = Field(None, description="Human-readable description")


class CeleryCrontabSchema(SSTSchema):
    """Crontab specification."""

    minute: int | str = Field(0, description="Minute (0-59 or *)")
    hour: int | str = Field(0, description="Hour (0-23 or *)")
    day_of_week: str = Field("*", description="Day of week (0-6, mon-sun, or *)")
    day_of_month: str = Field("*", description="Day of month (1-31 or *)")
    month_of_year: str = Field("*", description="Month (1-12 or *)")


class CeleryCrontabScheduleSchema(SSTSchema):
    """Crontab-based schedule entry."""

    task: str = Field(..., min_length=1, description="Task path")
    crontab: CeleryCrontabSchema = Field(..., description="Crontab specification")
    enabled: bool = Field(True, description="Whether this schedule is enabled")
    kwargs: dict[str, Any] = Field(default_factory=dict, description="Task kwargs")
    description: str | None = Field(None, description="Human-readable description")


class CeleryFeaturesSchema(SSTSchema):
    """Celery feature flags."""

    enable_cdn_tasks: bool = Field(True, description="Enable CDN maintenance tasks")
    enable_gmail_tasks: bool = Field(True, description="Enable Gmail sync tasks")
    enable_google_workspace_tasks: bool = Field(True, description="Enable Google Workspace tasks")
    enable_createos_tasks: bool = Field(True, description="Enable createOS tasks")
    enable_calendar_tasks: bool = Field(True, description="Enable calendar sync tasks")
    enable_inbox_tasks: bool = Field(True, description="Enable inbox sync tasks")
    default_batch_limit: int = Field(50, ge=1, description="Default batch limit")
    max_batch_limit: int = Field(200, ge=1, description="Maximum batch limit")


class CeleryConfigMeta(SSTSchema):
    """Metadata for celery configuration."""

    version: str = Field(..., description="Schema version")
    description: str = Field(..., description="Configuration description")
    model: str | None = Field(None, description="Database model (null for config-only)")
    sst_key: str = Field("celery", description="SST registry key")


class CeleryConfigSchema(SSTSchema):
    """
    Complete Celery configuration from celery.yaml.

    This is the root schema that contains all Celery-related configuration.

    Example:
        config = CeleryConfigSchema.get_from_sst()
        print(config.worker.soft_time_limit_seconds)  # 300
        print(config.intervals.hourly.seconds)  # 3600
    """

    SST_KEY: ClassVar[str] = "celery"

    # Metadata
    meta: CeleryConfigMeta = Field(..., alias="_meta", description="Configuration metadata")

    # Worker settings
    worker: CeleryWorkerSchema = Field(..., description="Worker configuration")

    # Interval presets
    intervals: CeleryIntervalsSchema = Field(..., description="Named interval presets")

    # Beat schedules (interval-based)
    schedules: dict[str, CeleryScheduleSchema] = Field(default_factory=dict, description="Interval-based schedules")

    # Crontab schedules
    crontab_schedules: dict[str, CeleryCrontabScheduleSchema] = Field(
        default_factory=dict, description="Crontab-based schedules"
    )

    # Feature flags
    features: CeleryFeaturesSchema | None = Field(None, description="Feature flags")

    @classmethod
    def get_sst_path(cls) -> Path:
        """Get path to SST YAML file."""
        from lightwave.schema.core.paths import get_sst_path

        return get_sst_path("celery")

    @classmethod
    def load_sst_data(cls) -> dict[str, Any]:
        """Load SST YAML file."""
        sst_path = cls.get_sst_path()
        if not sst_path.exists():
            raise FileNotFoundError(f"SST file not found: {sst_path}")
        with open(sst_path) as f:
            return yaml.safe_load(f)

    @classmethod
    def get_from_sst(cls) -> "CeleryConfigSchema":
        """
        Load Celery configuration from SST YAML.

        Returns:
            CeleryConfigSchema instance with all Celery config
        """
        data = cls.load_sst_data()

        # Get only fields defined in this schema
        known_fields = set(cls.model_fields.keys())
        known_fields.add("_meta")  # Include aliased field

        # Filter to only known fields
        filtered_data = {k: v for k, v in data.items() if k in known_fields or k == "_meta"}

        return cls(**filtered_data)

    def get_interval_seconds(self, interval_name: str) -> int:
        """
        Get interval duration by name.

        Args:
            interval_name: Interval preset name (e.g., 'hourly', 'daily')

        Returns:
            Interval duration in seconds

        Raises:
            ValueError: If interval doesn't exist
        """
        interval = getattr(self.intervals, interval_name, None)
        if interval is None:
            raise ValueError(f"Unknown interval preset: {interval_name}")
        return interval.seconds

    def get_django_celery_settings(self, *, redis_url: str) -> dict[str, Any]:
        """
        Generate Django CELERY_* settings dict.

        Args:
            redis_url: Redis connection URL

        Returns:
            Dict suitable for Django settings
        """
        return {
            "CELERY_BROKER_URL": redis_url,
            "CELERY_RESULT_BACKEND": redis_url,
            "CELERY_BEAT_SCHEDULER": "django_celery_beat.schedulers:DatabaseScheduler",
            "CELERY_TASK_SOFT_TIME_LIMIT": self.worker.soft_time_limit_seconds,
            "CELERY_TASK_TIME_LIMIT": self.worker.hard_time_limit_seconds,
            "CELERY_RESULT_EXPIRES": self.worker.result_expires_seconds,
            "CELERY_TASK_ACKS_LATE": self.worker.acks_late,
            "CELERY_TASK_REJECT_ON_WORKER_LOST": self.worker.reject_on_worker_lost,
            "CELERY_WORKER_MAX_TASKS_PER_CHILD": self.worker.max_tasks_per_child,
        }

    def get_beat_schedule(self) -> dict[str, dict]:
        """
        Generate Django CELERY_BEAT_SCHEDULE dict.

        Returns:
            Dict suitable for CELERY_BEAT_SCHEDULE setting
        """
        from celery.schedules import crontab

        schedule = {}

        # Add interval-based schedules
        for name, sched in self.schedules.items():
            if not sched.enabled:
                continue

            try:
                interval_seconds = self.get_interval_seconds(sched.interval)
            except ValueError:
                # Skip schedules with unknown intervals
                continue

            entry = {
                "task": sched.task,
                "schedule": interval_seconds,
            }
            if sched.kwargs:
                entry["kwargs"] = sched.kwargs

            # Convert schedule name from snake_case to kebab-case for consistency
            schedule_key = name.replace("_", "-")
            schedule[schedule_key] = entry

        # Add crontab schedules
        for name, sched in self.crontab_schedules.items():
            if not sched.enabled:
                continue

            cron = sched.crontab
            entry = {
                "task": sched.task,
                "schedule": crontab(
                    minute=cron.minute,
                    hour=cron.hour,
                    day_of_week=cron.day_of_week,
                    day_of_month=cron.day_of_month,
                    month_of_year=cron.month_of_year,
                ),
            }
            if sched.kwargs:
                entry["kwargs"] = sched.kwargs

            schedule_key = name.replace("_", "-")
            schedule[schedule_key] = entry

        return schedule

    def get_enabled_schedule_count(self) -> int:
        """Get count of enabled schedules."""
        interval_count = sum(1 for s in self.schedules.values() if s.enabled)
        crontab_count = sum(1 for s in self.crontab_schedules.values() if s.enabled)
        return interval_count + crontab_count
