use crate::types::*;

#[derive(Clone)]
pub struct Context(crate::context::Context);

// there are lots of action endpoints and they all follow the same pattern
macro_rules! action{
	($fname:ident,$action:expr)=>{
		pub async fn $fname(&self,config:SubmissionID)->Result<(),Error>{
			let url_raw=format!(concat!("{}/submissions/{}/status/",$action),self.0.base_url,config.0);
			let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;

			response_ok(
				self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
			).await.map_err(Error::Response)?;

			Ok(())
		}
	};
}
impl Context{
	pub fn new(base_url:String)->reqwest::Result<Self>{
		Ok(Self(crate::context::Context::new(base_url,None)?))
	}
	pub async fn get_script(&self,config:GetScriptRequest)->Result<ScriptResponse,Error>{
		let url_raw=format!("{}/scripts/{}",self.0.base_url,config.ScriptID.0);
		let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;

		response_ok(
			self.0.get(url).await.map_err(Error::Reqwest)?
		).await.map_err(Error::Response)?
		.json().await.map_err(Error::Reqwest)
	}
	pub async fn create_script<'a>(&self,config:CreateScriptRequest<'a>)->Result<ScriptIDResponse,Error>{
		let url_raw=format!("{}/scripts",self.0.base_url);
		let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;

		let body=serde_json::to_string(&config).map_err(Error::JSON)?;

		response_ok(
			self.0.post(url,body).await.map_err(Error::Reqwest)?
		).await.map_err(Error::Response)?
		.json().await.map_err(Error::Reqwest)
	}
	pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
		let url_raw=format!("{}/script-policy",self.0.base_url);
		let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;

		{
			let mut query_pairs=url.query_pairs_mut();
			query_pairs.append_pair("Page",config.Page.to_string().as_str());
			query_pairs.append_pair("Limit",config.Limit.to_string().as_str());
			if let Some(hash)=config.FromScriptHash{
				query_pairs.append_pair("FromScriptHash",hash);
			}
			if let Some(script_id)=config.ToScriptID{
				query_pairs.append_pair("ToScriptID",script_id.0.to_string().as_str());
			}
			if let Some(policy)=config.Policy{
				query_pairs.append_pair("Policy",(policy as i32).to_string().as_str());
			}
		}

		response_ok(
			self.0.get(url).await.map_err(Error::Reqwest)?
		).await.map_err(Error::Response)?
		.json().await.map_err(Error::Reqwest)
	}
	pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
		let policies=self.get_script_policies(GetScriptPoliciesRequest{
			Page:1,
			Limit:2,
			FromScriptHash:Some(config.hash),
			ToScriptID:None,
			Policy:None,
		}).await.map_err(SingleItemError::Other)?;
		if 1<policies.len(){
			return Err(SingleItemError::DuplicateItems);
		}
		Ok(policies.into_iter().next())
	}
	pub async fn create_script_policy(&self,config:CreateScriptPolicyRequest)->Result<ScriptPolicyIDResponse,Error>{
		let url_raw=format!("{}/script-policy",self.0.base_url);
		let url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;

		let body=serde_json::to_string(&config).map_err(Error::JSON)?;

		response_ok(
			self.0.post(url,body).await.map_err(Error::Reqwest)?
		).await.map_err(Error::Response)?
		.json().await.map_err(Error::Reqwest)
	}
	pub async fn update_submission_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{
		let url_raw=format!("{}/submissions/{}/model",self.0.base_url,config.SubmissionID);
		let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;

		{
			url.query_pairs_mut()
				.append_pair("ModelID",config.ModelID.to_string().as_str())
				.append_pair("ModelVersion",config.ModelVersion.to_string().as_str());
		}

		response_ok(
			self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
		).await.map_err(Error::Response)?;

		Ok(())
	}
	pub async fn action_submission_uploaded(&self,config:ActionSubmissionUploadedRequest)->Result<(),Error>{
		let url_raw=format!("{}/submissions/{}/status/validator-uploaded",self.0.base_url,config.SubmissionID);
		let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;

		if let Some(target_asset_id)=config.TargetAssetID{
			url.query_pairs_mut()
				.append_pair("TargetAssetID",target_asset_id.to_string().as_str());
		}
		response_ok(
			self.0.post_empty_body(url).await.map_err(Error::Reqwest)?
		).await.map_err(Error::Response)?;

		Ok(())
	}
	action!(action_submission_validated,"validator-validated");
	action!(action_submission_accepted,"validator-failed");
}