use std::path::PathBuf;
use std::time::Duration;
use std::{ops::Index, thread};

use file_backed_value::FileBackedValue;
use gethostname::gethostname;
use indexmap::IndexMap;

use crate::probes::{get_probes_elapsed, reset_probes, Probes};

pub struct IdlePower(IndexMap<String, f32>);

impl IdlePower {
    pub fn init(path: Option<&PathBuf>, idle_seconds: usize, probes: &mut Probes) -> Self {
        let filename = gethostname()
            .to_ascii_lowercase()
            .to_str()
            .map_or(format!("idle-{idle_seconds}s.json"), |user| {
                format!("idle-{idle_seconds}s-{user}.json")
            });

        let mut file = if let Some(path) = path {
            FileBackedValue::new_at(&filename, &path)
        } else {
            FileBackedValue::new(&filename)
        };

        log::info!("Idle file stored at {:?}", file.path());
        file.set_dirty_time(Duration::from_hours(18));
        let idle = file
            .get_or_insert_with(|| measure_idle(probes, idle_seconds))
            .unwrap();

        Self(idle.clone())
    }
}

impl Index<&str> for IdlePower {
    type Output = f32;

    fn index(&self, key: &str) -> &Self::Output {
        &self.0[key]
    }
}

fn measure_idle(probes: &mut Probes, idle_seconds: usize) -> IndexMap<String, f32> {
    if idle_seconds > 0 {
        let before = idle_seconds.min(30);
        log::trace!("Waiting for system to stabilise for {}s", before);
        thread::sleep(Duration::from_secs(before as u64));
    }

    log::info!("Measuring idle for {}s", idle_seconds);

    reset_probes(probes);
    thread::sleep(Duration::from_secs(1));
    let mut min_power = get_probes_elapsed(probes);
    min_power.shift_remove("runtime");

    for _ in 1..idle_seconds {
        reset_probes(probes);
        thread::sleep(Duration::from_secs(1));
        for (k, v) in get_probes_elapsed(probes) {
            min_power[&k] = min_power[&k].min(v);
        }
    }

    log::info!("Idle power draw: {:#?}", min_power);
    min_power
}
