use serde::{Deserialize, Serialize};

/// Chapter 11: Freeway Reliability Analysis - Adjustment Factors
///
/// This module provides Capacity Adjustment Factors (CAF) and Speed Adjustment
/// Factors (SAF) for weather, incidents, and work zones as defined in HCM Chapter 11.

// =============================================================================
// Weather Conditions (Exhibit 11-20 and 11-21)
// =============================================================================

/// Method for looking up adjustment factors by FFS
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum FfsLookupMethod {
    /// Use linear interpolation between FFS bins (55, 60, 65, 70, 75 mi/h)
    #[default]
    Interpolate,
    /// Use exact bin values only (rounds to nearest bin)
    ExactBin,
}

/// Weather event types as defined in HCM Chapter 11
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WeatherCondition {
    /// >0.10-0.25 in./h
    MediumRain,
    /// >0.25 in./h
    HeavyRain,
    /// >0.00-0.05 in./h
    LightSnow,
    /// >0.05-0.10 in./h
    LightMediumSnow,
    /// >0.10-0.50 in./h
    MediumHeavySnow,
    /// >0.50 in./h
    HeavySnow,
    /// <-4°F
    SevereCold,
    /// 0.50-0.99 mi visibility
    LowVisibility,
    /// 0.25-0.49 mi visibility
    VeryLowVisibility,
    /// <0.25 mi visibility
    MinimalVisibility,
    /// All conditions not listed above
    NonSevereWeather,
}

impl WeatherCondition {
    /// Get the Capacity Adjustment Factor (CAF) for this weather condition
    /// based on facility free-flow speed.
    /// From Exhibit 11-20: Default CAFs by Weather Condition
    ///
    /// Uses interpolation by default. Use `get_caf_with_method` to specify lookup method.
    pub fn get_caf(&self, ffs: f64) -> f64 {
        self.get_caf_with_method(ffs, FfsLookupMethod::default())
    }

    /// Get the Capacity Adjustment Factor (CAF) with specified lookup method.
    /// From Exhibit 11-20: Default CAFs by Weather Condition
    pub fn get_caf_with_method(&self, ffs: f64, method: FfsLookupMethod) -> f64 {
        // FFS bins: 55, 60, 65, 70, 75 mi/h
        let caf_values = match self {
            WeatherCondition::MediumRain => [0.94, 0.93, 0.92, 0.91, 0.90],
            WeatherCondition::HeavyRain => [0.89, 0.88, 0.86, 0.84, 0.82],
            WeatherCondition::LightSnow => [0.97, 0.96, 0.96, 0.95, 0.95],
            WeatherCondition::LightMediumSnow => [0.95, 0.94, 0.92, 0.90, 0.88],
            WeatherCondition::MediumHeavySnow => [0.93, 0.91, 0.90, 0.88, 0.87],
            WeatherCondition::HeavySnow => [0.80, 0.78, 0.76, 0.74, 0.72],
            WeatherCondition::SevereCold => [0.93, 0.92, 0.92, 0.91, 0.90],
            WeatherCondition::LowVisibility => [0.90, 0.90, 0.90, 0.90, 0.90],
            WeatherCondition::VeryLowVisibility => [0.88, 0.88, 0.88, 0.88, 0.88],
            WeatherCondition::MinimalVisibility => [0.90, 0.90, 0.90, 0.90, 0.90],
            WeatherCondition::NonSevereWeather => [1.00, 1.00, 1.00, 1.00, 1.00],
        };

        Self::lookup_by_ffs(ffs, &caf_values, method)
    }

    /// Get the Speed Adjustment Factor (SAF) for this weather condition
    /// based on facility free-flow speed.
    /// From Exhibit 11-21: Default SAFs by Weather Condition
    ///
    /// Uses interpolation by default. Use `get_saf_with_method` to specify lookup method.
    pub fn get_saf(&self, ffs: f64) -> f64 {
        self.get_saf_with_method(ffs, FfsLookupMethod::default())
    }

    /// Get the Speed Adjustment Factor (SAF) with specified lookup method.
    /// From Exhibit 11-21: Default SAFs by Weather Condition
    pub fn get_saf_with_method(&self, ffs: f64, method: FfsLookupMethod) -> f64 {
        // FFS bins: 55, 60, 65, 70, 75 mi/h
        let saf_values = match self {
            WeatherCondition::MediumRain => [0.96, 0.95, 0.94, 0.93, 0.93],
            WeatherCondition::HeavyRain => [0.94, 0.93, 0.93, 0.92, 0.91],
            WeatherCondition::LightSnow => [0.94, 0.92, 0.89, 0.87, 0.84],
            WeatherCondition::LightMediumSnow => [0.92, 0.90, 0.88, 0.86, 0.83],
            WeatherCondition::MediumHeavySnow => [0.90, 0.88, 0.86, 0.84, 0.82],
            WeatherCondition::HeavySnow => [0.88, 0.86, 0.85, 0.83, 0.81],
            WeatherCondition::SevereCold => [0.95, 0.95, 0.94, 0.93, 0.92],
            WeatherCondition::LowVisibility => [0.96, 0.95, 0.94, 0.94, 0.93],
            WeatherCondition::VeryLowVisibility => [0.95, 0.94, 0.93, 0.92, 0.91],
            WeatherCondition::MinimalVisibility => [0.95, 0.94, 0.93, 0.92, 0.91],
            WeatherCondition::NonSevereWeather => [1.00, 1.00, 1.00, 1.00, 1.00],
        };

        Self::lookup_by_ffs(ffs, &saf_values, method)
    }

