//! ModeSwitch component for automatic mode switching between mechanical cooling and free cooling
//!
//! This component manages transitions between operating modes with hysteresis,
//! minimum duration requirements, and safety interlocks.

use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};

/// System operating modes
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum SystemMode {
    /// Mechanical cooling (compressor active)
    MechanicalCooling,
    /// Free cooling (direct heat exchange)
    FreeCooling,
    /// Mixed mode (both)
    Mixed,
    /// Off
    Off,
    /// Emergency
    Emergency,
}

/// Transition conditions
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TransitionCondition {
    /// Outdoor temperature threshold (K)
    pub outdoor_temp_threshold: f64,
    /// Hysteresis (K)
    pub hysteresis: f64,
    /// Minimum time in mode before transition (seconds)
    pub min_mode_duration_secs: u64,
    /// Cold water temperature threshold (optional)
    pub cold_water_temp_threshold: Option<f64>,
}

/// Configuration for ModeSwitch
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ModeSwitchConfig {
    /// Condition for mechanical to free cooling
    pub mechanical_to_free: TransitionCondition,
    /// Condition for free cooling to mechanical
    pub free_to_mechanical: TransitionCondition,
    /// Safety interlocks
    pub safety_interlocks: Vec<SafetyInterlock>,
    /// Stabilization time between transitions (seconds)
    pub stabilization_time_secs: u64,
}

/// Safety interlock
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SafetyInterlock {
    /// Interlock name
    pub name: String,
    /// Minimum temperature (K) - freeze protection
    pub min_temperature: Option<f64>,
    /// Maximum temperature (K)
    pub max_temperature: Option<f64>,
    /// Minimum pressure (Pa)
    pub min_pressure: Option<f64>,
    /// Maximum pressure (Pa)
    pub max_pressure: Option<f64>,
    /// Action to take
    pub action: InterlockAction,
}

/// Interlock action
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum InterlockAction {
    /// Warning only
    Alarm,
    /// Forced mode change
    ForceMode(SystemMode),
    /// Emergency shutdown
    EmergencyShutdown,
}

/// Transition state
#[derive(Debug, Clone, PartialEq)]
pub enum TransitionState {
    /// Stable in a mode
    Stable,
    /// Currently transitioning
    Transitioning {
        from: SystemMode,
        to: SystemMode,
        start_time: Instant,
    },
    /// Waiting for stabilization
    Stabilizing {
        mode: SystemMode,
        start_time: Instant,
    },
}

/// Mode switch controller
#[derive(Debug)]
pub struct ModeSwitch {
    /// Configuration
    config: ModeSwitchConfig,
    /// Current mode
    current_mode: SystemMode,
    /// Transition state
    transition_state: TransitionState,
    /// Last transition
    last_transition: Option<(SystemMode, Instant)>,
    /// Active interlocks
    active_interlocks: Vec<String>,
    /// Current outdoor temperature
    current_outdoor_temp: Option<f64>,
    /// Current cold water temperature
    current_cold_water_temp: Option<f64>,
}

impl ModeSwitch {
    /// Creates a new ModeSwitch
    pub fn new(config: ModeSwitchConfig) -> Self {
        Self {
            config,
            current_mode: SystemMode::Off,
            transition_state: TransitionState::Stable,
            last_transition: None,
            active_interlocks: Vec::new(),
            current_outdoor_temp: None,
            current_cold_water_temp: None,
        }
    }

    /// Updates conditions and calculates new mode
    pub fn update(
        &mut self,
        outdoor_temp: Option<f64>,
        cold_water_temp: Option<f64>,
    ) -> Result<SystemMode, ModeSwitchError> {
        // Update conditions
        self.current_outdoor_temp = outdoor_temp;
        self.current_cold_water_temp = cold_water_temp;

        // Check safety interlocks first
        self.check_safety_interlocks()?;

        // If an interlock is active, apply its action
        if let Some(interlock_name) = self.active_interlocks.first() {
            let interlock = self
                .config
                .safety_interlocks
                .iter()
                .find(|i| &i.name == interlock_name)
                .ok_or(ModeSwitchError::InterlockNotFound)?;

            match interlock.action {
                InterlockAction::ForceMode(mode) => {
                    if self.current_mode != mode {
                        self.transition_to(mode)?;
                    }
                    return Ok(self.current_mode);
                }
                InterlockAction::EmergencyShutdown => {
                    if self.current_mode != SystemMode::Emergency {
                        self.transition_to(SystemMode::Emergency)?;
                    }
                    return Ok(self.current_mode);
                }
                InterlockAction::Alarm => {
                    // Continue with normal logic
                }
            }
        }

        // Normal transition logic
        match self.transition_state {
            TransitionState::Stable => {
                self.evaluate_transitions()?;
            }
            TransitionState::Transitioning {
                from,
                to,
                start_time,
            } => {
                let elapsed = start_time.elapsed();
                if elapsed >= Duration::from_secs(self.config.stabilization_time_secs) {
                    // Transition complete
                    self.current_mode = to;
                    self.transition_state = TransitionState::Stabilizing {
                        mode: to,
                        start_time: Instant::now(),
                    };
                    self.last_transition = Some((from, Instant::now()));
                }
            }
            TransitionState::Stabilizing {
                mode: _,
                start_time,
            } => {
                let elapsed = start_time.elapsed();
                if elapsed
                    >= Duration::from_secs(self.config.mechanical_to_free.min_mode_duration_secs)
                {
                    // Stabilization complete
                    self.transition_state = TransitionState::Stable;
                }
            }
        }

        Ok(self.current_mode)
    }

