use std::{
    fs::{File, OpenOptions},
    io::Write,
    path::{Path, PathBuf},
};

use time::{macros::format_description, OffsetDateTime};

#[derive(Debug)]
pub struct AdminLog {
    filename: PathBuf,
    file: File,
}

impl AdminLog {
    pub fn open(filename: &Path) -> Result<Self, LogError> {
        let file = OpenOptions::new()
            .append(true)
            .create(true)
            .open(filename)
            .map_err(|e| LogError::OpenLogFile(filename.into(), e))?;
        Ok(Self {
            filename: filename.into(),
            file,
        })
    }

    pub fn writeln(&mut self, text: &str) -> Result<(), LogError> {
        self.write("[")?;
        self.write(&now()?)?;
        self.write("] ")?;
        self.write(text)?;
        self.write("\n")?;
        Ok(())
    }

    fn write(&mut self, msg: &str) -> Result<(), LogError> {
        self.file
            .write_all(msg.as_bytes())
            .map_err(|e| LogError::WriteLogFile(self.filename.clone(), e))
    }
}

fn now() -> Result<String, LogError> {
    let fmt = format_description!("[year]-[month]-[day] [hour]:[minute]:[second]Z");
    OffsetDateTime::now_utc()
        .format(fmt)
        .map_err(LogError::TimeFormat)
}

#[derive(Debug, thiserror::Error)]
pub enum LogError {
    #[error("failed to open log file {0}")]
    OpenLogFile(PathBuf, #[source] std::io::Error),

    #[error("failed to write to log file {0}")]
    WriteLogFile(PathBuf, #[source] std::io::Error),

    #[error("failed to format time stamp")]
    TimeFormat(#[source] time::error::Format),
}
