"""Configuration validation schemas using Pydantic."""

from __future__ import annotations

from enum import Enum
from pathlib import Path
from typing import Annotated, Any

from pydantic import (
    BaseModel,
    ConfigDict,
    Field,
    SecretStr,
    field_validator,
    model_validator,
)


class LogLevel(str, Enum):
    """Logging levels."""

    DEBUG = "DEBUG"
    INFO = "INFO"
    WARNING = "WARNING"
    ERROR = "ERROR"
    CRITICAL = "CRITICAL"


class ServerConfig(BaseModel):
    """Server connection configuration."""

    model_config = ConfigDict(frozen=True)

    url: str = Field(..., description="Curadise server URL")
    timeout: Annotated[float, Field(gt=0, le=300)] = 30.0
    verify_ssl: bool = True
    ca_cert: Path | None = None

    @field_validator("url")
    @classmethod
    def validate_url(cls, v: str) -> str:
        if not v.startswith(("http://", "https://")):
            raise ValueError("URL must start with http:// or https://")
        return v.rstrip("/")


class AuthConfig(BaseModel):
    """Authentication configuration."""

    model_config = ConfigDict(frozen=True)

    enrollment_token: SecretStr | None = None
    token_file: Path | None = Field(default=None, description="Path to stored JWT token")
    mtls_cert: Path | None = None
    mtls_key: Path | None = None
    mtls_ca: Path | None = None

    @model_validator(mode="after")
    def validate_mtls_pair(self) -> AuthConfig:
        if (self.mtls_cert is None) != (self.mtls_key is None):
            raise ValueError("mtls_cert and mtls_key must both be set or both be None")
        return self


class BufferConfig(BaseModel):
    """Ring buffer configuration."""

    model_config = ConfigDict(frozen=True)

    max_size: Annotated[int, Field(ge=100, le=1_000_000)] = 10_000
    persistence_path: Path | None = None
    flush_interval: Annotated[float, Field(gt=0, le=300)] = 5.0
    batch_size: Annotated[int, Field(ge=1, le=10_000)] = 100
    compression_enabled: bool = True
    compression_level: Annotated[int, Field(ge=1, le=22)] = 3


class RetryConfig(BaseModel):
    """Retry and circuit breaker configuration."""

    model_config = ConfigDict(frozen=True)

    max_attempts: Annotated[int, Field(ge=1, le=10)] = 3
    initial_delay: Annotated[float, Field(gt=0, le=60)] = 1.0
    max_delay: Annotated[float, Field(gt=0, le=600)] = 60.0
    exponential_base: Annotated[float, Field(ge=1.5, le=4.0)] = 2.0
    circuit_breaker_threshold: Annotated[int, Field(ge=1, le=100)] = 5
    circuit_breaker_timeout: Annotated[float, Field(gt=0, le=3600)] = 60.0


class HeartbeatConfig(BaseModel):
    """Heartbeat configuration."""

    model_config = ConfigDict(frozen=True)

    interval: Annotated[float, Field(gt=0, le=3600)] = 30.0
    timeout: Annotated[float, Field(gt=0, le=60)] = 10.0
    include_metrics: bool = True


class CollectorConfig(BaseModel):
    """Individual collector configuration."""

    model_config = ConfigDict(frozen=True)

    enabled: bool = True
    interval: Annotated[float, Field(gt=0, le=3600)] = 60.0
    timeout: Annotated[float, Field(gt=0, le=300)] = 30.0
    options: dict[str, Any] = Field(default_factory=dict)


class HTTPCheckConfig(BaseModel):
    """HTTP health check configuration."""

    model_config = ConfigDict(frozen=True)

    url: str
    method: str = "GET"
    expected_status: int = 200
    timeout: Annotated[float, Field(gt=0, le=60)] = 10.0
    headers: dict[str, str] = Field(default_factory=dict)
    body: str | None = None

    @field_validator("method")
    @classmethod
    def validate_method(cls, v: str) -> str:
        allowed = {"GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS"}
        if v.upper() not in allowed:
            raise ValueError(f"Method must be one of {allowed}")
        return v.upper()


class CollectorsConfig(BaseModel):
    """All collectors configuration."""

    model_config = ConfigDict(frozen=True)

    system: CollectorConfig = Field(default_factory=CollectorConfig)
    http: CollectorConfig = Field(default_factory=CollectorConfig)
    http_checks: list[HTTPCheckConfig] = Field(default_factory=list)


class ExecutorConfig(BaseModel):
    """Command executor configuration."""

    model_config = ConfigDict(frozen=True)

    enabled: bool = True
    allowlist: list[str] = Field(default_factory=list, description="Allowed command patterns")
    denylist: list[str] = Field(default_factory=list, description="Denied command patterns")
    max_execution_time: Annotated[float, Field(gt=0, le=3600)] = 300.0
    max_output_size: Annotated[int, Field(ge=1024, le=10_485_760)] = 1_048_576  # 1MB default
    require_signature: bool = True
    sandbox_enabled: bool = True


class LoggingConfig(BaseModel):
    """Logging configuration."""

    model_config = ConfigDict(frozen=True)

    level: LogLevel = LogLevel.INFO
    format: str = "json"  # "json" or "console"
    file: Path | None = None
    max_size_mb: Annotated[int, Field(ge=1, le=1000)] = 100
    backup_count: Annotated[int, Field(ge=0, le=100)] = 5


class StateConfig(BaseModel):
    """State persistence configuration."""

    model_config = ConfigDict(frozen=True)

    directory: Path = Field(default=Path("/var/lib/curadise-agent"))
    agent_id_file: str = "agent_id"
    token_file: str = "token"
    buffer_file: str = "buffer.db"


class AgentConfig(BaseModel):
    """Complete agent configuration schema."""

    model_config = ConfigDict(frozen=True)

    server: ServerConfig
    auth: AuthConfig = Field(default_factory=AuthConfig)
    buffer: BufferConfig = Field(default_factory=BufferConfig)
    retry: RetryConfig = Field(default_factory=RetryConfig)
    heartbeat: HeartbeatConfig = Field(default_factory=HeartbeatConfig)
    collectors: CollectorsConfig = Field(default_factory=CollectorsConfig)
    executor: ExecutorConfig = Field(default_factory=ExecutorConfig)
    logging: LoggingConfig = Field(default_factory=LoggingConfig)
    state: StateConfig = Field(default_factory=StateConfig)
    tags: dict[str, str] = Field(default_factory=dict, description="Agent metadata tags")