    /// Evaluates transition conditions
    fn evaluate_transitions(&mut self) -> Result<(), ModeSwitchError> {
        let outdoor_temp = self
            .current_outdoor_temp
            .ok_or(ModeSwitchError::MissingTemperature)?;

        match self.current_mode {
            SystemMode::MechanicalCooling => {
                // Check if we can switch to free cooling
                if outdoor_temp < self.config.mechanical_to_free.outdoor_temp_threshold {
                    // Check minimum duration
                    if self.check_min_duration()? {
                        self.transition_to(SystemMode::FreeCooling)?;
                    }
                }
            }
            SystemMode::FreeCooling => {
                // Check if we should go back to mechanical
                if outdoor_temp > self.config.free_to_mechanical.outdoor_temp_threshold {
                    if self.check_min_duration()? {
                        self.transition_to(SystemMode::MechanicalCooling)?;
                    }
                }
            }
            SystemMode::Mixed => {
                // TODO: Implement mixed mode logic
            }
            _ => {
                // Other modes (Off, Emergency) - no automatic transition
            }
        }

        Ok(())
    }

    /// Checks minimum duration in current mode
    fn check_min_duration(&self) -> Result<bool, ModeSwitchError> {
        if let Some((_, last_transition_time)) = self.last_transition {
            let elapsed = last_transition_time.elapsed();
            let min_duration = match self.current_mode {
                SystemMode::MechanicalCooling => {
                    self.config.mechanical_to_free.min_mode_duration_secs
                }
                SystemMode::FreeCooling => self.config.free_to_mechanical.min_mode_duration_secs,
                _ => return Ok(true),
            };

            Ok(elapsed >= Duration::from_secs(min_duration))
        } else {
            Ok(true) // First transition
        }
    }

    /// Performs a transition to a new mode
    fn transition_to(&mut self, new_mode: SystemMode) -> Result<(), ModeSwitchError> {
        // Check if transition is valid
        if !self.is_valid_transition(self.current_mode, new_mode) {
            return Err(ModeSwitchError::InvalidTransition {
                from: self.current_mode,
                to: new_mode,
            });
        }

        // Start transition
        self.transition_state = TransitionState::Transitioning {
            from: self.current_mode,
            to: new_mode,
            start_time: Instant::now(),
        };

        Ok(())
    }

    /// Checks if a transition is valid
    fn is_valid_transition(&self, from: SystemMode, to: SystemMode) -> bool {
        match (from, to) {
            // Valid transitions
            (SystemMode::MechanicalCooling, SystemMode::FreeCooling) => true,
            (SystemMode::FreeCooling, SystemMode::MechanicalCooling) => true,
            (SystemMode::MechanicalCooling, SystemMode::Mixed) => true,
            (SystemMode::FreeCooling, SystemMode::Mixed) => true,
            (SystemMode::Mixed, SystemMode::MechanicalCooling) => true,
            (SystemMode::Mixed, SystemMode::FreeCooling) => true,
            (SystemMode::Off, SystemMode::MechanicalCooling) => true,
            (SystemMode::Off, SystemMode::FreeCooling) => true,
            (_, SystemMode::Emergency) => true, // Always possible
            (SystemMode::Emergency, SystemMode::Off) => true, // Recovery
            _ => false,
        }
    }

    /// Checks safety interlocks
    fn check_safety_interlocks(&mut self) -> Result<(), ModeSwitchError> {
        self.active_interlocks.clear();

        for interlock in &self.config.safety_interlocks {
            let mut triggered = false;

            // Check temperature
            if let (Some(min_temp), Some(current)) =
                (interlock.min_temperature, self.current_outdoor_temp)
            {
                if current < min_temp {
                    triggered = true;
                }
            }

            if let (Some(max_temp), Some(current)) =
                (interlock.max_temperature, self.current_outdoor_temp)
            {
                if current > max_temp {
                    triggered = true;
                }
            }

            // TODO: Check pressure when sensors are available

            if triggered {
                self.active_interlocks.push(interlock.name.clone());
            }
        }

        Ok(())
    }

    /// Returns current mode
    pub fn current_mode(&self) -> SystemMode {
        self.current_mode
    }