    /// Lookup adjustment factor based on FFS using specified method
    fn lookup_by_ffs(ffs: f64, values: &[f64; 5], method: FfsLookupMethod) -> f64 {
        let ffs_bins = [55.0, 60.0, 65.0, 70.0, 75.0];

        match method {
            FfsLookupMethod::Interpolate => {
                if ffs <= ffs_bins[0] {
                    return values[0];
                }
                if ffs >= ffs_bins[4] {
                    return values[4];
                }

                for i in 0..4 {
                    if ffs >= ffs_bins[i] && ffs <= ffs_bins[i + 1] {
                        let ratio = (ffs - ffs_bins[i]) / (ffs_bins[i + 1] - ffs_bins[i]);
                        return values[i] + ratio * (values[i + 1] - values[i]);
                    }
                }

                values[2] // Default to 65 mi/h value
            }
            FfsLookupMethod::ExactBin => {
                // Round to nearest bin
                let bin_index = if ffs <= 57.5 {
                    0 // 55 mi/h
                } else if ffs <= 62.5 {
                    1 // 60 mi/h
                } else if ffs <= 67.5 {
                    2 // 65 mi/h
                } else if ffs <= 72.5 {
                    3 // 70 mi/h
                } else {
                    4 // 75 mi/h
                };
                values[bin_index]
            }
        }
    }
}

// =============================================================================
// Incident Types (Exhibit 11-22 and 11-23)
// =============================================================================

/// Incident severity types as defined in HCM Chapter 11
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum IncidentSeverity {
    /// Shoulder closure only
    ShoulderClosed,
    /// One lane blocked
    OneLaneClosed,
    /// Two lanes blocked
    TwoLanesClosed,
    /// Three lanes blocked
    ThreeLanesClosed,
    /// Four or more lanes blocked
    FourPlusLanesClosed,
}

/// Incident duration parameters from Exhibit 11-22
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IncidentDuration {
    /// Mean duration in minutes
    pub mean: f64,
    /// Standard deviation in minutes
    pub std_dev: f64,
    /// Minimum duration in minutes
    pub min: f64,
    /// Maximum duration in minutes
    pub max: f64,
}

impl IncidentSeverity {
    /// Get the default incident severity distribution percentage
    /// From Exhibit 11-22
    pub fn get_distribution_pct(&self) -> f64 {
        match self {
            IncidentSeverity::ShoulderClosed => 75.4,
            IncidentSeverity::OneLaneClosed => 19.6,
            IncidentSeverity::TwoLanesClosed => 3.1,
            IncidentSeverity::ThreeLanesClosed => 1.9,
            IncidentSeverity::FourPlusLanesClosed => 0.0,
        }
    }

    /// Get the default incident duration parameters
    /// From Exhibit 11-22
    pub fn get_duration_params(&self) -> IncidentDuration {
        match self {
            IncidentSeverity::ShoulderClosed => IncidentDuration {
                mean: 34.0,
                std_dev: 15.1,
                min: 8.7,
                max: 58.0,
            },
            IncidentSeverity::OneLaneClosed => IncidentDuration {
                mean: 34.6,
                std_dev: 13.8,
                min: 16.0,
                max: 58.2,
            },
            IncidentSeverity::TwoLanesClosed => IncidentDuration {
                mean: 53.6,
                std_dev: 13.9,
                min: 30.5,
                max: 66.9,
            },
            IncidentSeverity::ThreeLanesClosed => IncidentDuration {
                mean: 67.9,
                std_dev: 21.9,
                min: 36.0,
                max: 93.3,
            },
            IncidentSeverity::FourPlusLanesClosed => IncidentDuration {
                mean: 67.9,
                std_dev: 21.9,
                min: 36.0,
                max: 93.3,
            },
        }
    }

    /// Get the Capacity Adjustment Factor (CAF) for this incident type
    /// based on number of directional lanes.
    /// From Exhibit 11-23: CAFs by Incident Type and Number of Directional Lanes
    ///
    /// Returns None if the incident is not applicable (lanes closed >= directional lanes)
    pub fn get_caf(&self, directional_lanes: u32) -> Option<f64> {
        if directional_lanes < 2 || directional_lanes > 8 {
            return None;
        }

        // CAF table indexed by [lanes - 2][incident_type]
        // Columns: No Incident, Shoulder, 1 Lane, 2 Lanes, 3 Lanes, 4 Lanes
        let caf_table: [[Option<f64>; 6]; 7] = [
            // 2 lanes
            [Some(1.00), Some(0.81), Some(0.70), None, None, None],
            // 3 lanes
            [Some(1.00), Some(0.83), Some(0.74), Some(0.51), None, None],
            // 4 lanes
            [Some(1.00), Some(0.85), Some(0.77), Some(0.50), Some(0.52), None],
            // 5 lanes
            [Some(1.00), Some(0.87), Some(0.81), Some(0.67), Some(0.50), Some(0.50)],
            // 6 lanes
            [Some(1.00), Some(0.89), Some(0.85), Some(0.75), Some(0.52), Some(0.52)],
            // 7 lanes
            [Some(1.00), Some(0.91), Some(0.88), Some(0.80), Some(0.63), Some(0.63)],
            // 8 lanes
            [Some(1.00), Some(0.93), Some(0.89), Some(0.84), Some(0.66), Some(0.66)],
        ];

        let row = (directional_lanes - 2) as usize;
        let col = match self {
            IncidentSeverity::ShoulderClosed => 1,
            IncidentSeverity::OneLaneClosed => 2,
            IncidentSeverity::TwoLanesClosed => 3,
            IncidentSeverity::ThreeLanesClosed => 4,
            IncidentSeverity::FourPlusLanesClosed => 5,
        };

        caf_table[row][col]
    }

