use std::{fs::File, os::unix::fs::MetadataExt as _, sync::mpsc};

use chrono::{DateTime, Local};
use egui::Id;
use eyre::{Context, ContextCompat};
use rand::{Rng, rng};

pub fn random_id() -> Id {
    Id::new(rng().random::<u64>())
}

/// An [mpsc::Sender] where the receiver is the GUI.
#[derive(Clone)]
pub struct GuiSender<T> {
    tx: mpsc::Sender<T>,
    ctx: egui::Context,
}

impl<T> GuiSender<T> {
    pub fn new(tx: mpsc::Sender<T>, ctx: &egui::Context) -> Self {
        Self {
            tx,
            ctx: ctx.clone(),
        }
    }

    pub fn send(&self, t: T) -> Result<(), mpsc::SendError<T>> {
        self.tx.send(t)?;
        self.ctx.request_repaint();
        Ok(())
    }
}

#[track_caller]
pub fn log_error<T>(chain: eyre::Report, f: impl FnOnce() -> eyre::Result<T>) -> Option<T> {
    f().map_err(|e| e.wrap_err(chain))
        .inspect_err(|e| log::error!("{e}"))
        .ok()
}

pub fn file_mtime(file: &File) -> eyre::Result<DateTime<Local>> {
    (move || {
        let meta = file.metadata().wrap_err("Failed to stat file")?;

        let sec = meta.mtime();
        let nsec = meta
            .mtime_nsec()
            .try_into()
            .wrap_err("Nanoseconds overflowed")?;

        DateTime::from_timestamp(sec, nsec)
            .wrap_err("Bad timestamp")
            .map(Into::into)
    })()
    .wrap_err("Failed to get file mtime")
}