    /// Returns transition state
    pub fn transition_state(&self) -> &TransitionState {
        &self.transition_state
    }

    /// Returns active interlocks
    pub fn active_interlocks(&self) -> &[String] {
        &self.active_interlocks
    }

    /// Forces a mode manually (for maintenance or testing)
    pub fn force_mode(&mut self, mode: SystemMode) -> Result<(), ModeSwitchError> {
        self.current_mode = mode;
        self.transition_state = TransitionState::Stable;
        Ok(())
    }

    /// Returns outdoor temperature
    pub fn outdoor_temperature(&self) -> Option<f64> {
        self.current_outdoor_temp
    }

    /// Returns cold water temperature
    pub fn cold_water_temperature(&self) -> Option<f64> {
        self.current_cold_water_temp
    }
}

/// ModeSwitch errors
#[derive(Debug, thiserror::Error)]
pub enum ModeSwitchError {
    #[error("Missing outdoor temperature")]
    MissingTemperature,

    #[error("Invalid transition from {from:?} to {to:?}")]
    InvalidTransition { from: SystemMode, to: SystemMode },

    #[error("Interlock not found")]
    InterlockNotFound,

    #[error("Invalid transition condition")]
    InvalidCondition,
}

impl Default for TransitionCondition {
    fn default() -> Self {
        Self {
            outdoor_temp_threshold: 285.15, // 12°C
            hysteresis: 2.0,
            min_mode_duration_secs: 1800, // 30 minutes
            cold_water_temp_threshold: None,
        }
    }
}

impl Default for ModeSwitchConfig {
    fn default() -> Self {
        Self {
            mechanical_to_free: TransitionCondition::default(),
            free_to_mechanical: TransitionCondition {
                outdoor_temp_threshold: 287.15, // 14°C (with hysteresis)
                ..Default::default()
            },
            safety_interlocks: vec![SafetyInterlock {
                name: "freeze_protection".to_string(),
                min_temperature: Some(277.15), // 4°C
                max_temperature: None,
                min_pressure: None,
                max_pressure: None,
                action: InterlockAction::ForceMode(SystemMode::MechanicalCooling),
            }],
            stabilization_time_secs: 300, // 5 minutes
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_mode_switch_creation() {
        let config = ModeSwitchConfig::default();
        let mode_switch = ModeSwitch::new(config);
        assert_eq!(mode_switch.current_mode(), SystemMode::Off);
    }

    #[test]
    fn test_mode_switching_auto_temperature() {
        let mut config = ModeSwitchConfig::default();
        config.mechanical_to_free.outdoor_temp_threshold = 285.15; // 12°C

        let mut mode_switch = ModeSwitch::new(config);

        // Start in mechanical cooling
        mode_switch
            .force_mode(SystemMode::MechanicalCooling)
            .unwrap();

        // High outdoor temperature -> stay mechanical
        let mode = mode_switch.update(Some(293.15), None).unwrap(); // 20°C
        assert_eq!(mode, SystemMode::MechanicalCooling);

        // Low outdoor temperature -> switch to free cooling
        let mode = mode_switch.update(Some(283.15), None).unwrap(); // 10°C
                                                                    // Should be transitioning or stabilized
        assert!(
            mode == SystemMode::FreeCooling
                || *mode_switch.transition_state() != TransitionState::Stable
        );
    }

    #[test]
    fn test_freeze_protection_interlock() {
        let mut config = ModeSwitchConfig::default();
        config.safety_interlocks = vec![SafetyInterlock {
            name: "freeze_protection".to_string(),
            min_temperature: Some(277.15), // 4°C
            max_temperature: None,
            min_pressure: None,
            max_pressure: None,
            action: InterlockAction::ForceMode(SystemMode::MechanicalCooling),
        }];

        let mut mode_switch = ModeSwitch::new(config);
        mode_switch.force_mode(SystemMode::FreeCooling).unwrap();

        // Temperature below freeze protection
        let mode = mode_switch.update(Some(273.15), None).unwrap(); // 0°C
        assert_eq!(mode, SystemMode::MechanicalCooling);
        assert!(!mode_switch.active_interlocks().is_empty());
    }

    #[test]
    fn test_valid_transitions() {
        let mut mode_switch = ModeSwitch::new(ModeSwitchConfig::default());

        // Valid transitions
        mode_switch.force_mode(SystemMode::Off).unwrap();
        assert!(mode_switch
            .transition_to(SystemMode::MechanicalCooling)
            .is_ok());

        mode_switch
            .force_mode(SystemMode::MechanicalCooling)
            .unwrap();
        assert!(mode_switch.transition_to(SystemMode::FreeCooling).is_ok());

        // Invalid transitions
        mode_switch.force_mode(SystemMode::Emergency).unwrap();
        assert!(mode_switch.transition_to(SystemMode::FreeCooling).is_err());
    }
}