    /// Get the number of lanes blocked by this incident
    pub fn lanes_blocked(&self) -> u32 {
        match self {
            IncidentSeverity::ShoulderClosed => 0,
            IncidentSeverity::OneLaneClosed => 1,
            IncidentSeverity::TwoLanesClosed => 2,
            IncidentSeverity::ThreeLanesClosed => 3,
            IncidentSeverity::FourPlusLanesClosed => 4,
        }
    }
}

// =============================================================================
// Work Zone Types
// =============================================================================

/// Work zone capacity adjustment factor calculation
/// From Chapter 10: CAF_WZ = c_WZ / c
///
/// Work zone CAF is the ratio of work zone capacity to normal capacity.
/// This depends on the specific work zone configuration.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkZoneCAF {
    /// Work zone capacity (pc/h/ln)
    pub work_zone_capacity: f64,
    /// Normal segment capacity (pc/h/ln)
    pub normal_capacity: f64,
}

impl WorkZoneCAF {
    /// Create a new work zone CAF calculation
    pub fn new(work_zone_capacity: f64, normal_capacity: f64) -> Self {
        Self {
            work_zone_capacity,
            normal_capacity,
        }
    }

    /// Calculate the work zone CAF
    /// CAF_WZ = c_WZ / c (from Chapter 10)
    pub fn calculate_caf(&self) -> f64 {
        if self.normal_capacity <= 0.0 {
            return 1.0;
        }
        (self.work_zone_capacity / self.normal_capacity).min(1.0)
    }
}

/// Work zone types for descriptive purposes
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WorkZoneType {
    /// Shoulder work only
    ShoulderWork,
    /// One lane closed
    OneLaneClosure,
    /// Two lanes closed
    TwoLaneClosure,
    /// Three or more lanes closed
    ThreePlusLaneClosure,
}

/// Work zone barrier types affecting capacity
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum WorkZoneBarrier {
    /// Cones, drums, or other soft barrier
    SoftBarrier,
    /// Concrete or other hard barrier
    HardBarrier,
}

impl WorkZoneType {
    /// Get the number of lanes closed
    pub fn lanes_closed(&self) -> u32 {
        match self {
            WorkZoneType::ShoulderWork => 0,
            WorkZoneType::OneLaneClosure => 1,
            WorkZoneType::TwoLaneClosure => 2,
            WorkZoneType::ThreePlusLaneClosure => 3,
        }
    }

    /// Estimate work zone capacity based on remaining lanes
    /// This is a simplified estimate; actual capacity depends on many factors
    ///
    /// # Arguments
    /// * `normal_lanes` - Number of lanes in normal conditions
    /// * `capacity_per_lane` - Normal capacity per lane (pc/h/ln)
    pub fn estimate_work_zone_capacity(&self, normal_lanes: u32, capacity_per_lane: f64) -> f64 {
        let remaining_lanes = normal_lanes.saturating_sub(self.lanes_closed());
        if remaining_lanes == 0 {
            return 0.0;
        }
        // Work zone lanes typically operate at reduced capacity (roughly 1600-1800 pc/h/ln)
        let wz_capacity_per_lane = capacity_per_lane.min(1700.0);
        remaining_lanes as f64 * wz_capacity_per_lane
    }
}

// =============================================================================
// Driver Population Adjustment
// =============================================================================

/// Driver population types affecting capacity and speed
/// From Exhibit 26-9: Recommended CAF and SAF Adjustments for Driver Population Impacts
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DriverPopulation {
    /// All familiar drivers, regular commuters
    AllFamiliar,
    /// Mostly familiar drivers
    MostlyFamiliar,
    /// Balanced mix of familiar and unfamiliar drivers
    BalancedMix,
    /// Mostly unfamiliar drivers
    MostlyUnfamiliar,
    /// All or overwhelmingly unfamiliar drivers
    AllUnfamiliar,
}

impl DriverPopulation {
    /// Get the CAF (CAFpop) for this driver population type
    /// From Exhibit 26-9
    pub fn get_caf(&self) -> f64 {
        match self {
            DriverPopulation::AllFamiliar => 1.000,
            DriverPopulation::MostlyFamiliar => 0.968,
            DriverPopulation::BalancedMix => 0.939,
            DriverPopulation::MostlyUnfamiliar => 0.898,
            DriverPopulation::AllUnfamiliar => 0.852,
        }
    }

    /// Get the SAF (SAFpop) for this driver population type
    /// From Exhibit 26-9
    pub fn get_saf(&self) -> f64 {
        match self {
            DriverPopulation::AllFamiliar => 1.000,
            DriverPopulation::MostlyFamiliar => 0.975,
            DriverPopulation::BalancedMix => 0.950,
            DriverPopulation::MostlyUnfamiliar => 0.913,
            DriverPopulation::AllUnfamiliar => 0.863,
        }
    }
}

// =============================================================================
// Demand Ratios (Exhibit 11-18 and 11-19)
// =============================================================================

/// Day of week for demand ratio lookup
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DayOfWeek {
    Monday = 0,
    Tuesday = 1,
    Wednesday = 2,
    Thursday = 3,
    Friday = 4,
    Saturday = 5,
    Sunday = 6,
}

/// Month of year for demand ratio lookup
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Month {
    January = 0,
    February = 1,
    March = 2,
    April = 3,
    May = 4,
    June = 5,
    July = 6,
    August = 7,
    September = 8,
    October = 9,
    November = 10,
    December = 11,
}

