//! PumpController component for intelligent pump sequencing and VFD optimization
//!
//! This component manages multiple pumps with optimal sequencing, runtime-based rotation,
//! and energy-efficient VFD control.

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

use crate::OperationalState;

/// Sequencing strategy for pump selection
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub enum SequencingStrategy {
    /// Fixed rotation (pump 1, 2, 3, 1, 2, 3...)
    FixedRotation,
    /// Based on operating hours
    RuntimeBased,
    /// Based on efficiency (energy optimization)
    EfficiencyBased,
    /// Alternation based on start count
    StartCountBased,
}

/// Configuration for an individual pump
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PumpConfig {
    /// Pump identifier
    pub id: String,
    /// Nominal power (W)
    pub nominal_power_w: f64,
    /// Nominal flow rate (m³/s)
    pub nominal_flow_m3s: f64,
    /// Nominal head (m)
    pub nominal_head_m: f64,
    /// Nominal speed (RPM)
    pub nominal_rpm: f64,
    /// Supports VFD
    pub supports_vfd: bool,
    /// VFD speed range (min, max) as fraction of nominal
    pub vfd_range: Option<(f64, f64)>,
}

/// State of an individual pump
#[derive(Debug, Clone)]
pub struct PumpState {
    /// Identifier
    pub id: String,
    /// Current operational state
    pub operational_state: OperationalState,
    /// Cumulative operating hours
    pub runtime_hours: f64,
    /// Cumulative start count
    pub start_count: u64,
    /// Current speed (fraction of nominal, 0.0-1.0)
    pub speed_fraction: f64,
    /// Current power consumption (W)
    pub current_power_w: f64,
    /// Current flow rate (m³/s)
    pub current_flow_m3s: f64,
    /// Last start time
    pub last_start: Option<Instant>,
    /// Last stop time
    pub last_stop: Option<Instant>,
    /// Is in fault state
    pub is_faulted: bool,
}

/// Configuration for the PumpController
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PumpControllerConfig {
    /// Configured pumps
    pub pumps: Vec<PumpConfig>,
    /// Minimum number of active pumps
    pub min_active_pumps: usize,
    /// Maximum number of active pumps
    pub max_active_pumps: usize,
    /// Sequencing strategy
    pub sequencing_strategy: SequencingStrategy,
    /// Rotation interval (hours)
    pub rotation_interval_hours: f64,
    /// Energy optimization enabled
    pub energy_optimization: bool,
    /// Minimum time between changes (seconds)
    pub min_switch_interval_secs: u64,
    /// Anti-short-cycle time (seconds)
    pub anti_short_cycle_time_secs: u64,
}

/// Pump controller for intelligent pump management
#[derive(Debug)]
pub struct PumpController {
    /// Configuration
    config: PumpControllerConfig,
    /// Pump states
    pump_states: Vec<PumpState>,
    /// Rotation queue
    rotation_queue: VecDeque<String>,
    /// Last rotation time
    last_rotation: Option<Instant>,
    /// Last pump count change
    last_pump_count_change: Option<Instant>,
    /// Flow setpoint (m³/s)
    flow_setpoint_m3s: f64,
    /// Current total flow
    current_total_flow_m3s: f64,
    /// Load demand (0.0-1.0)
    load_demand: f64,
}

impl PumpController {
    /// Creates a new pump controller
    pub fn new(config: PumpControllerConfig) -> Result<Self, PumpControllerError> {
        // Validation
        if config.min_active_pumps > config.max_active_pumps {
            return Err(PumpControllerError::InvalidConfiguration(
                "min_active_pumps cannot be greater than max_active_pumps".to_string(),
            ));
        }

        if config.pumps.len() < config.max_active_pumps {
            return Err(PumpControllerError::InvalidConfiguration(
                "Number of configured pumps must be >= max_active_pumps".to_string(),
            ));
        }

        // Initialize pump states
        let mut pump_states = Vec::new();
        let mut rotation_queue = VecDeque::new();

        for pump_config in &config.pumps {
            pump_states.push(PumpState {
                id: pump_config.id.clone(),
                operational_state: OperationalState::Off,
                runtime_hours: 0.0,
                start_count: 0,
                speed_fraction: 0.0,
                current_power_w: 0.0,
                current_flow_m3s: 0.0,
                last_start: None,
                last_stop: None,
                is_faulted: false,
            });
            rotation_queue.push_back(pump_config.id.clone());
        }

        Ok(Self {
            config,
            pump_states,
            rotation_queue,
            last_rotation: None,
            last_pump_count_change: None,
            flow_setpoint_m3s: 0.0,
            current_total_flow_m3s: 0.0,
            load_demand: 0.0,
        })
    }

