"""
Pydantic configuration settings for the chocolate tempering machine control system.
Provides type-safe configuration management with validation.
"""

import logging
from enum import Enum
from pathlib import Path
from typing import Optional, Dict, Any

from pydantic import BaseModel, Field, validator, root_validator
from pydantic_settings import BaseSettings


class LogLevel(str, Enum):
    """Logging levels."""
    CRITICAL = "CRITICAL"
    ERROR = "ERROR" 
    WARNING = "WARNING"
    INFO = "INFO"
    DEBUG = "DEBUG"


class SerialConfig(BaseModel):
    """Serial communication configuration."""
    port: str = Field(default="/dev/ttyUSB0", description="Serial port device path")
    baudrate: int = Field(default=9600, ge=1200, le=115200, description="Serial baudrate")
    timeout: float = Field(default=1.0, ge=0.1, le=10.0, description="Communication timeout in seconds")
    bytesize: int = Field(default=8, ge=5, le=8, description="Data bits")
    parity: str = Field(default="N", regex="^[NEO]$", description="Parity: N/E/O")
    stopbits: float = Field(default=1, description="Stop bits")
    max_retries: int = Field(default=3, ge=0, le=10, description="Maximum retry attempts")
    retry_delay: float = Field(default=0.1, ge=0.01, le=1.0, description="Delay between retries")


class ModbusConfig(BaseModel):
    """Modbus RTU configuration."""
    slave_address: int = Field(default=1, ge=1, le=247, description="Modbus slave address")
    read_timeout: float = Field(default=2.0, ge=0.5, le=10.0, description="Read operation timeout")
    write_timeout: float = Field(default=2.0, ge=0.5, le=10.0, description="Write operation timeout")
    max_read_registers: int = Field(default=125, ge=1, le=125, description="Maximum registers per read")
    max_write_registers: int = Field(default=100, ge=1, le=100, description="Maximum registers per write")
    connection_check_interval: float = Field(default=10.0, ge=1.0, le=300.0, description="Connection health check interval")


class TemperatureConfig(BaseModel):
    """Temperature control configuration."""
    # Safety limits
    absolute_max_temp: float = Field(default=80.0, ge=20.0, le=100.0, description="Absolute maximum temperature (°C)")
    absolute_min_temp: float = Field(default=10.0, ge=0.0, le=30.0, description="Absolute minimum temperature (°C)")
    
    # Process parameters
    tank_max_heat: float = Field(default=60.0, ge=20.0, le=80.0, description="Tank maximum heating temperature (°C)")
    pump_max_heat: float = Field(default=55.0, ge=20.0, le=80.0, description="Pump maximum heating temperature (°C)")
    pump_min_heat: float = Field(default=25.0, ge=10.0, le=40.0, description="Pump minimum heating temperature (°C)")
    
    # Temperature tolerances
    temperature_tolerance: float = Field(default=0.5, ge=0.1, le=2.0, description="Temperature control tolerance (°C)")
    sensor_accuracy: float = Field(default=0.1, ge=0.05, le=1.0, description="Temperature sensor accuracy (°C)")
    
    # PID control defaults
    default_kp: float = Field(default=1.0, ge=0.0, le=10.0, description="Default proportional gain")
    default_ki: float = Field(default=0.1, ge=0.0, le=1.0, description="Default integral gain")
    default_kd: float = Field(default=0.01, ge=0.0, le=1.0, description="Default derivative gain")
    default_kl: float = Field(default=100.0, ge=0.0, le=1000.0, description="Default output limit")

    @validator('tank_max_heat')
    def validate_tank_max_heat(cls, v, values):
        if 'absolute_max_temp' in values and v > values['absolute_max_temp']:
            raise ValueError('Tank max heat cannot exceed absolute max temperature')
        return v

    @validator('pump_max_heat')
    def validate_pump_max_heat(cls, v, values):
        if 'absolute_max_temp' in values and v > values['absolute_max_temp']:
            raise ValueError('Pump max heat cannot exceed absolute max temperature')
        return v