/// Urban freeway demand ratios from Exhibit 11-18
/// Ratios relative to Monday in January
pub fn get_urban_demand_ratio(month: Month, day: DayOfWeek) -> f64 {
    // [month][day_of_week]
    // From Exhibit 11-18: Default Urban Freeway Demand Ratios (ADT/Mondays in January)
    let ratios: [[f64; 7]; 12] = [
        // Jan: Mon, Tue, Wed, Thu, Fri, Sat, Sun
        [1.00, 1.00, 1.02, 1.05, 1.17, 1.01, 0.89],
        // Feb
        [1.03, 1.03, 1.05, 1.08, 1.21, 1.04, 0.92],
        // Mar
        [1.12, 1.12, 1.14, 1.18, 1.31, 1.13, 0.99],
        // Apr
        [1.19, 1.19, 1.21, 1.25, 1.39, 1.20, 1.05],
        // May
        [1.18, 1.18, 1.21, 1.24, 1.39, 1.20, 1.05],
        // Jun
        [1.24, 1.24, 1.27, 1.31, 1.46, 1.26, 1.10],
        // Jul
        [1.38, 1.38, 1.41, 1.45, 1.62, 1.39, 1.22],
        // Aug
        [1.26, 1.26, 1.28, 1.32, 1.47, 1.27, 1.12],
        // Sep
        [1.29, 1.29, 1.32, 1.36, 1.52, 1.31, 1.15],
        // Oct
        [1.21, 1.21, 1.24, 1.27, 1.42, 1.22, 1.07],
        // Nov
        [1.21, 1.21, 1.24, 1.27, 1.42, 1.22, 1.07],
        // Dec
        [1.19, 1.19, 1.21, 1.25, 1.40, 1.20, 1.06],
    ];

    ratios[month as usize][day as usize]
}

/// Rural freeway demand ratios from Exhibit 11-19
/// Ratios relative to Monday in January
pub fn get_rural_demand_ratio(month: Month, day: DayOfWeek) -> f64 {
    // [month][day_of_week]
    // From Exhibit 11-19: Default Rural Freeway Demand Ratios (ADT/Mondays in January)
    let ratios: [[f64; 7]; 12] = [
        // Jan: Mon, Tue, Wed, Thu, Fri, Sat, Sun
        [1.00, 0.96, 0.98, 1.03, 1.22, 1.11, 1.06],
        // Feb
        [1.11, 1.06, 1.09, 1.14, 1.35, 1.23, 1.18],
        // Mar
        [1.24, 1.19, 1.21, 1.28, 1.51, 1.37, 1.32],
        // Apr
        [1.33, 1.27, 1.30, 1.37, 1.62, 1.47, 1.41],
        // May
        [1.46, 1.39, 1.42, 1.50, 1.78, 1.61, 1.55],
        // Jun
        [1.48, 1.42, 1.45, 1.53, 1.81, 1.63, 1.57],
        // Jul
        [1.66, 1.59, 1.63, 1.72, 2.03, 1.84, 1.77],
        // Aug
        [1.52, 1.46, 1.49, 1.57, 1.86, 1.68, 1.62],
        // Sep
        [1.46, 1.39, 1.42, 1.50, 1.78, 1.61, 1.55],
        // Oct
        [1.33, 1.28, 1.31, 1.38, 1.63, 1.47, 1.42],
        // Nov
        [1.30, 1.25, 1.28, 1.35, 1.59, 1.44, 1.39],
        // Dec
        [1.17, 1.12, 1.14, 1.20, 1.43, 1.29, 1.24],
    ];

    ratios[month as usize][day as usize]
}

// =============================================================================
// Combined Adjustment Factors
// =============================================================================

/// Combined capacity adjustment factors for reliability analysis
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CapacityAdjustmentFactors {
    /// Weather-related CAF
    pub weather_caf: f64,
    /// Incident-related CAF
    pub incident_caf: Option<f64>,
    /// Work zone CAF
    pub work_zone_caf: f64,
    /// Driver population CAF
    pub driver_population_caf: f64,
}

impl Default for CapacityAdjustmentFactors {
    fn default() -> Self {
        Self {
            weather_caf: 1.0,
            incident_caf: None,
            work_zone_caf: 1.0,
            driver_population_caf: 1.0,
        }
    }
}

impl CapacityAdjustmentFactors {
    /// Create new adjustment factors
    pub fn new() -> Self {
        Self::default()
    }

    /// Set weather CAF from weather condition
    pub fn with_weather(mut self, condition: WeatherCondition, ffs: f64) -> Self {
        self.weather_caf = condition.get_caf(ffs);
        self
    }

    /// Set incident CAF from incident severity
    pub fn with_incident(mut self, severity: IncidentSeverity, directional_lanes: u32) -> Self {
        self.incident_caf = severity.get_caf(directional_lanes);
        self
    }

    /// Set work zone CAF using WorkZoneCAF calculation
    pub fn with_work_zone_caf(mut self, wz_caf: &WorkZoneCAF) -> Self {
        self.work_zone_caf = wz_caf.calculate_caf();
        self
    }

    /// Set work zone CAF directly
    pub fn with_work_zone_caf_value(mut self, caf: f64) -> Self {
        self.work_zone_caf = caf;
        self
    }

    /// Set driver population CAF
    pub fn with_driver_population(mut self, population: DriverPopulation) -> Self {
        self.driver_population_caf = population.get_caf();
        self
    }

