use crate::server::aliases::MurRes;
use http::StatusCode;
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
use serde::Serialize;
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct MurResponseBuilder {
	status: StatusCode,
	headers: HashMap<String, String>,
}

impl MurResponseBuilder {
	pub fn new() -> Self {
		Self {
			status: StatusCode::OK,
			headers: HashMap::new(),
		}
	}

	pub fn status(mut self, status: StatusCode) -> Self {
		self.status = status;
		self
	}

	pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
		self.headers.insert(name.into(), value.into());
		self
	}

	pub fn headers(mut self, headers: impl IntoIterator<Item = (String, String)>) -> Self {
		self.headers.extend(headers);
		self
	}

	pub fn content_type(self, content_type: impl Into<String>) -> Self {
		self.header("Content-Type", content_type)
	}

	pub fn json<T: Serialize>(self, body: T) -> MurRes {
		let json_body = serde_json::to_string(&body)
			.unwrap_or_else(|e| format!(r#"{{"error":"Serialization failed: {}"}}"#, e));
		let mut builder = Response::builder().status(self.status);

		for (name, value) in &self.headers {
			builder = builder.header(name.as_str(), value.as_str());
		}

		if !self.headers.contains_key("Content-Type") {
			builder = builder.header("Content-Type", "application/json");
		}

		Ok(builder.body(Full::new(Bytes::from(json_body))).unwrap())
	}

	pub fn text(self, body: impl Into<String>) -> MurRes {
		let text_body = body.into();
		let mut builder = Response::builder().status(self.status);

		for (name, value) in &self.headers {
			builder = builder.header(name.as_str(), value.as_str());
		}

		if !self.headers.contains_key("Content-Type") {
			builder = builder.header("Content-Type", "text/plain; charset=utf-8");
		}

		Ok(builder.body(Full::new(Bytes::from(text_body))).unwrap())
	}

	pub fn html(self, body: impl Into<String>) -> MurRes {
		let html_body = body.into();
		let mut builder = Response::builder().status(self.status);

		for (name, value) in &self.headers {
			builder = builder.header(name.as_str(), value.as_str());
		}

		if !self.headers.contains_key("Content-Type") {
			builder = builder.header("Content-Type", "text/html; charset=utf-8");
		}

		Ok(builder.body(Full::new(Bytes::from(html_body))).unwrap())
	}

	pub fn bytes(self, body: impl Into<Bytes>) -> MurRes {
		let mut builder = Response::builder().status(self.status);

		for (name, value) in &self.headers {
			builder = builder.header(name.as_str(), value.as_str());
		}

		if !self.headers.contains_key("Content-Type") {
			builder = builder.header("Content-Type", "application/octet-stream");
		}

		Ok(builder.body(Full::new(body.into())).unwrap())
	}

	pub fn empty(self) -> MurRes {
		let mut builder = Response::builder().status(self.status);

		for (name, value) in &self.headers {
			builder = builder.header(name.as_str(), value.as_str());
		}

		Ok(builder.body(Full::new(Bytes::new())).unwrap())
	}

	pub fn redirect(self, location: impl Into<String>) -> MurRes {
		self.header("Location", location).empty()
	}
}

impl Default for MurResponseBuilder {
	fn default() -> Self {
		Self::new()
	}
}
