use chrono::{DateTime, Duration, SecondsFormat, Utc};
use rand::rngs::SmallRng;
use rand::Rng;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct StepsRecord {
    pub startTime: String,
    pub endTime: String,
    pub count: u32,
}

impl StepsRecord {
    pub const IDENTIFIER: i32 = 1;
}

pub trait HealthRecord {
    const IDENTIFIER: i32;
    fn get_identifier(&self) -> i32;
}

pub trait Sampleable {
    fn generate_sample(rng: &mut SmallRng, start: DateTime<Utc>, duration: Duration) -> Self;
}

impl HealthRecord for StepsRecord {
    const IDENTIFIER: i32 = StepsRecord::IDENTIFIER;
    fn get_identifier(&self) -> i32 {
        Self::IDENTIFIER
    }
}

impl Sampleable for StepsRecord {
    fn generate_sample(rng: &mut SmallRng, start: DateTime<Utc>, duration: Duration) -> Self {
        Self {
            startTime: start.to_rfc3339_opts(SecondsFormat::Millis, true),
            endTime: (start + duration).to_rfc3339_opts(SecondsFormat::Millis, true),
            count: rng.random_range(100..5000),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct HeartRateRecord {
    pub startTime: String,
    pub endTime: String,
    pub bpm: u32,
}

impl HeartRateRecord {
    pub const IDENTIFIER: i32 = 2;
}

impl HealthRecord for HeartRateRecord {
    const IDENTIFIER: i32 = HeartRateRecord::IDENTIFIER;
    fn get_identifier(&self) -> i32 {
        Self::IDENTIFIER
    }
}

impl Sampleable for HeartRateRecord {
    fn generate_sample(rng: &mut SmallRng, start: DateTime<Utc>, duration: Duration) -> Self {
        Self {
            startTime: start.to_rfc3339_opts(SecondsFormat::Millis, true),
            endTime: (start + duration).to_rfc3339_opts(SecondsFormat::Millis, true),
            bpm: rng.random_range(60..180),
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
#[allow(non_snake_case)]
pub struct BloodGlucoseRecord {
    pub startTime: String,
    pub endTime: String,
    pub value: f32,
}

impl BloodGlucoseRecord {
    pub const IDENTIFIER: i32 = 3;
}

impl HealthRecord for BloodGlucoseRecord {
    const IDENTIFIER: i32 = BloodGlucoseRecord::IDENTIFIER;
    fn get_identifier(&self) -> i32 {
        Self::IDENTIFIER
    }
}

impl Sampleable for BloodGlucoseRecord {
    fn generate_sample(rng: &mut SmallRng, start: DateTime<Utc>, duration: Duration) -> Self {
        Self {
            startTime: start.to_rfc3339_opts(SecondsFormat::Millis, true),
            endTime: (start + duration).to_rfc3339_opts(SecondsFormat::Millis, true),
            value: rng.random_range(4.0..10.0),
        }
    }
}