// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
use super::{
	Reader, Writer, request::Request, response::{FailureResponse, RawResponse, RawFailureResponse},
	super::{Error, Connection, ConnectionHandle, QuicConnection, vu30::Vu30},
};

pub struct Client;

impl Client {
	pub async fn send(conn: QuicConnection, request: Request) -> Result<Connection, Error> {
		let handle = ConnectionHandle::new(conn);
		let mut main_stream = handle.open_stream().await?;
		Client::send_with(request, &mut main_stream.sender, &mut main_stream.receiver).await?;
		Ok(Connection::new(handle, main_stream))
	}

	async fn send_with(request: Request, writer: &mut impl Writer, reader: &mut impl Reader) -> Result<(), Error> {
		let (request_data, request_len) = request.serialise()?;
		writer.write_and_flush(&request_data).await?;

		let response = RawResponse::read(reader).await?;
		Client::process_response(request, request_len, response)
	}

	/**
	 * Converts the raw response received into the return type the caller gets.
	 */
	fn process_response(request: Request, request_len: usize, response: RawResponse) -> Result<(), Error> {
		Err(match response {
			RawResponse::Success(subrequest_index) => {
				match Client::check_subrequest_index(subrequest_index) {
					Ok(()) => return Ok(()),
					Err(err) => err,
				}
			},
			RawResponse::Failure(RawFailureResponse::Understood(subrequest_index, msg)) => {
				Client::check_subrequest_index(subrequest_index)?;
				Error::Response(FailureResponse::Understood(msg))
			},
			RawResponse::Failure(RawFailureResponse::Error(msg)) => Error::Response(FailureResponse::Error(msg)),
			RawResponse::Failure(RawFailureResponse::Invalid(msg)) => Error::Response(FailureResponse::Invalid(msg)),
			RawResponse::Failure(RawFailureResponse::BadPayload(subrequest_index, msg)) => {
				Client::check_subrequest_index(subrequest_index)?;
				Error::Response(FailureResponse::BadPayload(msg))
			},
			RawResponse::Failure(RawFailureResponse::TooLong(max_len, msg)) => {
				if (max_len.get() as usize) < request_len { Error::Response(FailureResponse::TooLong(max_len, msg)) }
				else { Error::Invalid(format!("server claims maximum length is {max_len} but returns Too Long for request of length {request_len}")) } // Server is lying
			},
			RawResponse::Failure(RawFailureResponse::UnsupportedProtocol(msg)) => Error::Response(FailureResponse::UnsupportedProtocol(msg)),
			RawResponse::Failure(RawFailureResponse::UnsupportedVersion { index, supported_version, msg }) => {
				Client::check_subrequest_index(index)?;
				if supported_version != request.subprotocol_version() { Error::Response(FailureResponse::UnsupportedVersion { supported_version, msg }) }
				else { Error::Invalid(format!("server claims it supports version {supported_version} but returns Unsupported Version")) } // Server is lying
			},
		})
	}

	fn check_subrequest_index(index: Vu30) -> Result<(), Error> {
		if index == Vu30::ZERO { Ok(()) }
		else { Err(Error::Invalid(String::from("invalid subrequest index, expected zero"))) }
	}
}