class SafetyConfig(BaseModel):
    """Safety monitoring configuration."""
    # Electrical safety limits
    grid_voltage_nominal: float = Field(default=230.0, ge=100.0, le=400.0, description="Nominal grid voltage (V)")
    grid_voltage_tolerance: float = Field(default=0.1, ge=0.05, le=0.2, description="Grid voltage tolerance (±%)")
    grid_frequency_nominal: float = Field(default=50.0, ge=45.0, le=65.0, description="Nominal grid frequency (Hz)")
    grid_frequency_tolerance: float = Field(default=0.02, ge=0.01, le=0.05, description="Grid frequency tolerance (±%)")
    
    # Current limits
    max_neutral_current: float = Field(default=16.0, ge=1.0, le=63.0, description="Maximum neutral current (A)")
    max_motor1_current: float = Field(default=10.0, ge=1.0, le=32.0, description="Maximum motor 1 current (A)")
    max_motor2_current: float = Field(default=10.0, ge=1.0, le=32.0, description="Maximum motor 2 current (A)")
    
    # Error handling
    error_check_interval: float = Field(default=1.0, ge=0.1, le=10.0, description="Error monitoring interval (s)")
    auto_recovery_attempts: int = Field(default=3, ge=0, le=10, description="Automatic recovery attempts")
    auto_recovery_delay: float = Field(default=5.0, ge=1.0, le=60.0, description="Auto recovery delay (s)")
    
    # Communication timeouts
    communication_timeout: float = Field(default=3.0, ge=1.0, le=30.0, description="Communication timeout (s)")
    heartbeat_interval: float = Field(default=1.0, ge=0.1, le=10.0, description="Heartbeat interval (s)")


class ProcessConfig(BaseModel):
    """Process timing and control configuration."""
    # Phase delays (seconds)
    heating_delay: float = Field(default=60.0, ge=0.0, le=300.0, description="Heating phase delay (s)")
    cooling_delay: float = Field(default=120.0, ge=0.0, le=600.0, description="Cooling phase delay (s)")
    pouring_delay: float = Field(default=30.0, ge=0.0, le=120.0, description="Pouring phase delay (s)")
    pump_delay: float = Field(default=10.0, ge=0.0, le=60.0, description="Pump startup delay (s)")
    mixer_delay: float = Field(default=5.0, ge=0.0, le=30.0, description="Mixer startup delay (s)")
    
    # Control loop intervals
    process_control_interval: float = Field(default=0.5, ge=0.1, le=2.0, description="Process control loop interval (s)")
    temperature_read_interval: float = Field(default=1.0, ge=0.1, le=5.0, description="Temperature reading interval (s)")
    status_update_interval: float = Field(default=2.0, ge=0.5, le=10.0, description="Status update interval (s)")
    
    # Recipe validation
    min_heating_goal: float = Field(default=40.0, ge=20.0, le=80.0, description="Minimum heating goal temperature (°C)")
    max_heating_goal: float = Field(default=60.0, ge=40.0, le=80.0, description="Maximum heating goal temperature (°C)")
    min_cooling_goal: float = Field(default=20.0, ge=10.0, le=40.0, description="Minimum cooling goal temperature (°C)")
    max_cooling_goal: float = Field(default=40.0, ge=20.0, le=60.0, description="Maximum cooling goal temperature (°C)")

    @root_validator
    def validate_temperature_goals(cls, values):
        min_heating = values.get('min_heating_goal', 40.0)
        max_cooling = values.get('max_cooling_goal', 40.0)
        if max_cooling >= min_heating:
            raise ValueError('Maximum cooling goal must be less than minimum heating goal')
        return values


class DatabaseConfig(BaseModel):
    """Database configuration."""
    url: str = Field(default="sqlite:///tempering_machine.db", description="Database connection URL")
    echo: bool = Field(default=False, description="Enable SQL query logging")
    pool_pre_ping: bool = Field(default=True, description="Validate connections before use")
    pool_recycle: int = Field(default=3600, ge=60, le=86400, description="Connection recycle time (s)")
    max_overflow: int = Field(default=10, ge=0, le=100, description="Maximum connection overflow")
    pool_size: int = Field(default=5, ge=1, le=50, description="Connection pool size")


