use regex::Regex;
use thiserror::Error;

// Define reused errors and transform errors from other crates by adding an entry with the proper
// attributes below. See https://docs.rs/thiserror for info
#[derive(Error, Debug)]
pub enum Error {
	#[error("{0}")]
	CliError(String),
	#[error("Could not find rezon.toml to load configuration. Run `rezon init` to create one.")]
	ConfigNotFound,
	#[error("Problem accessing {path}:\n\tcaused by: {source} ")]
	Io { path: std::path::PathBuf, source: std::io::Error },
	#[error("Failed to deploy model on Rezon cloud. Please try again!")]
	ModelDeployFailure,
	#[error("Failed to upload model to Rezon cloud. Please try again!")]
	ModelUploadFailure,
	#[error("No deployed models found. Start by deploying a model using the `rezon deploy` command.")]
	ModelsNotFound,
	#[error("{}", format_notok(response))]
	NotOk { response: String },
	#[error("Invalid Command")]
	InvalidCommand,
	#[error("Problem validating config: cannot have {0} and {1} in rezon.toml at the same time")]
	InvalidConfig(String, String),
	// Print our context plus csv's informative error
	#[error("Problem parsing CSV file :\n\tcaused by: {source}")]
	InvalidCSV {
		#[from]
		source: csv::Error,
	},
	// Print our context plus serde_json's informative error
	#[error("Problem parsing input data:\n\tcaused by: {source}")]
	InvalidJSON {
		#[from]
		source: serde_json::Error,
	},
	// Print our context plus toml's informative error
	#[error("Problem parsing toml file input:\n\tcaused by: {source}")]
	InvalidTOML {
		#[from]
		source: toml::de::Error,
	},
	// Print our context plus reqwest's informative error
	#[error("Problem communicating with Rezon servers\n\tcaused by: {source}")]
	Request {
		#[from]
		source: reqwest::Error,
	},
	#[error("Problem communicating with Rezon servers\n\tcaused by: {source}")]
	RequestMiddleware {
		#[from]
		source: reqwest_middleware::Error,
	},
	// Print our context plus requestty's informative error
	#[error("{source}")]
	RequestTTY {
		#[from]
		source: requestty::ErrorKind,
	},
	#[error("{}", response)]
	ModelServerErrorResponse {
		response: String,
	}
}

fn format_notok(response: &String) -> String {
	// NotOk needs special logic to extract the message from the server response
	let regex = Regex::new(r#""message":\s*"(.*?)""#).unwrap();
	let message = match regex.captures(response.as_str()) {
		Some(capture) => match capture.get(1) {
			Some(mtch) => String::from(mtch.as_str()),
			None => "Unexpected Error, Try Again!".to_string(),
		},
		None => "Unexpected Error, Try Again!".to_string(),
	};

	format!("Received non-OK status from Rezon servers: {message}")
} // forward to above implementation