    /// Updates load demand and calculates required pumps
    pub fn update_demand(
        &mut self,
        load_demand: f64,
        flow_setpoint_m3s: f64,
    ) -> Result<(), PumpControllerError> {
        self.load_demand = load_demand.clamp(0.0, 1.0);
        self.flow_setpoint_m3s = flow_setpoint_m3s.max(0.0);

        // Calculate required number of pumps
        let required_pumps = self.calculate_required_pumps()?;

        // Check if change is needed
        let current_active = self.count_active_pumps();

        if required_pumps != current_active {
            // Check minimum interval
            if let Some(last_change) = self.last_pump_count_change {
                let elapsed = last_change.elapsed().as_secs();
                if elapsed < self.config.min_switch_interval_secs {
                    return Ok(()); // Wait more
                }
            }

            // Apply change
            if required_pumps > current_active {
                self.start_pumps(required_pumps - current_active)?;
            } else {
                self.stop_pumps(current_active - required_pumps)?;
            }

            self.last_pump_count_change = Some(Instant::now());
        }

        // Optimize VFD speeds if enabled
        if self.config.energy_optimization {
            self.optimize_vfd_speeds()?;
        }

        // Update flows
        self.update_flows()?;

        Ok(())
    }

    /// Calculates required pumps based on demand
    fn calculate_required_pumps(&self) -> Result<usize, PumpControllerError> {
        // Simple calculation based on demand
        let flow_per_pump = self.calculate_nominal_flow_per_pump();
        let required = (self.flow_setpoint_m3s / flow_per_pump).ceil() as usize;

        // Apply limits
        Ok(required.clamp(self.config.min_active_pumps, self.config.max_active_pumps))
    }

    /// Starts N pumps
    fn start_pumps(&mut self, count: usize) -> Result<(), PumpControllerError> {
        let mut started = 0;

        for _ in 0..count {
            if let Some(pump_id) = self.get_next_pump_to_start()? {
                self.start_pump(&pump_id)?;
                started += 1;
            } else {
                break;
            }
        }

        if started < count {
            return Err(PumpControllerError::InsufficientPumps(format!(
                "Could only start {} of {} requested pumps",
                started, count
            )));
        }

        Ok(())
    }

    /// Stops N pumps
    fn stop_pumps(&mut self, count: usize) -> Result<(), PumpControllerError> {
        let mut _stopped = 0;

        for _ in 0..count {
            if let Some(pump_id) = self.get_next_pump_to_stop()? {
                self.stop_pump(&pump_id)?;
                _stopped += 1;
            } else {
                break;
            }
        }

        Ok(())
    }

    /// Finds the next pump to start (based on strategy)
    fn get_next_pump_to_start(&mut self) -> Result<Option<String>, PumpControllerError> {
        match self.config.sequencing_strategy {
            SequencingStrategy::FixedRotation => {
                // Take next in queue
                Ok(self.rotation_queue.pop_front())
            }
            SequencingStrategy::RuntimeBased => {
                // Find pump with least operating hours
                let mut candidates: Vec<_> = self
                    .pump_states
                    .iter()
                    .filter(|p| p.operational_state == OperationalState::Off && !p.is_faulted)
                    .collect();

                candidates.sort_by(|a, b| a.runtime_hours.partial_cmp(&b.runtime_hours).unwrap());

                Ok(candidates.first().map(|p| p.id.clone()))
            }
            SequencingStrategy::StartCountBased => {
                // Find pump with least starts
                let mut candidates: Vec<_> = self
                    .pump_states
                    .iter()
                    .filter(|p| p.operational_state == OperationalState::Off && !p.is_faulted)
                    .collect();

                candidates.sort_by(|a, b| a.start_count.cmp(&b.start_count));

                Ok(candidates.first().map(|p| p.id.clone()))
            }
            SequencingStrategy::EfficiencyBased => {
                // TODO: Implement based on performance curves
                Ok(self.rotation_queue.pop_front())
            }
        }
    }

