use std::fs;
use std::path::PathBuf;

use serde::Serialize;
use serde::de::DeserializeOwned;

use crate::Error;

const CONFIG_FILE_NAME: &str = "config.toml";

const CONFIG_FILE_PASSWORD: &str = "nupwuz-toxvif-0timNo";
const CONFIG_FILE_NONCE: &str = "novel-rs-config";

pub(crate) fn load_config_file<T, R>(app_name: T) -> Result<Option<R>, Error>
where
    T: AsRef<str>,
    R: DeserializeOwned,
{
    let config_file_path = config_file_path(app_name)?;

    if config_file_path.try_exists()? {
        tracing::info!(
            "The config file is located at: `{}`",
            config_file_path.display()
        );

        if let Ok(config) =
            crate::decrypt_from_file(config_file_path, CONFIG_FILE_PASSWORD, CONFIG_FILE_NONCE)
        {
            if let Ok::<R, _>(config) = toml::from_str(&config) {
                Ok(Some(config))
            } else {
                tracing::error!("Fail to parse the config file, a new one will be created");
                Ok(None)
            }
        } else {
            tracing::error!("Fail to decrypt the config file, a new one will be created");
            Ok(None)
        }
    } else {
        fs::create_dir_all(config_file_path.parent().unwrap())?;

        tracing::info!(
            "The config file will be created at: `{}`",
            config_file_path.display()
        );

        Ok(None)
    }
}

pub(crate) fn save_config_file<T, E>(app_name: T, config: E) -> Result<(), Error>
where
    T: AsRef<str>,
    E: Serialize,
{
    let config_file_path = config_file_path(app_name)?;

    tracing::info!("Save the config file at: `{}`", config_file_path.display());

    crate::encrypt_and_save_to_file(
        toml::to_string(&config)?,
        config_file_path,
        CONFIG_FILE_PASSWORD,
        CONFIG_FILE_NONCE,
    )?;

    Ok(())
}

fn config_file_path<T>(app_name: T) -> Result<PathBuf, Error>
where
    T: AsRef<str>,
{
    let mut config_file_path = crate::config_dir_path(app_name.as_ref())?;
    config_file_path.push(CONFIG_FILE_NAME);

    Ok(config_file_path)
}