    /// Calculate combined CAF (multiplicative)
    /// When multiple factors affect capacity, their combined effect is multiplicative
    pub fn combined_caf(&self) -> f64 {
        let mut caf = self.weather_caf * self.work_zone_caf * self.driver_population_caf;
        if let Some(incident_caf) = self.incident_caf {
            caf *= incident_caf;
        }
        caf
    }
}

/// Combined speed adjustment factors for reliability analysis
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpeedAdjustmentFactors {
    /// Weather-related SAF
    pub weather_saf: f64,
    /// Driver population SAF
    pub driver_population_saf: f64,
}

impl Default for SpeedAdjustmentFactors {
    fn default() -> Self {
        Self {
            weather_saf: 1.0,
            driver_population_saf: 1.0,
        }
    }
}

impl SpeedAdjustmentFactors {
    /// Create new adjustment factors
    pub fn new() -> Self {
        Self::default()
    }

    /// Set weather SAF from weather condition
    pub fn with_weather(mut self, condition: WeatherCondition, ffs: f64) -> Self {
        self.weather_saf = condition.get_saf(ffs);
        self
    }

    /// Set driver population SAF
    pub fn with_driver_population(mut self, population: DriverPopulation) -> Self {
        self.driver_population_saf = population.get_saf();
        self
    }

    /// Calculate combined SAF (multiplicative)
    pub fn combined_saf(&self) -> f64 {
        self.weather_saf * self.driver_population_saf
    }
}

/// Demand Adjustment Factor for reliability analysis
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DemandAdjustmentFactor {
    /// Base demand multiplier from day/month combination
    pub demand_ratio: f64,
    /// Additional multiplier for special events, weather effects, etc.
    pub additional_multiplier: f64,
}

impl Default for DemandAdjustmentFactor {
    fn default() -> Self {
        Self {
            demand_ratio: 1.0,
            additional_multiplier: 1.0,
        }
    }
}

impl DemandAdjustmentFactor {
    /// Create new DAF
    pub fn new() -> Self {
        Self::default()
    }

    /// Set demand ratio for urban freeway
    pub fn with_urban_demand(mut self, month: Month, day: DayOfWeek) -> Self {
        self.demand_ratio = get_urban_demand_ratio(month, day);
        self
    }

    /// Set demand ratio for rural freeway
    pub fn with_rural_demand(mut self, month: Month, day: DayOfWeek) -> Self {
        self.demand_ratio = get_rural_demand_ratio(month, day);
        self
    }

    /// Set additional multiplier
    pub fn with_multiplier(mut self, multiplier: f64) -> Self {
        self.additional_multiplier = multiplier;
        self
    }

    /// Calculate combined DAF
    pub fn combined_daf(&self) -> f64 {
        self.demand_ratio * self.additional_multiplier
    }
}

// =============================================================================
// Reliability Analysis Parameters
// =============================================================================

/// Scenario for reliability analysis
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReliabilityScenario {
    /// Day of week
    pub day: DayOfWeek,
    /// Month of year
    pub month: Month,
    /// Weather condition
    pub weather: WeatherCondition,
    /// Incident severity (if any)
    pub incident: Option<IncidentSeverity>,
    /// Work zone CAF (if any) - calculated as c_WZ / c per Chapter 10
    pub work_zone_caf: Option<f64>,
    /// Driver population
    pub driver_population: DriverPopulation,
}

impl ReliabilityScenario {
    /// Create a base scenario with no incidents or work zones
    pub fn new(day: DayOfWeek, month: Month) -> Self {
        Self {
            day,
            month,
            weather: WeatherCondition::NonSevereWeather,
            incident: None,
            work_zone_caf: None,
            driver_population: DriverPopulation::AllFamiliar,
        }
    }

    /// Set weather condition
    pub fn with_weather(mut self, weather: WeatherCondition) -> Self {
        self.weather = weather;
        self
    }

    /// Set incident
    pub fn with_incident(mut self, incident: IncidentSeverity) -> Self {
        self.incident = Some(incident);
        self
    }

    /// Set work zone CAF directly
    /// Work zone CAF should be calculated as c_WZ / c (work zone capacity / normal capacity)
    pub fn with_work_zone_caf(mut self, caf: f64) -> Self {
        self.work_zone_caf = Some(caf);
        self
    }

    /// Set work zone CAF from WorkZoneCAF calculation
    pub fn with_work_zone(mut self, wz_caf: &WorkZoneCAF) -> Self {
        self.work_zone_caf = Some(wz_caf.calculate_caf());
        self
    }

    /// Set driver population
    pub fn with_driver_population(mut self, population: DriverPopulation) -> Self {
        self.driver_population = population;
        self
    }

    /// Calculate CAF for this scenario
    pub fn calculate_caf(&self, ffs: f64, directional_lanes: u32, is_urban: bool) -> f64 {
        let mut caf_builder = CapacityAdjustmentFactors::new()
            .with_weather(self.weather, ffs)
            .with_driver_population(self.driver_population);

        if let Some(incident) = self.incident {
            caf_builder = caf_builder.with_incident(incident, directional_lanes);
        }

        if let Some(wz_caf) = self.work_zone_caf {
            caf_builder = caf_builder.with_work_zone_caf_value(wz_caf);
        }

        // Note: is_urban is available for future extensions
        let _ = is_urban;

        caf_builder.combined_caf()
    }

    /// Calculate SAF for this scenario
    pub fn calculate_saf(&self, ffs: f64) -> f64 {
        SpeedAdjustmentFactors::new()
            .with_weather(self.weather, ffs)
            .with_driver_population(self.driver_population)
            .combined_saf()
    }