    /// Finds the next pump to stop
    fn get_next_pump_to_stop(&self) -> Result<Option<String>, PumpControllerError> {
        // Simple logic: stop the most recently started
        let active_pumps: Vec<_> = self
            .pump_states
            .iter()
            .filter(|p| p.operational_state == OperationalState::On)
            .collect();

        if active_pumps.is_empty() {
            return Ok(None);
        }

        // Return the most recent (based on last_start)
        let mut sorted = active_pumps;
        sorted.sort_by(|a, b| {
            let a_time = a.last_start.unwrap_or(Instant::now());
            let b_time = b.last_start.unwrap_or(Instant::now());
            b_time.cmp(&a_time) // Most recent first
        });

        Ok(sorted.first().map(|p| p.id.clone()))
    }

    /// Starts a specific pump
    fn start_pump(&mut self, pump_id: &str) -> Result<(), PumpControllerError> {
        let pump = self
            .pump_states
            .iter_mut()
            .find(|p| p.id == pump_id)
            .ok_or_else(|| PumpControllerError::PumpNotFound(pump_id.to_string()))?;

        if pump.is_faulted {
            return Err(PumpControllerError::PumpFaulted(pump_id.to_string()));
        }

        pump.operational_state = OperationalState::On;
        pump.speed_fraction = 1.0; // Full speed by default
        pump.last_start = Some(Instant::now());
        pump.start_count += 1;

        // Update rotation queue
        self.rotation_queue.push_back(pump_id.to_string());

        Ok(())
    }

    /// Stops a specific pump
    fn stop_pump(&mut self, pump_id: &str) -> Result<(), PumpControllerError> {
        let pump = self
            .pump_states
            .iter_mut()
            .find(|p| p.id == pump_id)
            .ok_or_else(|| PumpControllerError::PumpNotFound(pump_id.to_string()))?;

        pump.operational_state = OperationalState::Off;
        pump.speed_fraction = 0.0;
        pump.current_power_w = 0.0;
        pump.current_flow_m3s = 0.0;
        pump.last_stop = Some(Instant::now());

        Ok(())
    }

    /// Optimizes VFD speeds to minimize power consumption
    fn optimize_vfd_speeds(&mut self) -> Result<(), PumpControllerError> {
        let active_pumps = self.count_active_pumps();

        if active_pumps == 0 {
            return Ok(());
        }

        // Calculate optimal speed for each pump
        let optimal_speed = self.calculate_optimal_speed()?;

        for pump in &mut self.pump_states {
            if pump.operational_state == OperationalState::On {
                let pump_config = self.config.pumps.iter().find(|c| c.id == pump.id).unwrap();

                if pump_config.supports_vfd {
                    // Apply optimal speed with VFD limits
                    if let Some((min_speed, max_speed)) = pump_config.vfd_range {
                        pump.speed_fraction = optimal_speed.clamp(min_speed, max_speed);
                    } else {
                        pump.speed_fraction = optimal_speed;
                    }

                    // Calculate new power (affinity laws)
                    pump.current_power_w =
                        pump_config.nominal_power_w * pump.speed_fraction.powi(3);
                }
            }
        }

        Ok(())
    }

    /// Calculates optimal VFD speed
    fn calculate_optimal_speed(&self) -> Result<f64, PumpControllerError> {
        // Affinity laws: Q ∝ N, P ∝ N³
        // To minimize energy, we want the lowest speed that satisfies flow requirement

        let active_pumps = self.count_active_pumps() as f64;
        let flow_per_pump = self.flow_setpoint_m3s / active_pumps;

        // Required speed (as fraction of nominal)
        let required_speed = flow_per_pump / self.calculate_nominal_flow_per_pump();

        // Apply safety margin (5%)
        let safety_margin = 1.05;

        Ok((required_speed * safety_margin).clamp(0.3, 1.0)) // Min 30%, max 100%
    }

    /// Updates current flow rates
    fn update_flows(&mut self) -> Result<(), PumpControllerError> {
        let mut total_flow = 0.0;

        for pump in &mut self.pump_states {
            if pump.operational_state == OperationalState::On {
                let pump_config = self.config.pumps.iter().find(|c| c.id == pump.id).unwrap();

                // Affinity laws: Q ∝ N
                pump.current_flow_m3s = pump_config.nominal_flow_m3s * pump.speed_fraction;
                total_flow += pump.current_flow_m3s;
            } else {
                pump.current_flow_m3s = 0.0;
            }
        }

        self.current_total_flow_m3s = total_flow;
        Ok(())
    }

