// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
#[cfg(test)]
mod tests;

use quinn::VarInt;

use super::{
	Reader, Writer, request::{Request, RawSubrequest, SubrequestResult}, response::{FailureResponse, RawResponse, RawFailureResponse},
	super::{Error, Connection, ConnectionHandle, QuicConnection, Sender, Receiver, conn::Stream, vu30::Vu30},
};

use crate::utils::or::Or;

pub struct Server;

impl Server {
	/**
	 * Returns the received request and an object to reply with a Success or
	 * Understood response, or indicates an error.
	 *
	 * The error either consists of an error to be logged, a failure response to
	 * be sent back, or both, *but not none*.
	 */
	pub async fn recv(conn: QuicConnection) -> Result<(Request, PendingResponse), Or<Error, FailureResponse>> {
		// Right now only the main stream is created by the client
		conn.set_max_concurrent_bi_streams(VarInt::from_u32(1));
		conn.set_max_concurrent_uni_streams(VarInt::from_u32(0));

		let handle = ConnectionHandle::new(conn);
		let mut main_stream = handle.accept_stream().await.map_err(Or::A)?;

		match Server::recv_with(&mut main_stream.receiver).await {
			Ok((request, subrequest_index)) => Ok((request, PendingResponse { handle, sender: ResponseSender { sender: main_stream.sender, subrequest_index }, receiver: main_stream.receiver })),
			Err(err) => {
				let mut to_reply = None;
				let err = err.map_b(|response| {
					to_reply = Some(RawResponse::Failure(response.clone()));
					response.process()
				});

				// Replies with the response
				if let Some(Ok(data)) = to_reply.as_ref().map(RawResponse::serialise) {
					let _ = main_stream.sender.write_and_flush(&data).await;
				}

				Err(err)
			},
		}
	}

	/**
	 * Receives a request from the client.
	 */
	pub(super) async fn recv_with(reader: &mut impl Reader) -> Result<(Request, Vu30), Or<Error, RawFailureResponse>> {
		let subrequests = RawSubrequest::read_all(reader).await?;

		if subrequests.is_empty() {
			return Err(Or::B(RawFailureResponse::Invalid(String::from("empty subrequest array"))));
		}

		let mut supported_version = None;
		for (i, req) in subrequests.into_iter().enumerate() {
			let index = Vu30::try_from_usize(i).unwrap_or(Vu30::MAX);
			match req.deserialise() {
				SubrequestResult::Supported(Ok(req)) => return Ok((req, index)),
				SubrequestResult::Supported(Err(err)) => return Err(Or::Both(err, RawFailureResponse::BadPayload(index, String::new()))),
				SubrequestResult::UnsupportedVersion(version) => _ = supported_version.get_or_insert((index, version)),
				SubrequestResult::UnsupportedProtocol => (),
			}
		}

		// No supported request found
		Err(Or::B(if let Some((index, supported_version)) = supported_version {
			RawFailureResponse::UnsupportedVersion { index, supported_version, msg: String::new() }
		} else {
			RawFailureResponse::UnsupportedProtocol(String::new())
		}))
	}
}

pub struct PendingResponse {
	handle: ConnectionHandle,
	sender: ResponseSender,
	receiver: Receiver,
}

impl PendingResponse {
	/**
	 * Sends a SERP Success response.
	 */
	pub async fn success(mut self) -> Result<Connection, Error> {
		self.sender.send(self.sender.success_status()).await?;
		Ok(self.into_connection())
	}

	/**
	 * Sends a SERP Success response without flushing.
	 *
	 * This method should only be used if server quickly sends and flushes the
	 * main stream.
	 */
	pub async fn success_unflushed(mut self) -> Result<Connection, Error> {
		self.sender.send_unflushed(self.sender.success_status()).await?;
		Ok(self.into_connection())
	}

	fn into_connection(self) -> Connection {
		Connection::new(self.handle, Stream { sender: self.sender.sender, receiver: self.receiver })
	}

	/**
	 * Sends a SERP Understood response with an associated message.
	 *
	 * Unlike the other methods, this doesn't return a `Result` because I'm not
	 * interested if sending the response failed.
	 */
	pub async fn understood(self, msg: &str) {
		self.sender.understood(msg).await;
	}

	/**
	 * Converts the pending response into a "pre-connection", which is when the
	 * response hasn't been sent so it's still in the initiation phase, but it
	 * has the same interface as a connection.
	 */
	pub fn into_pre_connection(self) -> (ConnectionHandle, ResponseSender, Receiver) {
		(self.handle, self.sender, self.receiver)
	}
}

pub struct ResponseSender {
	sender: Sender,
	subrequest_index: Vu30,
}

impl ResponseSender {
	fn success_status(&self) -> RawResponse {
		RawResponse::Success(self.subrequest_index)
	}

	pub async fn success_unflushed(mut self) -> Result<Sender, Error> {
		self.send_unflushed(self.success_status()).await?;
		Ok(self.sender)
	}

	pub async fn understood(mut self, msg: &str) {
		let _ = self.send(RawResponse::Failure(RawFailureResponse::Understood(self.subrequest_index, msg.to_owned()))).await;
	}

	pub fn set_priority(&mut self, priority: i32) -> Result<(), Error> {
		self.sender.set_priority(priority)
	}

	async fn send(&mut self, response: RawResponse) -> Result<(), Error> {
		self.sender.write_and_flush(&response.serialise()?).await
	}

	async fn send_unflushed(&mut self, response: RawResponse) -> Result<(), Error> {
		self.sender.write(&response.serialise()?).await
	}
}
