// Copyright Marcus Del Favero 2025
// Licensed under the GNU AGPLv3 with an exception, see `README.md` for details
/**
 * The initiation phase begins after the QUIC connection is established, in
 * which the *client* opens a bidirectional stream and sends a *request* to the
 * *server*. The server then replies with a *response* which is either
 * successful or some kind of failure. If the request was successful, SERP
 * enters the connection phase.
 *
 * SERP's initiation phase includes protocol versioning. The purpose of this is
 * so that if a client connects to a server of a different version, the
 * connection gracefully fails. This is opposed to the client sending or
 * receiving unexpected data and failing when there's a protocol violation. This
 * is bad for many reasons. First, this might falsely succeed and produce
 * unexpected behaviour. Second, the protocol violation might only happen late
 * into the connection. In the example of the Play request, a disconnection in
 * the middle of game is much more frustrating than at the beginning of a game.
 * Third, errors the user receives from protocol version differences are much
 * more helpful than "protocol violation" or "unexpected error", as the user can
 * know they need to update/revert versions.
 *
 * Now here's the high-level schema of a SERP request:
 * 1. Magic number of the ASCII string "seRp".
 * 2. Length of the remaining request payload, as a vu30.
 * 3. The request payload, as a tightly packed array of subrequests.
 *
 * The magic number is a fixed string the server checks before processing the
 * request. If it doesn't match, the server closes the connection.
 *
 * The purpose of the magic number is to avoid wasting processing power from
 * random general-purpose bots on the internet that connect to arbitrary
 * addresses and ports. I could instead send back a SERP failure response rather
 * than closing the connection, but that exposes unnecessary information that
 * might help the bot (if its purpose is to surveil the internet and identify
 * what services are being hosted).
 *
 * The request payload consists of an array of *subrequests*, which must be of
 * at least size one. This array is tightly packed with one subrequest after the
 * other, with there being no information of the number of subrequests. The
 * request payload's length in bytes being known is enough, subrequests are read
 * until this array ends.
 *
 * For now, the client only ever sends out one subrequest. The purpose of
 * supporting multiple subrequests is that in the future the client might
 * support multiple versions of a subprotocol or multiple similar subprotocol
 * types. In that case it would be nice for the client to send out multiple
 * requests at once and have the server respond to whichever one it supports.
 *
 * Note that if this is used in the future, the first subrequest should be the
 * oldest supported version, as before v1.4.2 I didn't bother checking any
 * subrequests after the first.
 *
 * Here is the schema of an individual subrequest:
 * 1. Subprotocol type, as a vu30.
 * 2. Subprotocol version, as a vu30.
 * 3. Length of the subrequest payload, as a vu30.
 * 4. The subrequest payload, as arbitrary data.
 *
 * The subprotocol type specifies what type of request is made, such as Play and
 * Discover. The subprotocol version is a specific version of that subprotocol
 * type. The exact assignment of subprotocol types is arbitrary: a subprotocol
 * type can consist of multiple different requests (such as Publish).
 *
 * Subprotocol types are assigned starting from zero and increasing as new types
 * are added. The same is mostly true for subprotocol versions, with development
 * versions starting from 0x20000000 (so that it can be distinguished from
 * release versions in error messages, and so release versions increment
 * nicely).
 *
 * Coordination to avoid conflicts between subprotocol types and versions is
 * important, so if you're interested in forking the project and using your own
 * subprotocols, consider making an issue requesting allocation of a subprotocol
 * type.
 *
 * The payload is data specific to the subprotocol. For example for the Play
 * request, its data is a `struct` that includes the player's requested name and
 * colour. The server doesn't need to know the semantics of this data if it
 * doesn't support the subprotocol, as it knows the length and so can just skip
 * over it.
 *
 * Note that "request" is often used in place for "subrequest" when discussing
 * the data of a subrequest or the high-level behaviour of such a request. Also
 * note that "subprotocol" is often simply referred to "protocol".
 *
 * Once the server receives a request, they send a *response*. A response can
 * either be successful or one of the many pre-defined failures.
 *
 * Here's the schema of a SERP response:
 * 1. Magic number of the ASCII string "Serp".
 * 2. Status number, as a vu30.
 * 3. Data dependent on status.
 *
 * The magic number in the response exists for a similar purpose to the magic
 * number in the request, to minimise the chance of a client getting a false
 * response if they connect to a random server. Note that the magic number is
 * slightly different ("seRp" vs "Serp"). This is intentional and is to prevent
 * things from seeming valid if the client connects to a QUIC echo server which
 * sends back exactly what it receives (I don't know why this would happen but
 * it might).
 *
 * The status number indicates which of the pre-defined statuses the response
 * is. All data after that in the response depends on this status.
 *
 * Here are each of these status numbers and the data that comes after:
 *
 * 0 = Success: The response was successful.
 * 	1. Index of the replied subrequest, as a vu30.
 *
 * 1 = Understood: The request was understood by the server but couldn't be
 *     fulfilled.
 * 	1. Index of the subrequest, as a vu30.
 * 	2. Length of the error message, as a vu30.
 * 	3. Error message, as UTF-8.
 *
 * 2 = Error: A general-purpose status for errors that don't fall in the other
 *     categories.
 * 	1. Length of the error message, as a vu30.
 * 	2. Error message, as UTF-8.
 *
 * 3 = Invalid: The client violated the SERP protocol.
 * 	1. Length of the error message, as a vu30.
 * 	2. Error message, as UTF-8.
 *
 * 4 = Bad Payload: A subrequest that the client sent contained an invalid
 *     payload.
 * 	1. Index of the subrequest, as a vu30.
 * 	2. Length of the error message, as a vu30.
 * 	3. Error message, as UTF-8.
 *
 * 5 = Too Long: The server didn't want to process the request as it was too
 *     long. Includes the maximum size the server wants and an additional error
 *     message.
 * 	1. Maximum length the server supports, as a vu30. This must be less than
 * 	   the request payload length.
 * 	2. Length of the error message, as a vu30.
 * 	3. Error message, as UTF-8.
 *
 * 6 = Unsupported Protocol: The server doesn't support the subprotocol type of
 *     any subrequest.
 * 	1. Length of the error message, as a vu30.
 * 	2. Error message, as UTF-8.
 *
 * 7 = Unsupported Version: A subprotocol version of a subrequest sent by the
 *     client isn't supported.
 * 	1. Index of the subrequest, as a vu30.
 * 	2. A version the server supports, as a vu30. This supported version cannot
 * 	   be the version of that subrequest.
 * 	3. Length of the error message, as a vu30.
 * 	4. Error message, as UTF-8.
 *
 * Note that it is a protocol violation to have a subrequest index that doesn't
 * refer to any request.
 *
 * All error messages in the responses that include them shouldn't contain any
 * redundant information. These error messages are allowed to be empty.
 *
 * If the response was successful, all data that comes after it is managed by
 * the connection phase. If the response wasn't successful, the connection is
 * finished and any data that comes after from the client or the server is
 * ignored.
 *
 * Note that in the future I might find myself limited with these rigid
 * pre-defined error types. In that case there are a few options. One option is
 * to add more status numbers, which has the disadvantage that previous SERP
 * implementations won't understand the error. Another option is to find the
 * error that best fits this and send back more data after the part previous
 * clients would ignore.
 */
pub mod request;
pub mod response;
pub mod client;
pub mod server;

#[cfg(test)]
mod mock_io;

use super::{Error, vu30::{Vu30, ReadError}};

const CLIENT_MAGIC_NUMBER: &[u8] = b"seRp"; // 'R' for "request" is capitalised
const SERVER_MAGIC_NUMBER: &[u8] = b"Serp"; // 'S' for "server" is capitalised

pub(super) trait Reader {
	async fn read_exact(&mut self, data: &mut [u8]) -> Result<(), Error>;
	async fn read_vu30(&mut self) -> Result<Vu30, Error>;
	async fn read_bytes(&mut self) -> Result<Vec<u8>, (Error, ReadError)>;
	async fn read_string(&mut self) -> Result<String, Error>;
}

pub(super) trait Writer {
	async fn write(&mut self, data: &[u8]) -> Result<(), Error>;
	async fn write_and_flush(&mut self, data: &[u8]) -> Result<(), Error>;
}