    /// Calculates nominal flow per pump (average)
    fn calculate_nominal_flow_per_pump(&self) -> f64 {
        let total_nominal_flow: f64 = self.config.pumps.iter().map(|p| p.nominal_flow_m3s).sum();

        total_nominal_flow / self.config.pumps.len() as f64
    }

    /// Counts active pumps
    pub fn count_active_pumps(&self) -> usize {
        self.pump_states
            .iter()
            .filter(|p| p.operational_state == OperationalState::On)
            .count()
    }

    /// Returns pump states
    pub fn pump_states(&self) -> &[PumpState] {
        &self.pump_states
    }

    /// Returns total current power consumption
    pub fn total_power_consumption(&self) -> f64 {
        self.pump_states.iter().map(|p| p.current_power_w).sum()
    }

    /// Returns total current flow
    pub fn total_flow(&self) -> f64 {
        self.current_total_flow_m3s
    }

    /// Checks if a pump is faulted
    pub fn is_pump_faulted(&self, pump_id: &str) -> bool {
        self.pump_states
            .iter()
            .find(|p| p.id == pump_id)
            .map(|p| p.is_faulted)
            .unwrap_or(false)
    }

    /// Sets a pump fault state
    pub fn set_pump_fault(
        &mut self,
        pump_id: &str,
        faulted: bool,
    ) -> Result<(), PumpControllerError> {
        let pump = self
            .pump_states
            .iter_mut()
            .find(|p| p.id == pump_id)
            .ok_or_else(|| PumpControllerError::PumpNotFound(pump_id.to_string()))?;

        pump.is_faulted = faulted;

        if faulted && pump.operational_state == OperationalState::On {
            // Stop pump if running
            self.stop_pump(pump_id)?;
        }

        Ok(())
    }

    /// Performs scheduled pump rotation
    pub fn rotate_pumps(&mut self) -> Result<(), PumpControllerError> {
        // Check rotation interval
        if let Some(last_rotation) = self.last_rotation {
            let elapsed_hours = last_rotation.elapsed().as_secs() as f64 / 3600.0;
            if elapsed_hours < self.config.rotation_interval_hours {
                return Ok(()); // Not time to rotate yet
            }
        }

        // Rotation: take first active pump and put it at end of queue
        if let Some(first_active) = self
            .pump_states
            .iter()
            .find(|p| p.operational_state == OperationalState::On)
            .map(|p| p.id.clone())
        {
            // Remove from queue and add to end
            if let Some(pos) = self
                .rotation_queue
                .iter()
                .position(|id| *id == first_active)
            {
                self.rotation_queue.remove(pos);
                self.rotation_queue.push_back(first_active);
            }
        }

        self.last_rotation = Some(Instant::now());
        Ok(())
    }
}

/// PumpController errors
#[derive(Debug, thiserror::Error)]
pub enum PumpControllerError {
    #[error("Invalid configuration: {0}")]
    InvalidConfiguration(String),

    #[error("Pump not found: {0}")]
    PumpNotFound(String),

    #[error("Pump faulted: {0}")]
    PumpFaulted(String),

    #[error("Insufficient pumps: {0}")]
    InsufficientPumps(String),

    #[error("Calculation error")]
    CalculationError,
}

