use std::path::Path;
use std::path::PathBuf;
use tokio::fs::File;
use tokio::io::AsyncWriteExt;

use crate::error::CustomError;

use super::raw::RawConfig;

/// This is the config struct used by most of the code, which is an interpreted version of the RawConfig struct which is the raw disk-representation of the config.
pub(crate) struct Config {
    raw: RawConfig,
    config_path: PathBuf,
}

impl Config {
    pub(crate) fn new<P: AsRef<Path>>(root_dir: P) -> Result<Config, CustomError> {
        fn inner(root_dir: &Path) -> Result<Config, CustomError> {
            let file_path = root_dir.join("writer.toml");
            Ok(Config {
                raw: RawConfig::default(),
                config_path: file_path,
            })
        }
        inner(root_dir.as_ref())
    }

    pub(crate) async fn load_from_file<P: Into<PathBuf>>(path: P) -> Result<Config, CustomError> {
        async fn inner(path: PathBuf) -> Result<Config, CustomError> {
            let contents = tokio::fs::read_to_string(&path).await?;
            let parsed_contents: RawConfig = toml::from_str(contents.as_str())?;
            Ok(Config {
                raw: parsed_contents,
                config_path: path,
            })
        }
        inner(path.into()).await
    }

    pub(crate) async fn write_to_disk(&self) -> Result<(), CustomError> {
        let mut config_file = File::create(&self.config_path).await?;
        config_file
            .write_all(toml::to_string(&self.raw)?.as_bytes())
            .await?;
        Ok(())
    }

    pub(crate) fn get_root_directory(&self) -> &Path {
        &self
            .config_path
            .parent()
            .expect("Config file must exist inside a directory.")
    }

    pub(crate) fn get_posts_directory(&self) -> PathBuf {
        self.get_root_directory().join("posts")
    }

    pub(crate) fn get_output_directory(&self) -> PathBuf {
        self.get_root_directory().join("output")
    }

    pub(crate) fn use_relative_paths(&self) -> bool {
        self.raw.use_relative_paths.unwrap_or(true)
    }

    pub(crate) fn get_web_root(&self) -> Option<&str> {
        self.raw.web_root.as_deref()
    }
}