//! Component State Machine and Circuit Management
//!
//! This module provides types for managing component operational states (ON/OFF/BYPASS)
//! and circuit identification, as required by FR6-FR9 of the Entropyk specification.
//!
//! ## Operational States
//!
//! Components can be in one of three operational states:
//! - **On**: Normal operation with full thermodynamic behavior
//! - **Off**: Component contributes zero mass flow (FR7)
//! - **Bypass**: Component behaves as an adiabatic pipe (FR8)
//!
//! ## Circuit Identification
//!
//! Each component belongs to a specific circuit identified by a `CircuitId`.
//! Multi-circuit machines allow simulation of complex systems like dual-circuit
//! heat pumps (FR9).
//!
//! ## State Management
//!
//! The [`StateManageable`] trait provides a common interface for components
//! that support operational state management. All major components (Compressor,
//! ExpansionValve, HeatExchanger) implement this trait.
//!
//! ## State History
//!
//! For debugging purposes, the [`StateHistory`] type can track state transitions
//! with timestamps.
//!
//! ## Example
//!
//! ```rust
//! use entropyk_components::state_machine::{OperationalState, CircuitId, StateManageable};
//!
//! // Create a circuit identifier from a number
//! let circuit = CircuitId::from_number(1);
//!
//! // Or from a string (hashed to u8)
//! let circuit_from_str: CircuitId = "primary".into();
//!
//! // Set component state
//! let state = OperationalState::On;
//!
//! // Check transitions
//! assert!(state.can_transition_to(OperationalState::Off));
//! ```

use std::collections::VecDeque;
use std::time::Instant;

use crate::ComponentError;

/// Error type for invalid state transitions.
///
/// This error is returned when attempting an invalid state transition.
/// Currently, all transitions between states are allowed, but this type
/// is provided for future extensibility and custom transition rules.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StateTransitionError {
    /// The state we're transitioning from
    pub from: OperationalState,
    /// The state we're attempting to transition to
    pub to: OperationalState,
    /// Human-readable reason for the failure
    pub reason: &'static str,
}

impl std::fmt::Display for StateTransitionError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Invalid state transition from {:?} to {:?}: {}",
            self.from, self.to, self.reason
        )
    }
}

impl std::error::Error for StateTransitionError {}

impl From<StateTransitionError> for crate::ComponentError {
    fn from(err: StateTransitionError) -> Self {
        crate::ComponentError::InvalidStateTransition {
            from: err.from,
            to: err.to,
            reason: err.reason.to_string(),
        }
    }
}

/// Operational state of a component.
///
/// This enum represents the three possible operational states of a component
/// as defined in FR6-FR8:
///
/// - **On**: Normal operation with full thermodynamic calculations
/// - **Off**: Component contributes zero mass flow to the system
/// - **Bypass**: Component behaves as an adiabatic pipe (P_in = P_out, h_in = h_out)
///
/// # State Behavior
///
/// | State | Mass Flow | Energy Transfer | Pressure Drop |
/// |-------|-----------|-----------------|---------------|
/// | On    | Normal    | Full            | Normal        |
/// | Off   | Zero      | None            | Infinite      |
/// | Bypass| Continuity| None (adiabatic)| Zero          |
///
/// # Examples
///
/// ```
/// use entropyk_components::state_machine::OperationalState;
///
/// let state = OperationalState::On;
/// assert!(state.is_active());
///
/// let state = OperationalState::Off;
/// assert!(!state.is_active());
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum OperationalState {
    /// Normal operation with full thermodynamic behavior.
    ///
    /// In this state, the component performs its full thermodynamic calculations,
    /// including heat transfer, pressure changes, and work transfer.
    On,

    /// Component is turned off, contributing zero mass flow.
    ///
    /// When a component is in the Off state (FR7):
    /// - Mass flow through the component is forced to zero
    /// - The component acts as a blockage in the circuit
    /// - No heat transfer or work transfer occurs
    /// - This state is used for simulating component failures or seasonal operation
    Off,

    /// Bypass mode - component behaves as an adiabatic pipe.
    ///
    /// When a component is in the Bypass state (FR8):
    /// - Pressure at inlet equals pressure at outlet (P_in = P_out)
    /// - Enthalpy at inlet equals enthalpy at outlet (h_in = h_out)
    /// - No heat transfer occurs (adiabatic)
    /// - Mass flow continues through the bypass path
    /// - This state is useful for economizers in summer mode or valve bypasses
    Bypass,
}