    /// Calculate DAF for this scenario
    pub fn calculate_daf(&self, is_urban: bool) -> f64 {
        if is_urban {
            get_urban_demand_ratio(self.month, self.day)
        } else {
            get_rural_demand_ratio(self.month, self.day)
        }
    }
}

// =============================================================================
// Travel Time Index Calculations (Planning Level - Equations 11-1 to 11-5)
// =============================================================================

/// Calculate recurring delay rate (Equation 11-2)
///
/// # Arguments
/// * `ffs` - Free-flow speed (mi/h)
/// * `peak_hour_speed` - Peak hour speed (mi/h)
///
/// # Returns
/// Recurring delay rate in hours per mile
pub fn calculate_recurring_delay_rate(ffs: f64, peak_hour_speed: f64) -> f64 {
    if peak_hour_speed <= 0.0 {
        return 0.0;
    }
    (1.0 / peak_hour_speed) - (1.0 / ffs)
}

/// Calculate incident delay rate (Equation 11-3)
///
/// # Arguments
/// * `num_lanes` - Number of lanes in one direction (2 to 4)
/// * `vc_ratio` - Peak hour volume-to-capacity ratio (capped at 1.0)
///
/// # Returns
/// Incident delay rate in hours per mile
///
/// # Note
/// TODO: VERIFY COEFFICIENTS - The coefficients below are assumed values.
/// Need to verify from HCM Chapter 11 or Chapter 25 for exact Equation 11-3 formula.
/// See docs/verification_notes.md for details.
pub fn calculate_incident_delay_rate(num_lanes: u32, vc_ratio: f64) -> f64 {
    // Cap values as specified in the methodology
    let n = num_lanes.min(4).max(2);
    let x = vc_ratio.min(1.0);

    // Equation 11-3 coefficients depend on number of lanes
    // IDR = a * X^b where a and b depend on N
    // TODO: VERIFY - These coefficients are assumed, not from manual
    let (a, b) = match n {
        2 => (0.000584, 6.32),
        3 => (0.000382, 5.42),
        4 => (0.000260, 4.56),
        _ => (0.000382, 5.42), // Default to 3 lanes
    };

    a * x.powf(b)
}

/// Calculate mean travel time index (Equation 11-1)
///
/// # Arguments
/// * `ffs` - Free-flow speed (mi/h)
/// * `recurring_delay_rate` - From Equation 11-2
/// * `incident_delay_rate` - From Equation 11-3
///
/// # Returns
/// Mean annual travel time index (unitless, >= 1.0)
pub fn calculate_mean_tti(ffs: f64, recurring_delay_rate: f64, incident_delay_rate: f64) -> f64 {
    1.0 + ffs * (recurring_delay_rate + incident_delay_rate)
}

/// Calculate 95th percentile travel time index (Equation 11-4)
///
/// # Arguments
/// * `tti_mean` - Mean travel time index from Equation 11-1
///
/// # Returns
/// 95th percentile TTI (unitless)
pub fn calculate_tti_95(tti_mean: f64) -> f64 {
    1.0 + 3.67 * (tti_mean - 1.0)
}

/// Calculate percent of trips below 45 mi/h (Equation 11-5)
///
/// # Arguments
/// * `tti_mean` - Mean travel time index
///
/// # Returns
/// Percentage of trips at speeds < 45 mi/h (0.0 to 1.0)
pub fn calculate_pt_45(tti_mean: f64) -> f64 {
    let pt = (tti_mean - 1.0) / 0.0135;
    pt.max(0.0).min(1.0)
}

// =============================================================================
// Incident Frequency Calculations (Chapter 25 - Equations 25-77 to 25-79)
// =============================================================================

/// Default incident-to-crash ratio (national default from Chapter 25)
pub const DEFAULT_INCIDENT_CRASH_RATIO: f64 = 4.9;

/// Calculate expected incident frequency - Equation 25-77
///
/// # Arguments
/// * `incident_rate` - Incident rate per 100 million VMT (IR_j)
/// * `vmt` - Vehicle miles traveled for scenarios in month j
///
/// # Returns
/// Expected frequency of all incidents in the study period (rounded to nearest integer)
pub fn calculate_incident_frequency(incident_rate: f64, vmt: f64) -> f64 {
    (incident_rate * vmt / 100_000_000.0).round()
}

/// Estimate incident rate from crash rate - Equation 25-78
///
/// # Arguments
/// * `crash_rate` - Crash rate per 100 million VMT (CR_j)
/// * `incident_crash_ratio` - Incident-to-crash ratio (default 4.9)
///
/// # Returns
/// Incident rate per 100 million VMT
pub fn estimate_incident_rate(crash_rate: f64, incident_crash_ratio: f64) -> f64 {
    crash_rate * incident_crash_ratio
}

/// Estimate crash rate using HERS model - Equation 25-79
///
/// # Arguments
/// * `aadt` - Annual average daily traffic (veh/day)
/// * `two_way_hourly_capacity` - Two-way hourly capacity (veh/h)
/// * `lane_width` - Lane width in feet
///
/// # Returns
/// Crash rate per 100 million VMT
pub fn estimate_crash_rate_hers(aadt: f64, two_way_hourly_capacity: f64, lane_width: f64) -> f64 {
    if two_way_hourly_capacity <= 0.0 {
        return 0.0;
    }

    let acr = aadt / two_way_hourly_capacity;

    // CR = (154.0 - 1.203*ACR + 0.258*ACR^2 - 0.00000524*ACR^5) * e^(0.0082*(12-LW))
    let base = 154.0 - 1.203 * acr + 0.258 * acr.powi(2) - 0.00000524 * acr.powi(5);
    let lane_width_factor = (0.0082 * (12.0 - lane_width)).exp();

    (base * lane_width_factor).max(0.0)
}

