//! Climatological region lookup. 1:1 port of
//! `lib/microwaveprop/propagation/region.ex`.

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Region {
    GulfCoast,
    Southeast,
    SouthernPlains,
    CornBelt,
    Northeast,
    DesertSouthwest,
    PacificNorthwest,
    MountainWest,
    Other,
}

/// Lat min (inclusive), lat max (exclusive), lon min (inclusive), lon max (exclusive).
struct BBox(Region, f64, f64, f64, f64);

// Ordered to match Elixir's `Enum.find_value` short-circuit.
const REGIONS: &[BBox] = &[
    BBox(Region::GulfCoast, 25.0, 32.0, -100.0, -80.0),
    BBox(Region::Southeast, 30.0, 37.0, -90.0, -75.0),
    BBox(Region::SouthernPlains, 32.0, 38.0, -105.0, -93.0),
    BBox(Region::CornBelt, 38.0, 48.0, -100.0, -82.0),
    BBox(Region::Northeast, 38.0, 48.0, -82.0, -67.0),
    BBox(Region::DesertSouthwest, 30.0, 38.0, -120.0, -105.0),
    BBox(Region::PacificNorthwest, 42.0, 50.0, -125.0, -115.0),
    BBox(Region::MountainWest, 38.0, 48.0, -115.0, -100.0),
];

pub fn for_point(lat: f64, lon: f64) -> Region {
    for bbox in REGIONS {
        let BBox(name, lat_min, lat_max, lon_min, lon_max) = *bbox;
        if lat >= lat_min && lat < lat_max && lon >= lon_min && lon < lon_max {
            return name;
        }
    }
    Region::Other
}

/// Multiplier applied to the band's `seasonal_base` score. Unknown
/// region/month combinations return 1.0. Range [0.7, 1.3].
pub fn seasonal_adjustment(region: Region, month: u8) -> f64 {
    match (region, month) {
        (Region::GulfCoast, 6) => 0.95,
        (Region::GulfCoast, 7) => 0.95,
        (Region::GulfCoast, 8) => 1.15,
        (Region::GulfCoast, 9) => 1.10,
        (Region::CornBelt, 6) => 1.05,
        (Region::CornBelt, 7) => 0.85,
        (Region::CornBelt, 8) => 0.80,
        (Region::CornBelt, 9) => 0.95,
        (Region::Southeast, 7) => 0.97,
        (Region::Southeast, 8) => 1.08,
        (Region::SouthernPlains, 8) => 1.05,
        (Region::DesertSouthwest, 7) => 1.10,
        (Region::DesertSouthwest, 8) => 1.10,
        (Region::DesertSouthwest, 9) => 1.05,
        (Region::PacificNorthwest, 6) => 1.15,
        (Region::PacificNorthwest, 7) => 1.20,
        (Region::PacificNorthwest, 8) => 1.20,
        (Region::PacificNorthwest, 9) => 1.10,
        _ => 1.0,
    }
}

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

    #[test]
    fn gulf_coast_points() {
        // Houston ≈ (29.8, -95.4) — Gulf Coast bbox.
        assert_eq!(for_point(29.8, -95.4), Region::GulfCoast);
    }

    #[test]
    fn corn_belt_points() {
        // Des Moines ≈ (41.6, -93.6) — Corn Belt bbox.
        assert_eq!(for_point(41.6, -93.6), Region::CornBelt);
    }

    #[test]
    fn non_conus_returns_other() {
        // Alaska (61.2, -149.9) — outside every bbox.
        assert_eq!(for_point(61.2, -149.9), Region::Other);
    }

    #[test]
    fn adjustments_match_elixir_table() {
        assert_eq!(seasonal_adjustment(Region::GulfCoast, 8), 1.15);
        assert_eq!(seasonal_adjustment(Region::CornBelt, 7), 0.85);
        assert_eq!(seasonal_adjustment(Region::PacificNorthwest, 7), 1.20);
        assert_eq!(seasonal_adjustment(Region::Other, 8), 1.0);
        assert_eq!(seasonal_adjustment(Region::CornBelt, 1), 1.0);
    }

    #[test]
    fn precedence_matches_elixir_ordering() {
        // (30.5, -90.0) is inside BOTH gulf_coast (25..32, -100..-80) and
        // southeast (30..37, -90..-75). Elixir hits gulf_coast first.
        assert_eq!(for_point(30.5, -90.0), Region::GulfCoast);
    }
}