impl OperationalState {
    /// Returns true if the component is active (On or Bypass).
    ///
    /// An active component allows mass flow through it, though Bypass
    /// mode has different thermodynamic behavior than On mode.
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::OperationalState;
    ///
    /// assert!(OperationalState::On.is_active());
    /// assert!(OperationalState::Bypass.is_active());
    /// assert!(!OperationalState::Off.is_active());
    /// ```
    pub fn is_active(&self) -> bool {
        matches!(self, OperationalState::On | OperationalState::Bypass)
    }

    /// Returns true if the component is in normal operation mode.
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::OperationalState;
    ///
    /// assert!(OperationalState::On.is_on());
    /// assert!(!OperationalState::Off.is_on());
    /// assert!(!OperationalState::Bypass.is_on());
    /// ```
    pub fn is_on(&self) -> bool {
        matches!(self, OperationalState::On)
    }

    /// Returns true if the component is off.
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::OperationalState;
    ///
    /// assert!(OperationalState::Off.is_off());
    /// assert!(!OperationalState::On.is_off());
    /// ```
    pub fn is_off(&self) -> bool {
        matches!(self, OperationalState::Off)
    }

    /// Returns true if the component is in bypass mode.
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::OperationalState;
    ///
    /// assert!(OperationalState::Bypass.is_bypass());
    /// assert!(!OperationalState::On.is_bypass());
    /// ```
    pub fn is_bypass(&self) -> bool {
        matches!(self, OperationalState::Bypass)
    }

    /// Returns the mass flow multiplier for this state.
    ///
    /// This multiplier is used in residual calculations:
    /// - On: 1.0 (full mass flow)
    /// - Bypass: 1.0 (mass flow continues through bypass)
    /// - Off: 0.0 (no mass flow)
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::OperationalState;
    ///
    /// assert_eq!(OperationalState::On.mass_flow_multiplier(), 1.0);
    /// assert_eq!(OperationalState::Bypass.mass_flow_multiplier(), 1.0);
    /// assert_eq!(OperationalState::Off.mass_flow_multiplier(), 0.0);
    /// ```
    pub fn mass_flow_multiplier(&self) -> f64 {
        match self {
            OperationalState::On => 1.0,
            OperationalState::Off => 0.0,
            OperationalState::Bypass => 1.0,
        }
    }

    /// Checks if a transition to the target state is valid.
    ///
    /// Currently, all state transitions are allowed. This method is provided
    /// for components that may have custom transition rules.
    ///
    /// # Arguments
    ///
    /// * `target` - The target operational state
    ///
    /// # Returns
    ///
    /// Returns `true` if the transition is valid.
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::OperationalState;
    ///
    /// assert!(OperationalState::On.can_transition_to(OperationalState::Off));
    /// assert!(OperationalState::Off.can_transition_to(OperationalState::Bypass));
    /// assert!(OperationalState::Bypass.can_transition_to(OperationalState::On));
    /// ```
    pub fn can_transition_to(&self, target: OperationalState) -> bool {
        matches!(
            (self, target),
            (OperationalState::On, OperationalState::Off)
                | (OperationalState::On, OperationalState::Bypass)
                | (OperationalState::Off, OperationalState::On)
                | (OperationalState::Off, OperationalState::Bypass)
                | (OperationalState::Bypass, OperationalState::On)
                | (OperationalState::Bypass, OperationalState::Off)
                | (OperationalState::On, OperationalState::On)
                | (OperationalState::Off, OperationalState::Off)
                | (OperationalState::Bypass, OperationalState::Bypass)
        )
    }

