//! Time series types
//!
//! This module defines time series data structures that combine metric identity
//! (labels) with actual data points (samples).

use crate::{Label, Sample};
use serde::{Deserialize, Serialize};

/// SeriesId is a unique identifier for a time series.
///
/// This is typically the fingerprint hash of the metric's label set.
/// Using a numeric ID instead of the full label set improves performance
/// for lookups and storage.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
pub struct SeriesId(pub u64);

impl SeriesId {
    /// Create a new SeriesId
    pub fn new(id: u64) -> Self {
        Self(id)
    }

    /// Get the numeric ID value
    pub fn as_u64(&self) -> u64 {
        self.0
    }
}

impl From<u64> for SeriesId {
    fn from(id: u64) -> Self {
        Self(id)
    }
}

impl From<SeriesId> for u64 {
    fn from(id: SeriesId) -> Self {
        id.0
    }
}

/// TimeSeries represents a complete time series with identity and data.
///
/// A time series consists of:
/// - Unique identifier (SeriesId)
/// - Label set (metric identity)
/// - Data points (samples ordered by timestamp)
///
/// # Memory Layout
///
/// This struct is designed for efficient serialization and storage.
/// For large time series, consider splitting samples into separate chunks
/// to avoid loading unnecessary data.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TimeSeries {
    /// Unique series identifier (fingerprint of labels)
    pub id: SeriesId,

    /// Label set identifying this time series
    pub labels: Vec<Label>,

    /// Data points ordered by timestamp (ascending)
    pub samples: Vec<Sample>,
}

impl TimeSeries {
    /// Create a new time series
    pub fn new(id: SeriesId, labels: Vec<Label>, samples: Vec<Sample>) -> Self {
        Self {
            id,
            labels,
            samples,
        }
    }

    /// Create a time series without samples (metadata only)
    pub fn metadata_only(id: SeriesId, labels: Vec<Label>) -> Self {
        Self {
            id,
            labels,
            samples: Vec::new(),
        }
    }

    /// Get the metric name (value of __name__ label)
    pub fn name(&self) -> Option<&str> {
        self.labels
            .iter()
            .find(|l| l.name == "__name__")
            .map(|l| l.value.as_str())
    }

    /// Get a label value by name
    pub fn get_label(&self, name: &str) -> Option<&str> {
        self.labels
            .iter()
            .find(|l| l.name == name)
            .map(|l| l.value.as_str())
    }

    /// Get the number of samples in this time series
    pub fn len(&self) -> usize {
        self.samples.len()
    }

    /// Check if this time series has no samples
    pub fn is_empty(&self) -> bool {
        self.samples.is_empty()
    }

    /// Get the timestamp range covered by this time series
    ///
    /// Returns (min_timestamp, max_timestamp) or None if no samples exist.
    pub fn time_range(&self) -> Option<(i64, i64)> {
        if self.samples.is_empty() {
            return None;
        }

        let min = self.samples.first().unwrap().timestamp;
        let max = self.samples.last().unwrap().timestamp;
        Some((min, max))
    }

    /// Add a sample to this time series
    ///
    /// # Note
    ///
    /// Samples must be added in timestamp order for correct behavior.
    /// This method does not enforce ordering - the caller is responsible.
    ///
    /// # TODO (S3)
    ///
    /// - Add validation to ensure samples are in order
    /// - Consider automatic sorting on insert
    pub fn push_sample(&mut self, sample: Sample) {
        self.samples.push(sample);
    }

    /// Filter samples by time range
    ///
    /// Returns a new TimeSeries with only samples in [start, end] range.
    pub fn filter_by_time(&self, start: i64, end: i64) -> Self {
        let filtered_samples: Vec<Sample> = self
            .samples
            .iter()
            .filter(|s| s.timestamp >= start && s.timestamp <= end)
            .cloned()
            .collect();

        Self {
            id: self.id,
            labels: self.labels.clone(),
            samples: filtered_samples,
        }
    }

    /// Get the latest sample (highest timestamp)
    pub fn latest_sample(&self) -> Option<&Sample> {
        self.samples.last()
    }

    /// Get the oldest sample (lowest timestamp)
    pub fn oldest_sample(&self) -> Option<&Sample> {
        self.samples.first()
    }
}

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

    #[test]
    fn test_series_id_creation() {
        let id = SeriesId::new(12345);
        assert_eq!(id.as_u64(), 12345);
    }

    #[test]
    fn test_series_id_conversion() {
        let id: SeriesId = 67890u64.into();
        let val: u64 = id.into();
        assert_eq!(val, 67890);
    }

    #[test]
    fn test_time_series_creation() {
        let labels = vec![
            Label::new("__name__", "test_metric"),
            Label::new("job", "test"),
        ];
        let samples = vec![
            Sample::new(1000, 1.0),
            Sample::new(2000, 2.0),
        ];

        let series = TimeSeries::new(SeriesId::new(1), labels, samples);

        assert_eq!(series.id.as_u64(), 1);
        assert_eq!(series.name(), Some("test_metric"));
        assert_eq!(series.len(), 2);
        assert!(!series.is_empty());
    }

    #[test]
    fn test_metadata_only() {
        let labels = vec![Label::new("__name__", "test_metric")];
        let series = TimeSeries::metadata_only(SeriesId::new(1), labels);

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

    #[test]
    fn test_time_range() {
        let samples = vec![
            Sample::new(1000, 1.0),
            Sample::new(2000, 2.0),
            Sample::new(3000, 3.0),
        ];

        let series = TimeSeries::new(
            SeriesId::new(1),
            vec![Label::new("__name__", "test")],
            samples,
        );

        assert_eq!(series.time_range(), Some((1000, 3000)));
    }

    #[test]
    fn test_filter_by_time() {
        let samples = vec![
            Sample::new(1000, 1.0),
            Sample::new(2000, 2.0),
            Sample::new(3000, 3.0),
            Sample::new(4000, 4.0),
        ];

        let series = TimeSeries::new(
            SeriesId::new(1),
            vec![Label::new("__name__", "test")],
            samples,
        );

        let filtered = series.filter_by_time(2000, 3000);
        assert_eq!(filtered.len(), 2);
        assert_eq!(filtered.samples[0].timestamp, 2000);
        assert_eq!(filtered.samples[1].timestamp, 3000);
    }

    #[test]
    fn test_latest_sample() {
        let samples = vec![
            Sample::new(1000, 1.0),
            Sample::new(2000, 2.0),
        ];

        let series = TimeSeries::new(
            SeriesId::new(1),
            vec![Label::new("__name__", "test")],
            samples,
        );

        let latest = series.latest_sample().unwrap();
        assert_eq!(latest.timestamp, 2000);
        assert_eq!(latest.value, 2.0);
    }

    #[test]
    fn test_push_sample() {
        let mut series = TimeSeries::metadata_only(
            SeriesId::new(1),
            vec![Label::new("__name__", "test")],
        );

        assert_eq!(series.len(), 0);

        series.push_sample(Sample::new(1000, 1.0));
        assert_eq!(series.len(), 1);

        series.push_sample(Sample::new(2000, 2.0));
        assert_eq!(series.len(), 2);
    }
}