class RedisConfig(BaseModel):
    """Redis configuration for message queue."""
    host: str = Field(default="localhost", description="Redis host")
    port: int = Field(default=6379, ge=1, le=65535, description="Redis port")
    db: int = Field(default=0, ge=0, le=15, description="Redis database number")
    password: Optional[str] = Field(default=None, description="Redis password")
    socket_timeout: float = Field(default=5.0, ge=1.0, le=30.0, description="Socket timeout (s)")
    connection_pool_max_connections: int = Field(default=10, ge=1, le=100, description="Max pool connections")


class WebConfig(BaseModel):
    """Web service configuration."""
    host: str = Field(default="0.0.0.0", description="Server bind address")
    port: int = Field(default=8000, ge=1024, le=65535, description="Server port")
    workers: int = Field(default=1, ge=1, le=10, description="Number of worker processes")
    reload: bool = Field(default=False, description="Enable auto-reload in development")
    access_log: bool = Field(default=True, description="Enable access logging")
    cors_origins: list[str] = Field(default=["http://localhost:3000"], description="CORS allowed origins")
    api_title: str = Field(default="Chocolate Tempering Machine API", description="API title")
    api_version: str = Field(default="1.0.0", description="API version")


class Settings(BaseSettings):
    """Main application settings."""
    
    # Environment
    environment: str = Field(default="development", regex="^(development|production|testing)$")
    debug: bool = Field(default=False, description="Enable debug mode")
    log_level: LogLevel = Field(default=LogLevel.INFO, description="Logging level")
    
    # Component configurations
    serial: SerialConfig = Field(default_factory=SerialConfig)
    modbus: ModbusConfig = Field(default_factory=ModbusConfig)
    temperature: TemperatureConfig = Field(default_factory=TemperatureConfig)
    safety: SafetyConfig = Field(default_factory=SafetyConfig)
    process: ProcessConfig = Field(default_factory=ProcessConfig)
    database: DatabaseConfig = Field(default_factory=DatabaseConfig)
    redis: RedisConfig = Field(default_factory=RedisConfig)
    web: WebConfig = Field(default_factory=WebConfig)
    
    # File paths
    data_directory: Path = Field(default=Path("data"), description="Data storage directory")
    log_directory: Path = Field(default=Path("logs"), description="Log file directory")
    config_directory: Path = Field(default=Path("config"), description="Configuration directory")
    backup_directory: Path = Field(default=Path("backups"), description="Backup directory")
    
    # Application metadata
    app_name: str = Field(default="Chocolate Tempering Machine", description="Application name")
    app_version: str = Field(default="1.0.0", description="Application version")
    
    class Config:
        env_file = ".env"
        env_prefix = "TEMPERING_"
        env_nested_delimiter = "__"
        case_sensitive = False

    @validator('data_directory', 'log_directory', 'config_directory', 'backup_directory')
    def ensure_directory_exists(cls, v):
        """Ensure directories exist."""
        if isinstance(v, str):
            v = Path(v)
        v.mkdir(parents=True, exist_ok=True)
        return v

    def get_logging_config(self) -> Dict[str, Any]:
        """Get logging configuration dictionary."""
        return {
            "version": 1,
            "disable_existing_loggers": False,
            "formatters": {
                "default": {
                    "format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
                },
                "detailed": {
                    "format": "%(asctime)s - %(name)s - %(levelname)s - %(module)s - %(funcName)s:%(lineno)d - %(message)s",
                },
            },
            "handlers": {
                "console": {
                    "class": "logging.StreamHandler",
                    "level": self.log_level.value,
                    "formatter": "default",
                },
                "file": {
                    "class": "logging.handlers.RotatingFileHandler",
                    "level": self.log_level.value,
                    "formatter": "detailed",
                    "filename": str(self.log_directory / "tempering_machine.log"),
                    "maxBytes": 10485760,  # 10MB
                    "backupCount": 10,
                },
            },
            "loggers": {
                "tempering_machine": {
                    "level": self.log_level.value,
                    "handlers": ["console", "file"],
                    "propagate": False,
                },
                "uvicorn": {
                    "level": "INFO",
                    "handlers": ["console"],
                    "propagate": False,
                },
            },
            "root": {
                "level": self.log_level.value,
                "handlers": ["console"],
            },
        }


# Global settings instance
settings = Settings()