//! This is the module Awesome.toml

use crate::prelude::*;
use crate::utils::{XTake, XTakeVal};
use crate::VERSION;
use std::fs;
use std::path::Path;
use toml::Value;

static AWESOME_TMPL: &'static str = include_str!("../tmpl/Awesome.toml");

const AWESOME_FILE_NAME: &str = "Awesome.toml";
const SRC_TAURI_DIR: &str = "src-tauri";
const PACKAGE_JSON_FILE: &str = "package.json";

pub fn ensure_config(root_dir: &Path) -> Result<Config> {
	// --- Check that we run in the root folder
	if !root_dir.join(SRC_TAURI_DIR).is_dir() && !root_dir.join(PACKAGE_JSON_FILE).is_file() {
		return Err(Error::DirNotValid);
	}

	// --- Check
	let awesome_file = root_dir.join(AWESOME_FILE_NAME);

	if !awesome_file.is_file() {
		let mut content = String::new();
		content.push_str(&format!(
			r#"# Generated by 'awesome-app' command line.
awesome_app_version = "{VERSION}"

"#
		));
		content.push_str(AWESOME_TMPL);
		fs::write(&awesome_file, content)?;
	}

	let toml_str = fs::read_to_string(&awesome_file)?;

	match toml::from_str::<Value>(&toml_str) {
		Ok(root) => Config::try_from(root),
		Err(ex) => Err(Error::ConfigParsingError(format!("{}", ex))),
	}
}

#[derive(Debug)]
pub struct Config {
	pub cli_version: Option<String>,
	pub dev_runners: Option<Vec<Runner>>,
}

impl TryFrom<Value> for Config {
	type Error = Error;
	fn try_from(mut val: Value) -> Result<Config> {
		let cli_version = val.x_take::<String>("cli_version")?;
		let runners_raw = val.get_mut("runners").map(|runners| runners.x_take::<Vec<Value>>("dev"));

		// TODO: need to cleans this up
		let runners_raw = match runners_raw {
			Some(r) => Some(r?),
			None => None,
		}
		.flatten();

		// Option<Result<Vec<Runner>>>
		let dev_runners = runners_raw.map(|v| v.into_iter().map(|v| Runner::try_from(v)).collect::<Result<Vec<_>>>());

		// Option<Vec<Runner>>
		let dev_runners = match dev_runners {
			Some(r) => Some(r?),
			None => None,
		};

		Ok(Config { cli_version, dev_runners })
	}
}

#[derive(Debug)]
pub struct Runner {
	pub name: String,
	pub working_dir: Option<String>,
	pub cmd: String,
	pub args: Option<Vec<String>>,
	pub wait_before: u64,      // default to 0
	pub concurrent: bool,      // default to false
	pub end_all_on_exit: bool, // default to false
}

impl TryFrom<Value> for Runner {
	type Error = Error;
	fn try_from(mut val: Value) -> Result<Runner> {
		let name = val.x_take_val::<String>("name")?;
		let working_dir = val.x_take::<String>("working_dir")?;
		let cmd = val.x_take_val::<String>("cmd")?;
		let args = val.x_take::<Vec<String>>("args")?;
		let wait_before = val.x_take::<u64>("wait_before")?.unwrap_or(0);
		let concurrent = val.x_take::<bool>("concurrent")?.unwrap_or(false);
		let end_all_on_exit = val.x_take::<bool>("end_all_on_exit")?.unwrap_or(false);

		// TODO: Error when concurrent = false, and end_all_on_exit = true (would not make much sense)

		Ok(Runner {
			name,
			working_dir,
			wait_before,
			cmd,
			args,
			concurrent,
			end_all_on_exit,
		})
	}
}

// region:    --- Tests
#[cfg(test)]
#[path = "./_tests/tests_config.rs"]
mod tests;
// endregion: --- Tests
