use actix_web::body::BoxBody;
use actix_web::http::StatusCode;
use actix_web::{HttpRequest, HttpResponse, Responder};
use serde::{Deserialize, Serialize};

use crate::PrettyJson;

#[derive(Serialize, Deserialize)]
pub struct Response<T = ()> {
	pub code: u16,
	pub data: Option<T>,
	pub message: String,
	#[serde(skip)]
	#[allow(dead_code)]
	status: StatusCode,
}

impl<T: Serialize> Responder for Response<T> {
	type Body = BoxBody;

	fn respond_to(self, _req: &HttpRequest) -> HttpResponse<Self::Body> {
		HttpResponse::build(self.status).json(&self)
	}
}

impl Default for Response<()> {
	fn default() -> Self {
		Self::error(StatusCode::NOT_FOUND, "not found")
	}
}

#[allow(dead_code)]
impl<T> Response<T> {
	pub fn ok(message: impl Into<String>, data: Option<T>) -> Self {
		Self {
			code: StatusCode::OK.as_u16(),
			status: StatusCode::OK,
			data,
			message: message.into(),
		}
	}

	pub fn success(data: T) -> Self {
		Self::ok("success", Some(data))
	}

	pub fn error(status: StatusCode, message: impl Into<String>) -> Self {
		Self { code: status.as_u16(), status, data: None, message: message.into() }
	}

	pub fn not_found(message: impl Into<String>) -> Self {
		Self::error(StatusCode::NOT_FOUND, message)
	}

	pub fn bad_request(message: impl Into<String>) -> Self {
		Self::error(StatusCode::BAD_REQUEST, message)
	}

	pub fn internal_error(message: impl Into<String>) -> Self {
		Self::error(StatusCode::INTERNAL_SERVER_ERROR, message)
	}

	pub fn unauthorized(message: impl Into<String>) -> Self {
		Self::error(StatusCode::UNAUTHORIZED, message)
	}

	pub fn forbidden(message: impl Into<String>) -> Self {
		Self::error(StatusCode::FORBIDDEN, message)
	}
}

impl<T: Serialize> Response<T> {
	pub fn pretty(self) -> PrettyJson<Self> {
		let status = self.status;
		PrettyJson { inner: self, status }
	}
}
