Documentation
use std::path;

use serde::{
	Serialize,
	Deserialize,
};

use crate::event::DirectoryType;
use crate::control::ControlSocket;
use crate::control::argument::KeyArg;


pub const DEFAULT_CFG_FILE_NAME: &str = "./cnsprcy.tml";
pub const DEFAULT_PORT: u16 = 3030;

/*
 *
 *	CONFIG FILE
 *
 */

pub fn default_cfg_file_path() -> Option<path::PathBuf> {
	Some(dirs::config_dir()?
			.join("cnsprcy")
			.join("cnsprcy.tml"))
}

pub fn default_db_path() -> Option<path::PathBuf> {
	Some(dirs::data_dir()?
			.join("cnsprcy")
			.join("cnsprcy.db"))
}

#[tracing::instrument]
pub fn get_config<P: AsRef<path::Path> + std::fmt::Debug>(path: Option<P>)
	-> ParseResult
{
	match path {
		Some(path) => try_parse_config_file(path),
		None => match default_cfg_file_path() {
			Some(path) => match try_parse_config_file(path) {
				ParseResult::NotFound =>
					try_parse_config_file(DEFAULT_CFG_FILE_NAME),
				result => result
			},
			None => ParseResult::Err(
				"Could not determine config file path!".to_string()
			)
		}
	}
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct Config {
	pub cnsprcy: GeneralConfig,
	pub server: ServerConfig,
	pub handlers: Option<Vec<HandlerDirectory>>
}

pub enum ParseResult {
	Ok(Config),
	NotFound,
	Err(String)
}

impl Config {
	pub fn to_pretty_string(&self) -> String {
		toml::to_string_pretty(self).unwrap_or_else(|e| {
			panic!("Serializing config into TOML failed: {e}");
		})
	}
	pub fn db_path(&self) -> path::PathBuf {
		match self.cnsprcy.database.as_ref() {
			Some(path) => path.clone(),
			None => default_db_path()
				.expect("could not determine XDG data base directory")
		}
	}
}
impl Default for Config {
	fn default() -> Self {
		Self {
			cnsprcy: GeneralConfig {
				database: default_db_path(),
				control_socket_path: ControlSocket::default_path(),
				key: KeyArg::random()
			},
			server: ServerConfig {
				interfaces: None,
				addrs: Some(vec![])
			},
			handlers: Some(vec![ HandlerDirectory {
				r#type: None, //DirectoryType::V1,
				path: dirs::data_dir()
					.or_else(|| std::env::current_dir().ok())
					.unwrap_or_else(|| ".".into())
					.join("cnsprcy")
					.join("handlers")
			}])
		}
	}
}

#[tracing::instrument]
pub fn try_parse_config_file<P>(path: P) -> ParseResult
	where P: AsRef<path::Path> + std::fmt::Debug
{
	if !path.as_ref().exists() {
		return ParseResult::NotFound;
	}
	match std::fs::read_to_string(path) {
		Ok(toml_str) => match toml::from_str::<Config>(&toml_str) {
			Ok(cfg) => ParseResult::Ok(cfg),
			Err(e) => ParseResult::Err(
				format!("Could not parse config file: {e}!")
			)
		},
		Err(e) => ParseResult::Err(
			format!("Could not read config file: {e}!")
		)
	}
}


/*
 *
 *	GENERAL CONFIG
 *
 */

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct GeneralConfig {
	/// Path for the control socket (UNIX datagram socket)
	pub control_socket_path: path::PathBuf,

	/// Path for the database
	pub database: Option<path::PathBuf>,

	/// Secret encryption key for the CNSPRCY network
	pub key: KeyArg
}

/*
 *
 *	SERVER CONFIG
 *
 */

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct ServerConfig {
	/// List of interfaces to use
	pub interfaces: Option<Vec<String>>,

	/// List of socket addresses to listen on
	pub addrs: Option<Vec<String>>
}

/*
 *
 *	HANDLER DIRECTORY LIST
 *
 */

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub struct HandlerDirectory {
	/// Handler directory type
	pub r#type: Option<DirectoryType>,

	/// Path of the handler directory
	pub path: path::PathBuf
}