Documentation
use std::error::Error;
use std::fs::File;

use std::io::{Read, Write};
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
use std::{fs, io};

use serde::{Deserialize, Serialize};
use tracing::error;

pub mod command;
pub mod listener;
pub mod log;
pub mod login;
pub mod plugin_manager;

fn get_service_path() -> &'static PathBuf {
    static PATH: OnceLock<PathBuf> = OnceLock::new();
    PATH.get_or_init(|| {
        let p = PathBuf::from("service");
        let _ = fs::create_dir(&p);
        p
    })
}

pub struct Service {
    name: String,
    path: PathBuf,
}

impl Service {
    pub fn new<S: ToString>(name: S) -> Self {
        let name = name.to_string();
        let mut p = get_service_path().clone();
        let mut s = name.clone();
        p.push(&s);
        s.push_str(".toml");
        p.push(&s);
        Self { name, path: p }
    }

    pub fn with_path<P: AsRef<Path>>(&mut self, path: P) {
        let path = path.as_ref();
        if !path.is_dir() {
            fs::create_dir_all(path).unwrap();
        }
        self.path = path.join(format!("{}.toml", self.name()));
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn read_config<T>(&self) -> T
    where
        for<'de> T: Serialize + Deserialize<'de> + Default,
    {
        fn _read_config<T>(path: &Path) -> Result<T, Box<dyn Error>>
        where
            for<'de> T: Serialize + Deserialize<'de> + Default,
        {
            let exist = path.is_file();

            let mut f = fs::OpenOptions::new()
                .create(true)
                .read(true)
                .write(true)
                .open(path)?;

            let data = if exist {
                let mut s = String::new();
                f.read_to_string(&mut s)?;
                toml::from_str(&s)?
            } else {
                let dat = T::default();
                let str = toml::to_string_pretty(&dat)?;
                let _ = f.write_all(str.as_bytes());
                dat
            };

            Ok(data)
        }

        match _read_config(&self.path) {
            Ok(data) => data,
            Err(e) => {
                error!("读取配置文件({:?})时发生意料之外的错误: {}", self.path, e);

                let mut bk = self.path.clone();
                bk.pop();
                bk.push(format!("{}.toml.bak", self.name()));
                let _ = fs::copy(&self.path, bk);

                let data = T::default();
                if let Ok(mut f) = File::create(&self.path) {
                    let s = toml::to_string_pretty(&data).unwrap_or_else(|e| {
                        panic!("Cannot serialize service data: {}, {e}", self.name)
                    });
                    let _ = f.write_all(s.as_bytes());
                }

                data
            }
        }
    }

    pub fn write_config<T>(&self, data: &T)
    where
        T: Serialize,
    {
        fn _write_config<T>(path: &Path, data: &T) -> io::Result<()>
        where
            T: Serialize,
        {
            let mut f = File::create(path)?;
            let s = toml::to_string_pretty(data).expect("Cannot serialize data");

            f.write_all(s.as_bytes())?;
            Ok(())
        }

        if let Err(e) = _write_config(&self.path, data) {
            error!("写入配置文件({:?})时发生意料之外的错误: {}", self.path, e);
        }
    }
}