"""Pydantic models for ScheduledJob validation."""

from typing import Any, Dict, List, Optional

from pydantic import Field, field_validator, model_validator

from .base import ToothFairyModel
from .enums import CronJobFrequency, CronJobStatus, Priority


class CronScheduleModel(ToothFairyModel):
    frequency: CronJobFrequency
    interval: Optional[int] = None
    cron_expression: Optional[str] = None
    day_of_week: Optional[int] = Field(default=None, ge=0, le=6)
    day_of_month: Optional[int] = Field(default=None, ge=1, le=31)
    hour: Optional[int] = Field(default=None, ge=0, le=23)
    minute: Optional[int] = Field(default=None, ge=0, le=59)
    start_date: Optional[str] = None
    end_date: Optional[str] = None

    @model_validator(mode="after")
    def validate_frequency_params(self) -> "CronScheduleModel":
        freq = self.frequency
        if freq == "CUSTOM" and not self.cron_expression:
            raise ValueError("cron_expression is required when frequency is CUSTOM")
        if freq == "WEEKLY" and self.day_of_week is None:
            raise ValueError("day_of_week is required when frequency is WEEKLY")
        if freq == "MONTHLY" and self.day_of_month is None:
            raise ValueError("day_of_month is required when frequency is MONTHLY")
        return self

    def to_api_payload(self) -> Dict[str, Any]:
        payload = {"frequency": self.frequency}
        if self.interval is not None:
            payload["interval"] = self.interval
        if self.cron_expression is not None:
            payload["cronExpression"] = self.cron_expression
        if self.day_of_week is not None:
            payload["dayOfWeek"] = self.day_of_week
        if self.day_of_month is not None:
            payload["dayOfMonth"] = self.day_of_month
        if self.hour is not None:
            payload["hour"] = self.hour
        if self.minute is not None:
            payload["minute"] = self.minute
        if self.start_date is not None:
            payload["startDate"] = self.start_date
        if self.end_date is not None:
            payload["endDate"] = self.end_date
        return payload


class ExecutionConfigModel(ToothFairyModel):
    timeout: Optional[int] = Field(default=None, gt=0)
    max_duration: Optional[int] = Field(default=None, gt=0)
    priority: Optional[Priority] = None
    input_parameters: Optional[Dict[str, Any]] = None
    output_format: Optional[str] = None
    webhook_url: Optional[str] = None
    webhook_headers: Optional[Dict[str, str]] = None

    @field_validator("webhook_url")
    @classmethod
    def validate_webhook_url(cls, v: Optional[str]) -> Optional[str]:
        if v is not None and not v.startswith(("http://", "https://")):
            raise ValueError("webhook_url must start with http:// or https://")
        return v

    def to_api_payload(self) -> Dict[str, Any]:
        payload = {}
        if self.timeout is not None:
            payload["timeout"] = self.timeout
        if self.max_duration is not None:
            payload["maxDuration"] = self.max_duration
        if self.priority is not None:
            payload["priority"] = self.priority
        if self.input_parameters is not None:
            payload["inputParameters"] = self.input_parameters
        if self.output_format is not None:
            payload["outputFormat"] = self.output_format
        if self.webhook_url is not None:
            payload["webhookUrl"] = self.webhook_url
        if self.webhook_headers is not None:
            payload["webhookHeaders"] = self.webhook_headers
        return payload


class NotificationConfigModel(ToothFairyModel):
    on_success: bool = False
    on_failure: bool = True
    on_start: bool = False
    email_recipients: List[str] = Field(default_factory=list)
    communication_service_id: Optional[str] = None

    @field_validator("email_recipients")
    @classmethod
    def validate_emails(cls, v: List[str]) -> List[str]:
        import re
        email_pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
        for email in v:
            if not re.match(email_pattern, email):
                raise ValueError(f"Invalid email address: {email}")
        return v

    def to_api_payload(self) -> Dict[str, Any]:
        payload = {
            "onSuccess": self.on_success,
            "onFailure": self.on_failure,
            "onStart": self.on_start,
            "emailRecipients": self.email_recipients,
        }
        if self.communication_service_id is not None:
            payload["communicationServiceID"] = self.communication_service_id
        return payload