/// Default incident frequency by month (relative to January)
/// From Exhibit 25-103 (example values - may vary by location)
pub fn get_default_incident_frequency_factor(month: Month) -> f64 {
    match month {
        Month::January => 0.65,
        Month::February => 0.67,
        Month::March => 0.72,
        Month::April => 0.77,
        Month::May => 0.77,
        Month::June => 0.80,
        Month::July => 0.89,
        Month::August => 0.82,
        Month::September => 0.83,
        Month::October => 0.83,
        Month::November => 0.79,
        Month::December => 0.77,
    }
}

// =============================================================================
// Planning-Level Reliability Results
// =============================================================================

/// Planning-level reliability analysis results
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlanningReliabilityResult {
    /// Mean annual travel time index
    pub tti_mean: f64,
    /// 95th percentile travel time index (Planning Time Index)
    pub tti_95: f64,
    /// Percent of trips below 45 mi/h
    pub pt_45: f64,
    /// Recurring delay rate (h/mi)
    pub recurring_delay_rate: f64,
    /// Incident delay rate (h/mi)
    pub incident_delay_rate: f64,
}

/// Perform planning-level reliability analysis
///
/// # Arguments
/// * `ffs` - Free-flow speed (mi/h)
/// * `peak_hour_speed` - Peak hour speed (mi/h)
/// * `num_lanes` - Number of directional lanes
/// * `vc_ratio` - Peak hour volume-to-capacity ratio
///
/// # Returns
/// Planning-level reliability results
pub fn planning_reliability_analysis(
    ffs: f64,
    peak_hour_speed: f64,
    num_lanes: u32,
    vc_ratio: f64,
) -> PlanningReliabilityResult {
    let rdr = calculate_recurring_delay_rate(ffs, peak_hour_speed);
    let idr = calculate_incident_delay_rate(num_lanes, vc_ratio);
    let tti_mean = calculate_mean_tti(ffs, rdr, idr);
    let tti_95 = calculate_tti_95(tti_mean);
    let pt_45 = calculate_pt_45(tti_mean);

    PlanningReliabilityResult {
        tti_mean,
        tti_95,
        pt_45,
        recurring_delay_rate: rdr,
        incident_delay_rate: idr,
    }
}

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

    #[test]
    fn test_weather_caf() {
        // Test at exact FFS values
        assert!((WeatherCondition::MediumRain.get_caf(55.0) - 0.94).abs() < 0.001);
        assert!((WeatherCondition::MediumRain.get_caf(75.0) - 0.90).abs() < 0.001);
        assert!((WeatherCondition::HeavySnow.get_caf(65.0) - 0.76).abs() < 0.001);
        assert!((WeatherCondition::NonSevereWeather.get_caf(70.0) - 1.00).abs() < 0.001);
    }

    #[test]
    fn test_weather_saf() {
        assert!((WeatherCondition::MediumRain.get_saf(55.0) - 0.96).abs() < 0.001);
        assert!((WeatherCondition::LightSnow.get_saf(75.0) - 0.84).abs() < 0.001);
        assert!((WeatherCondition::NonSevereWeather.get_saf(65.0) - 1.00).abs() < 0.001);
    }

    #[test]
    fn test_ffs_lookup_method() {
        // Test interpolation (default) - FFS 67.5 is between 65 and 70
        // MediumRain CAF: 65->0.92, 70->0.91, so 67.5 should be ~0.915
        let caf_interp = WeatherCondition::MediumRain.get_caf_with_method(67.5, FfsLookupMethod::Interpolate);
        assert!((caf_interp - 0.915).abs() < 0.001);

        // Test exact bin - FFS 67.5 rounds to 70 mi/h bin (since 67.5 <= 67.5)
        // Actually 67.5 is the boundary, should go to 65 bin
        let caf_exact = WeatherCondition::MediumRain.get_caf_with_method(67.5, FfsLookupMethod::ExactBin);
        assert!((caf_exact - 0.92).abs() < 0.001); // 65 mi/h bin value

        // Test exact bin at 68 mi/h - should round to 70 mi/h bin
        let caf_exact_68 = WeatherCondition::MediumRain.get_caf_with_method(68.0, FfsLookupMethod::ExactBin);
        assert!((caf_exact_68 - 0.91).abs() < 0.001); // 70 mi/h bin value

        // Default method should be interpolation
        let caf_default = WeatherCondition::MediumRain.get_caf(67.5);
        assert!((caf_default - caf_interp).abs() < 0.0001);
    }

    #[test]
    fn test_incident_caf() {
        // From Exhibit 11-23
        assert_eq!(IncidentSeverity::ShoulderClosed.get_caf(2), Some(0.81));
        assert_eq!(IncidentSeverity::OneLaneClosed.get_caf(3), Some(0.74));
        assert_eq!(IncidentSeverity::TwoLanesClosed.get_caf(6), Some(0.75));
        assert_eq!(IncidentSeverity::TwoLanesClosed.get_caf(2), None); // N/A
    }

    #[test]
    fn test_incident_distribution() {
        assert!((IncidentSeverity::ShoulderClosed.get_distribution_pct() - 75.4).abs() < 0.1);
        assert!((IncidentSeverity::OneLaneClosed.get_distribution_pct() - 19.6).abs() < 0.1);
    }

    #[test]
    fn test_urban_demand_ratio() {
        // From Exhibit 11-18: Default Urban Freeway Demand Ratios
        // Monday in January should be 1.00
        assert!((get_urban_demand_ratio(Month::January, DayOfWeek::Monday) - 1.00).abs() < 0.01);
        // Tuesday in January should be 1.00
        assert!((get_urban_demand_ratio(Month::January, DayOfWeek::Tuesday) - 1.00).abs() < 0.01);
        // Sunday in January should be 0.89
        assert!((get_urban_demand_ratio(Month::January, DayOfWeek::Sunday) - 0.89).abs() < 0.01);
        // Friday in July should be 1.62
        assert!((get_urban_demand_ratio(Month::July, DayOfWeek::Friday) - 1.62).abs() < 0.01);
    }

    #[test]
    fn test_rural_demand_ratio() {
        // From Exhibit 11-19: Default Rural Freeway Demand Ratios
        // Monday in January should be 1.00
        assert!((get_rural_demand_ratio(Month::January, DayOfWeek::Monday) - 1.00).abs() < 0.01);
        // Tuesday in January should be 0.96
        assert!((get_rural_demand_ratio(Month::January, DayOfWeek::Tuesday) - 0.96).abs() < 0.01);
        // Friday in July should be 2.03 (highest)
        assert!((get_rural_demand_ratio(Month::July, DayOfWeek::Friday) - 2.03).abs() < 0.01);
    }

    #[test]
    fn test_driver_population() {
        // From Exhibit 26-9
        assert!((DriverPopulation::AllFamiliar.get_caf() - 1.000).abs() < 0.001);
        assert!((DriverPopulation::AllFamiliar.get_saf() - 1.000).abs() < 0.001);

        assert!((DriverPopulation::MostlyFamiliar.get_caf() - 0.968).abs() < 0.001);
        assert!((DriverPopulation::MostlyFamiliar.get_saf() - 0.975).abs() < 0.001);

        assert!((DriverPopulation::BalancedMix.get_caf() - 0.939).abs() < 0.001);
        assert!((DriverPopulation::BalancedMix.get_saf() - 0.950).abs() < 0.001);

        assert!((DriverPopulation::MostlyUnfamiliar.get_caf() - 0.898).abs() < 0.001);
        assert!((DriverPopulation::MostlyUnfamiliar.get_saf() - 0.913).abs() < 0.001);

        assert!((DriverPopulation::AllUnfamiliar.get_caf() - 0.852).abs() < 0.001);
        assert!((DriverPopulation::AllUnfamiliar.get_saf() - 0.863).abs() < 0.001);
    }

    #[test]
    fn test_work_zone_caf() {
        // CAF_WZ = c_WZ / c
        let wz_caf = WorkZoneCAF::new(1700.0, 2400.0);
        assert!((wz_caf.calculate_caf() - 0.708).abs() < 0.01);

        // Full capacity should give 1.0
        let wz_caf_full = WorkZoneCAF::new(2400.0, 2400.0);
        assert!((wz_caf_full.calculate_caf() - 1.0).abs() < 0.001);
    }

    #[test]
    fn test_combined_caf() {
        let caf = CapacityAdjustmentFactors::new()
            .with_weather(WeatherCondition::MediumRain, 65.0)
            .with_incident(IncidentSeverity::ShoulderClosed, 4);

        // 0.92 * 0.85 = 0.782
        assert!((caf.combined_caf() - 0.782).abs() < 0.01);
    }

    #[test]
    fn test_planning_reliability() {
        let result = planning_reliability_analysis(70.0, 55.0, 3, 0.9);

        // TTI mean should be > 1.0
        assert!(result.tti_mean > 1.0);
        // TTI 95 should be > TTI mean
        assert!(result.tti_95 > result.tti_mean);
        // PT 45 should be between 0 and 1
        assert!(result.pt_45 >= 0.0 && result.pt_45 <= 1.0);
    }

    #[test]
    fn test_scenario_calculations() {
        let scenario = ReliabilityScenario::new(DayOfWeek::Friday, Month::July)
            .with_weather(WeatherCondition::MediumRain)
            .with_incident(IncidentSeverity::OneLaneClosed);

        let caf = scenario.calculate_caf(70.0, 4, true);
        let saf = scenario.calculate_saf(70.0);
        let daf = scenario.calculate_daf(true);

        // All factors should be reasonable
        assert!(caf > 0.0 && caf <= 1.0);
        assert!(saf > 0.0 && saf <= 1.0);
        assert!(daf > 0.0);
    }

    #[test]
    fn test_incident_frequency_equations() {
        // Equation 25-77: n_j = IR_j × VMT_j / 100,000,000
        let n = calculate_incident_frequency(150.0, 50_000_000.0);
        assert!((n - 75.0).abs() < 1.0); // 150 * 50M / 100M = 75

        // Equation 25-78: IR_j = CR_j × ICR
        let ir = estimate_incident_rate(30.0, DEFAULT_INCIDENT_CRASH_RATIO);
        assert!((ir - 147.0).abs() < 1.0); // 30 * 4.9 = 147

        // Equation 25-79: HERS crash rate model
        // Test with typical values: AADT=50000, capacity=4000, lane width=12
        let cr = estimate_crash_rate_hers(50000.0, 4000.0, 12.0);
        assert!(cr > 0.0 && cr < 500.0); // Should be positive and reasonable
    }

    #[test]
    fn test_incident_frequency_by_month() {
        // From Exhibit 25-103
        assert!((get_default_incident_frequency_factor(Month::January) - 0.65).abs() < 0.01);
        assert!((get_default_incident_frequency_factor(Month::July) - 0.89).abs() < 0.01);
        assert!((get_default_incident_frequency_factor(Month::December) - 0.77).abs() < 0.01);
    }
}
