use jiff::{Error, Unit};

use crate::temporal::AnyTemporal;

#[derive(Debug, Clone)]
pub struct TemporalExtent {
    extent: Option<(AnyTemporal, AnyTemporal)>,
}

impl TemporalExtent {
    pub fn new() -> Self {
        Self { extent: None }
    }

    pub fn clear(&mut self) {
        self.extent = None;
    }

    pub fn add(&mut self, value: AnyTemporal) -> Result<(), String> {
        match &mut self.extent {
            None => self.extent = Some((value.clone(), value)),
            Some((min, max)) => {
                if !min.has_same_type(&value) {
                    return Err(format!("temporal extent attempting to handle incompatible types: {} and {} (current min: {:?}, given value: {:?})", min.kind_as_str(), value.kind_as_str(), min, value));
                }

                if value < *min {
                    *min = value.clone();
                } else if value > *max {
                    *max = value.clone();
                }
            }
        };

        Ok(())
    }

    pub fn earliest(&self) -> Option<AnyTemporal> {
        self.extent.as_ref().map(|(z, _)| z.clone())
    }

    pub fn lastest(&self) -> Option<AnyTemporal> {
        self.extent.as_ref().map(|(_, z)| z.clone())
    }

    pub fn count(&self, unit: Unit) -> Result<Option<f64>, Error> {
        match &self.extent {
            None => Ok(None),
            Some((earliest, latest)) => earliest.relative_total(latest, unit).map(Some),
        }
    }

    pub fn merge(&mut self, other: Self) {
        match self.extent.as_mut() {
            None => {
                self.extent = other.extent;
            }
            Some((min, max)) => {
                if let Some((other_min, other_max)) = other.extent {
                    if other_min < *min {
                        *min = other_min;
                    }
                    if other_max > *max {
                        *max = other_max;
                    }
                }
            }
        }
    }
}