    /// Attempts to transition to the target state.
    ///
    /// # Arguments
    ///
    /// * `target` - The target operational state
    ///
    /// # Returns
    ///
    /// Returns `Ok(target)` if the transition is valid, or a [`StateTransitionError`]
    /// if the transition is not allowed.
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::OperationalState;
    ///
    /// let state = OperationalState::On;
    /// let new_state = state.transition_to(OperationalState::Off).unwrap();
    /// assert_eq!(new_state, OperationalState::Off);
    /// ```
    pub fn transition_to(
        &self,
        target: OperationalState,
    ) -> Result<OperationalState, StateTransitionError> {
        if self.can_transition_to(target) {
            Ok(target)
        } else {
            Err(StateTransitionError {
                from: *self,
                to: target,
                reason: "Transition not allowed",
            })
        }
    }
}

impl Default for OperationalState {
    /// Default operational state is On.
    fn default() -> Self {
        OperationalState::On
    }
}

pub use entropyk_core::CircuitId;

/// Record of a state transition for debugging purposes.
///
/// Tracks when a component changed states, what the previous state was,
/// and what the new state is.
#[derive(Debug, Clone)]
pub struct StateTransitionRecord {
    /// Timestamp when the transition occurred
    pub timestamp: Instant,
    /// State before the transition
    pub from_state: OperationalState,
    /// State after the transition
    pub to_state: OperationalState,
}

impl StateTransitionRecord {
    /// Creates a new state transition record.
    ///
    /// # Arguments
    ///
    /// * `from_state` - The state before transition
    /// * `to_state` - The state after transition
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::{StateTransitionRecord, OperationalState};
    ///
    /// let record = StateTransitionRecord::new(OperationalState::On, OperationalState::Off);
    /// assert_eq!(record.from_state, OperationalState::On);
    /// assert_eq!(record.to_state, OperationalState::Off);
    /// ```
    pub fn new(from_state: OperationalState, to_state: OperationalState) -> Self {
        Self {
            timestamp: Instant::now(),
            from_state,
            to_state,
        }
    }

    /// Returns the elapsed time since this transition occurred.
    pub fn elapsed(&self) -> std::time::Duration {
        self.timestamp.elapsed()
    }
}

/// History buffer for tracking state transitions.
///
/// Maintains a configurable-size buffer of recent state transitions
/// for debugging and analysis purposes.
///
/// # Examples
///
/// ```
/// use entropyk_components::state_machine::{StateHistory, OperationalState};
///
/// let mut history = StateHistory::new(10);
/// history.record(OperationalState::On, OperationalState::Off);
///
/// assert_eq!(history.len(), 1);
/// assert_eq!(history.records()[0].from_state, OperationalState::On);
/// ```
#[derive(Debug, Clone)]
pub struct StateHistory {
    records: VecDeque<StateTransitionRecord>,
    max_depth: usize,
}

impl StateHistory {
    /// Default maximum history depth.
    pub const DEFAULT_MAX_DEPTH: usize = 10;

    /// Creates a new state history with the specified maximum depth.
    ///
    /// # Arguments
    ///
    /// * `max_depth` - Maximum number of records to keep
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::StateHistory;
    ///
    /// let history = StateHistory::new(20);
    /// assert_eq!(history.max_depth(), 20);
    /// ```
    pub fn new(max_depth: usize) -> Self {
        Self {
            records: VecDeque::with_capacity(max_depth),
            max_depth,
        }
    }

    /// Creates a new state history with default depth (10 records).
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::StateHistory;
    ///
    /// let history = StateHistory::default();
    /// assert_eq!(history.max_depth(), 10);
    /// ```
    pub fn with_default_depth() -> Self {
        Self::new(Self::DEFAULT_MAX_DEPTH)
    }

    /// Returns the maximum number of records this history can hold.
    pub fn max_depth(&self) -> usize {
        self.max_depth
    }