impl Default for PumpControllerConfig {
    fn default() -> Self {
        Self {
            pumps: Vec::new(),
            min_active_pumps: 1,
            max_active_pumps: 3,
            sequencing_strategy: SequencingStrategy::RuntimeBased,
            rotation_interval_hours: 168.0, // 1 week
            energy_optimization: true,
            min_switch_interval_secs: 300, // 5 minutes
            anti_short_cycle_time_secs: 300,
        }
    }
}

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

    #[test]
    fn test_pump_controller_creation() {
        let config = PumpControllerConfig {
            pumps: vec![
                PumpConfig {
                    id: "pump1".to_string(),
                    nominal_power_w: 1000.0,
                    nominal_flow_m3s: 0.01,
                    nominal_head_m: 20.0,
                    nominal_rpm: 2900.0,
                    supports_vfd: true,
                    vfd_range: Some((0.3, 1.0)),
                },
                PumpConfig {
                    id: "pump2".to_string(),
                    nominal_power_w: 1000.0,
                    nominal_flow_m3s: 0.01,
                    nominal_head_m: 20.0,
                    nominal_rpm: 2900.0,
                    supports_vfd: true,
                    vfd_range: Some((0.3, 1.0)),
                },
            ],
            min_active_pumps: 1,
            max_active_pumps: 2,
            ..Default::default()
        };

        let controller = PumpController::new(config);
        assert!(controller.is_ok());
    }

    #[test]
    fn test_invalid_configuration() {
        let config = PumpControllerConfig {
            pumps: vec![],
            min_active_pumps: 2,
            max_active_pumps: 1,
            ..Default::default()
        };

        let controller = PumpController::new(config);
        assert!(controller.is_err());
    }

    #[test]
    fn test_pump_sequencing() {
        let config = PumpControllerConfig {
            pumps: vec![
                PumpConfig {
                    id: "pump1".to_string(),
                    nominal_power_w: 1000.0,
                    nominal_flow_m3s: 0.01,
                    nominal_head_m: 20.0,
                    nominal_rpm: 2900.0,
                    supports_vfd: false,
                    vfd_range: None,
                },
                PumpConfig {
                    id: "pump2".to_string(),
                    nominal_power_w: 1000.0,
                    nominal_flow_m3s: 0.01,
                    nominal_head_m: 20.0,
                    nominal_rpm: 2900.0,
                    supports_vfd: false,
                    vfd_range: None,
                },
                PumpConfig {
                    id: "pump3".to_string(),
                    nominal_power_w: 1000.0,
                    nominal_flow_m3s: 0.01,
                    nominal_head_m: 20.0,
                    nominal_rpm: 2900.0,
                    supports_vfd: false,
                    vfd_range: None,
                },
            ],
            min_active_pumps: 1,
            max_active_pumps: 3,
            sequencing_strategy: SequencingStrategy::FixedRotation,
            ..Default::default()
        };

        let mut controller = PumpController::new(config).unwrap();

        // Low demand: 1 pump
        controller.update_demand(0.3, 0.005).unwrap();
        assert_eq!(controller.count_active_pumps(), 1);
        assert_eq!(controller.pump_states()[0].id, "pump1");

        // Medium demand: 2 pumps
        controller.update_demand(0.6, 0.015).unwrap();
        assert_eq!(controller.count_active_pumps(), 2);

        // High demand: 3 pumps
        controller.update_demand(0.9, 0.025).unwrap();
        assert_eq!(controller.count_active_pumps(), 3);

        // Back to low demand
        controller.update_demand(0.2, 0.005).unwrap();
        assert_eq!(controller.count_active_pumps(), 1);
    }

    #[test]
    fn test_pump_fault_handling() {
        let config = PumpControllerConfig {
            pumps: vec![PumpConfig {
                id: "pump1".to_string(),
                nominal_power_w: 1000.0,
                nominal_flow_m3s: 0.01,
                nominal_head_m: 20.0,
                nominal_rpm: 2900.0,
                supports_vfd: false,
                vfd_range: None,
            }],
            min_active_pumps: 1,
            max_active_pumps: 1,
            ..Default::default()
        };

        let mut controller = PumpController::new(config).unwrap();

        // Start pump
        controller.update_demand(1.0, 0.01).unwrap();
        assert_eq!(controller.count_active_pumps(), 1);

        // Set fault
        controller.set_pump_fault("pump1", true).unwrap();
        assert!(controller.is_pump_faulted("pump1"));
        assert_eq!(controller.count_active_pumps(), 0);
    }

    #[test]
    fn test_vfd_optimization() {
        let config = PumpControllerConfig {
            pumps: vec![PumpConfig {
                id: "pump1".to_string(),
                nominal_power_w: 1000.0,
                nominal_flow_m3s: 0.01,
                nominal_head_m: 20.0,
                nominal_rpm: 2900.0,
                supports_vfd: true,
                vfd_range: Some((0.3, 1.0)),
            }],
            min_active_pumps: 1,
            max_active_pumps: 1,
            energy_optimization: true,
            ..Default::default()
        };

        let mut controller = PumpController::new(config).unwrap();

        // 50% demand
        controller.update_demand(0.5, 0.005).unwrap();

        let pump_state = &controller.pump_states()[0];
        assert!(pump_state.speed_fraction < 1.0);
        assert!(pump_state.current_power_w < 1000.0);
    }
}