class RetryConfigModel(ToothFairyModel):
    max_retries: int = Field(default=3, ge=0, le=10)
    retry_interval: int = Field(default=60, gt=0)
    backoff_multiplier: float = Field(default=2.0, gt=0)
    max_retry_interval: int = Field(default=3600, gt=0)

    @model_validator(mode="after")
    def validate_retry_config(self) -> "RetryConfigModel":
        if self.max_retry_interval < self.retry_interval:
            raise ValueError("max_retry_interval must be >= retry_interval")
        return self

    def to_api_payload(self) -> Dict[str, Any]:
        return {
            "maxRetries": self.max_retries,
            "retryInterval": self.retry_interval,
            "backoffMultiplier": self.backoff_multiplier,
            "maxRetryInterval": self.max_retry_interval,
        }


class ScheduledJobCreateModel(ToothFairyModel):
    name: str = Field(..., min_length=1, max_length=200)
    agent_id: str = Field(..., min_length=1)
    custom_prompt_id: str = Field(..., min_length=1)
    timezone: str = Field(default="UTC")
    description: Optional[str] = Field(default=None, max_length=1000)
    forced_prompt: Optional[str] = None
    schedule: Optional[CronScheduleModel] = None
    is_active: bool = True
    start_date: Optional[str] = None
    end_date: Optional[str] = None
    execution_config: Optional[ExecutionConfigModel] = None
    notification_config: Optional[NotificationConfigModel] = None
    retry_config: Optional[RetryConfigModel] = None
    metadata: Optional[Dict[str, Any]] = None

    @model_validator(mode="after")
    def validate_dates(self) -> "ScheduledJobCreateModel":
        if self.start_date and self.end_date:
            if self.start_date >= self.end_date:
                raise ValueError("start_date must be before end_date")
        return self

    def to_api_payload(self) -> Dict[str, Any]:
        payload: Dict[str, Any] = {
            "name": self.name,
            "agentID": self.agent_id,
            "customPromptID": self.custom_prompt_id,
            "timezone": self.timezone,
            "isActive": self.is_active,
        }
        if self.description is not None:
            payload["description"] = self.description
        if self.forced_prompt is not None:
            payload["forcedPrompt"] = self.forced_prompt
        if self.schedule is not None:
            payload["schedule"] = self.schedule.to_api_payload()
        if self.start_date is not None:
            payload["startDate"] = self.start_date
        if self.end_date is not None:
            payload["endDate"] = self.end_date
        if self.execution_config is not None:
            payload["executionConfig"] = self.execution_config.to_api_payload()
        if self.notification_config is not None:
            payload["notificationConfig"] = self.notification_config.to_api_payload()
        if self.retry_config is not None:
            payload["retryConfig"] = self.retry_config.to_api_payload()
        if self.metadata is not None:
            payload["metadata"] = self.metadata
        return payload


class ScheduledJobUpdateModel(ToothFairyModel):
    name: Optional[str] = Field(default=None, min_length=1, max_length=200)
    description: Optional[str] = Field(default=None, max_length=1000)
    timezone: Optional[str] = None
    schedule: Optional[CronScheduleModel] = None
    is_active: Optional[bool] = None
    status: Optional[CronJobStatus] = None
    start_date: Optional[str] = None
    end_date: Optional[str] = None
    execution_config: Optional[ExecutionConfigModel] = None
    notification_config: Optional[NotificationConfigModel] = None
    retry_config: Optional[RetryConfigModel] = None
    metadata: Optional[Dict[str, Any]] = None

    def to_api_payload(self) -> Dict[str, Any]:
        payload: Dict[str, Any] = {}
        if self.name is not None:
            payload["name"] = self.name
        if self.description is not None:
            payload["description"] = self.description
        if self.timezone is not None:
            payload["timezone"] = self.timezone
        if self.schedule is not None:
            payload["schedule"] = self.schedule.to_api_payload()
        if self.is_active is not None:
            payload["isActive"] = self.is_active
        if self.status is not None:
            payload["status"] = self.status
        if self.start_date is not None:
            payload["startDate"] = self.start_date
        if self.end_date is not None:
            payload["endDate"] = self.end_date
        if self.execution_config is not None:
            payload["executionConfig"] = self.execution_config.to_api_payload()
        if self.notification_config is not None:
            payload["notificationConfig"] = self.notification_config.to_api_payload()
        if self.retry_config is not None:
            payload["retryConfig"] = self.retry_config.to_api_payload()
        if self.metadata is not None:
            payload["metadata"] = self.metadata
        return payload