    /// Records a state transition.
    ///
    /// If the history is full, the oldest record is removed.
    ///
    /// # Arguments
    ///
    /// * `from_state` - The state before transition
    /// * `to_state` - The state after transition
    ///
    /// # Examples
    ///
    /// ```
    /// use entropyk_components::state_machine::{StateHistory, OperationalState};
    ///
    /// let mut history = StateHistory::default();
    /// history.record(OperationalState::On, OperationalState::Off);
    ///
    /// assert_eq!(history.len(), 1);
    /// ```
    pub fn record(&mut self, from_state: OperationalState, to_state: OperationalState) {
        if self.records.len() >= self.max_depth {
            self.records.pop_front();
        }
        self.records
            .push_back(StateTransitionRecord::new(from_state, to_state));
    }

    /// Returns the number of records in the history.
    pub fn len(&self) -> usize {
        self.records.len()
    }

    /// Returns true if there are no records in the history.
    pub fn is_empty(&self) -> bool {
        self.records.is_empty()
    }

    /// Returns a slice of all records, oldest first.
    pub fn records(&self) -> &[StateTransitionRecord] {
        self.records.as_slices().0
    }

    /// Returns the most recent transition record, if any.
    pub fn last(&self) -> Option<&StateTransitionRecord> {
        self.records.back()
    }

    /// Clears all records from the history.
    pub fn clear(&mut self) {
        self.records.clear();
    }
}

impl Default for StateHistory {
    fn default() -> Self {
        Self::with_default_depth()
    }
}

/// Trait for components that support operational state management.
///
/// This trait provides a common interface for managing the operational
/// state of thermodynamic components. All major components (Compressor,
/// ExpansionValve, HeatExchanger) implement this trait.
///
/// # Object Safety
///
/// This trait is object-safe and can be used with dynamic dispatch.
///
/// # Callback Hooks
///
/// The trait provides optional callback hooks via `on_state_change()` which
/// can be overridden to perform actions when state transitions occur.
///
/// # Examples
///
/// ```rust,ignore
/// use entropyk_components::state_machine::{StateManageable, OperationalState, CircuitId};
/// use entropyk_components::ComponentError;
///
/// fn check_component_state(component: &dyn StateManageable) {
///     println!("Component state: {:?}", component.state());
///     println!("Circuit: {}", component.circuit_id());
/// }
/// ```
pub trait StateManageable {
    /// Returns the current operational state.
    fn state(&self) -> OperationalState;

    /// Sets the operational state with validation.
    ///
    /// # Arguments
    ///
    /// * `state` - The new operational state
    ///
    /// # Returns
    ///
    /// Returns `Ok(())` if the transition is valid, or a [`ComponentError`]
    /// if the transition is not allowed.
    ///
    /// # Errors
    ///
    /// Returns [`ComponentError::InvalidStateTransition`] if the transition
    /// is not valid for this component.
    fn set_state(&mut self, state: OperationalState) -> Result<(), ComponentError>;

    /// Checks if a transition to the target state is valid.
    ///
    /// # Arguments
    ///
    /// * `target` - The target operational state
    ///
    /// # Returns
    ///
    /// Returns `true` if the transition is valid.
    fn can_transition_to(&self, target: OperationalState) -> bool;

    /// Returns the circuit identifier.
    fn circuit_id(&self) -> &CircuitId;

    /// Sets the circuit identifier.
    ///
    /// # Arguments
    ///
    /// * `circuit_id` - The new circuit identifier
    fn set_circuit_id(&mut self, circuit_id: CircuitId);

    /// Optional callback invoked after a state change.
    ///
    /// Override this method to perform actions when the component's state changes,
    /// such as logging, updating internal state, or triggering side effects.
    ///
    /// # Arguments
    ///
    /// * `from` - The previous operational state
    /// * `to` - The new operational state
    ///
    /// # Default Implementation
    ///
    /// The default implementation does nothing. Override to add custom behavior.
    fn on_state_change(&mut self, _from: OperationalState, _to: OperationalState) {
        // Default: no-op. Override to add callback behavior.
    }

    /// Returns the state transition history, if tracking is enabled.
    ///
    /// Components can optionally track state transition history for debugging.
    /// By default, this returns `None`. Override to return a reference to
    /// the component's state history.
    ///
    /// # Returns
    ///
    /// Returns `Some(&StateHistory)` if history tracking is enabled, or `None` otherwise.
    fn state_history(&self) -> Option<&StateHistory> {
        None
    }
}

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

    #[test]
    fn test_operational_state_on() {
        let state = OperationalState::On;
        assert!(state.is_on());
        assert!(!state.is_off());
        assert!(!state.is_bypass());
        assert!(state.is_active());
        assert_eq!(state.mass_flow_multiplier(), 1.0);
    }

    #[test]
    fn test_operational_state_off() {
        let state = OperationalState::Off;
        assert!(!state.is_on());
        assert!(state.is_off());
        assert!(!state.is_bypass());
        assert!(!state.is_active());
        assert_eq!(state.mass_flow_multiplier(), 0.0);
    }

    #[test]
    fn test_operational_state_bypass() {
        let state = OperationalState::Bypass;
        assert!(!state.is_on());
        assert!(!state.is_off());
        assert!(state.is_bypass());
        assert!(state.is_active());
        assert_eq!(state.mass_flow_multiplier(), 1.0);
    }

    #[test]
    fn test_operational_state_default() {
        let state: OperationalState = Default::default();
        assert!(state.is_on());
    }

    #[test]
    fn test_operational_state_equality() {
        assert_eq!(OperationalState::On, OperationalState::On);
        assert_eq!(OperationalState::Off, OperationalState::Off);
        assert_eq!(OperationalState::Bypass, OperationalState::Bypass);
        assert_ne!(OperationalState::On, OperationalState::Off);
        assert_ne!(OperationalState::On, OperationalState::Bypass);
    }

    #[test]
    fn test_can_transition_to_all_combinations() {
        let states = [
            OperationalState::On,
            OperationalState::Off,
            OperationalState::Bypass,
        ];

        for from in states {
            for to in states {
                assert!(
                    from.can_transition_to(to),
                    "Transition from {:?} to {:?} should be allowed",
                    from,
                    to
                );
            }
        }
    }

    #[test]
    fn test_transition_to_success() {
        let state = OperationalState::On;
        let result = state.transition_to(OperationalState::Off);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), OperationalState::Off);
    }

    #[test]
    fn test_transition_to_same_state() {
        let state = OperationalState::On;
        let result = state.transition_to(OperationalState::On);
        assert!(result.is_ok());
        assert_eq!(result.unwrap(), OperationalState::On);
    }

    #[test]
    fn test_state_transition_error_display() {
        let err = StateTransitionError {
            from: OperationalState::On,
            to: OperationalState::Off,
            reason: "Test reason",
        };
        let msg = format!("{}", err);
        assert!(msg.contains("Invalid state transition"));
        assert!(msg.contains("On"));
        assert!(msg.contains("Off"));
        assert!(msg.contains("Test reason"));
    }

    #[test]
    fn test_circuit_id_from_number() {
        let circuit = CircuitId::from_number(5);
        assert_eq!(circuit.as_number(), 5);
    }

    #[test]
    fn test_circuit_id_from_u8() {
        let circuit: CircuitId = 42u16.into();
        assert_eq!(circuit.0, 42);
    }

    #[test]
    fn test_circuit_id_from_str_deterministic() {
        let c1: CircuitId = "primary".into();
        let c2: CircuitId = "primary".into();
        assert_eq!(c1, c2);
    }

    #[test]
    fn test_circuit_id_default() {
        let circuit = CircuitId::default();
        assert_eq!(circuit, CircuitId::ZERO);
    }

    #[test]
    fn test_circuit_id_zero() {
        assert_eq!(CircuitId::ZERO.0, 0);
    }

    #[test]
    fn test_circuit_id_equality() {
        let c1 = CircuitId(1);
        let c2 = CircuitId(1);
        let c3 = CircuitId(2);
        assert_eq!(c1, c2);
        assert_ne!(c1, c3);
    }

    #[test]
    fn test_circuit_id_display() {
        let circuit = CircuitId(3);
        assert_eq!(format!("{}", circuit), "Circuit-3");
    }

    #[test]
    fn test_circuit_id_ordering() {
        let c1 = CircuitId(1);
        let c2 = CircuitId(2);
        assert!(c1 < c2);
    }

    #[test]
    fn test_circuit_id_hash() {
        use std::collections::HashMap;

        let mut map = HashMap::new();
        map.insert(CircuitId(1), 1);
        map.insert(CircuitId(2), 2);

        assert_eq!(map.get(&CircuitId(1)), Some(&1));
        assert_eq!(map.get(&CircuitId(2)), Some(&2));
    }

    #[test]
    fn test_state_transition_record_creation() {
        let record = StateTransitionRecord::new(OperationalState::On, OperationalState::Off);
        assert_eq!(record.from_state, OperationalState::On);
        assert_eq!(record.to_state, OperationalState::Off);
    }

    #[test]
    fn test_state_transition_record_elapsed() {
        let record = StateTransitionRecord::new(OperationalState::On, OperationalState::Off);
        let elapsed = record.elapsed();
        let _ = elapsed.as_nanos();
    }

    #[test]
    fn test_state_history_creation() {
        let history = StateHistory::new(5);
        assert_eq!(history.max_depth(), 5);
        assert!(history.is_empty());
        assert_eq!(history.len(), 0);
    }

    #[test]
    fn test_state_history_default() {
        let history = StateHistory::default();
        assert_eq!(history.max_depth(), StateHistory::DEFAULT_MAX_DEPTH);
    }

    #[test]
    fn test_state_history_with_default_depth() {
        let history = StateHistory::with_default_depth();
        assert_eq!(history.max_depth(), 10);
    }

    #[test]
    fn test_state_history_record() {
        let mut history = StateHistory::new(10);
        history.record(OperationalState::On, OperationalState::Off);

        assert_eq!(history.len(), 1);
        assert!(!history.is_empty());

        let records = history.records();
        assert_eq!(records.len(), 1);
        assert_eq!(records[0].from_state, OperationalState::On);
        assert_eq!(records[0].to_state, OperationalState::Off);
    }

    #[test]
    fn test_state_history_last() {
        let mut history = StateHistory::new(10);
        assert!(history.last().is_none());

        history.record(OperationalState::On, OperationalState::Off);
        let last = history.last().unwrap();
        assert_eq!(last.from_state, OperationalState::On);
        assert_eq!(last.to_state, OperationalState::Off);

        history.record(OperationalState::Off, OperationalState::Bypass);
        let last = history.last().unwrap();
        assert_eq!(last.from_state, OperationalState::Off);
        assert_eq!(last.to_state, OperationalState::Bypass);
    }

    #[test]
    fn test_state_history_max_depth() {
        let mut history = StateHistory::new(3);

        history.record(OperationalState::On, OperationalState::Off);
        history.record(OperationalState::Off, OperationalState::Bypass);
        history.record(OperationalState::Bypass, OperationalState::On);
        assert_eq!(history.len(), 3);

        history.record(OperationalState::On, OperationalState::Off);
        assert_eq!(history.len(), 3);

        let records = history.records();
        assert_eq!(records[0].from_state, OperationalState::Off);
        assert_eq!(records[0].to_state, OperationalState::Bypass);
    }

    #[test]
    fn test_state_history_clear() {
        let mut history = StateHistory::new(10);
        history.record(OperationalState::On, OperationalState::Off);
        history.record(OperationalState::Off, OperationalState::Bypass);

        assert_eq!(history.len(), 2);

        history.clear();

        assert!(history.is_empty());
        assert_eq!(history.len(), 0);
    }

    #[test]
    fn test_state_history_multiple_transitions() {
        let mut history = StateHistory::new(10);

        history.record(OperationalState::On, OperationalState::Off);
        history.record(OperationalState::Off, OperationalState::Bypass);
        history.record(OperationalState::Bypass, OperationalState::On);

        assert_eq!(history.len(), 3);

        let records = history.records();
        assert_eq!(records[0].from_state, OperationalState::On);
        assert_eq!(records[1].from_state, OperationalState::Off);
        assert_eq!(records[2].from_state, OperationalState::Bypass);
    }
}
