#pragma once

#include <LibCore/AnonymousBuffer.h>
#include <LibHTTP/Header.h>
#include <LibRequests/CacheSizes.h>
#include <LibRequests/NetworkError.h>
#include <LibRequests/RequestTimingInfo.h>
#include <LibURL/URL.h>
#include <RequestServer/RequestType.h>
#include <AK/Error.h>
#include <AK/MemoryStream.h>
#include <AK/OwnPtr.h>
#include <AK/Platform.h>
#include <AK/Result.h>
#include <AK/Utf8View.h>
#include <LibIPC/Attachment.h>
#include <LibIPC/Connection.h>
#include <LibIPC/Decoder.h>
#include <LibIPC/Encoder.h>
#include <LibIPC/File.h>
#include <LibIPC/Message.h>
#include <LibIPC/Stub.h>

#if defined(AK_COMPILER_CLANG)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdefaulted-function-deleted"
#endif

namespace Messages::RequestClient {

enum class MessageID : i32 {
    RequestStarted = 1,
    RequestFinished = 2,
    HeadersBecameAvailable = 3,
    RetrieveHttpCookie = 4,
    WebsocketConnected = 5,
    WebsocketReceived = 6,
    WebsocketErrored = 7,
    WebsocketClosed = 8,
    WebsocketReadyStateChanged = 9,
    WebsocketSubprotocol = 10,
    WebsocketCertificateRequested = 11,
    CertificateRequested = 12,
    EstimatedCacheSize = 13,
};

class RequestStarted final : public IPC::Message {
public:
    RequestStarted(u64 request_id, IPC::File fd)
        : m_request_id(move(request_id))
        , m_fd(move(fd))
    {
    }

    RequestStarted(RequestStarted const&) = default;
    RequestStarted(RequestStarted&&) = default;
    RequestStarted& operator=(RequestStarted const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::RequestStarted; }
    static i32 static_message_id() { return (int)MessageID::RequestStarted; }
    virtual StringView message_name() const override { return "RequestClient::RequestStarted"sv; }

    static ErrorOr<NonnullOwnPtr<RequestStarted>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto request_id = TRY((decoder.decode<u64>()));
        auto fd = TRY((decoder.decode<IPC::File>()));
        return make<RequestStarted>(move(request_id), move(fd));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 request_id, IPC::File const& fd)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::RequestStarted));
        TRY(stream.encode(request_id));
        TRY(stream.encode(fd));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_request_id, m_fd);
    }

    u64 request_id() const { return m_request_id; }

    IPC::File const& fd() const { return m_fd; }
    IPC::File take_fd() { return move(m_fd); }

private:
    u64 m_request_id;
    IPC::File m_fd;
};

class RequestFinished final : public IPC::Message {
public:
    RequestFinished(u64 request_id, u64 total_size, Requests::RequestTimingInfo timing_info, Optional<Requests::NetworkError> network_error)
        : m_request_id(move(request_id))
        , m_total_size(move(total_size))
        , m_timing_info(move(timing_info))
        , m_network_error(move(network_error))
    {
    }

    RequestFinished(RequestFinished const&) = default;
    RequestFinished(RequestFinished&&) = default;
    RequestFinished& operator=(RequestFinished const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::RequestFinished; }
    static i32 static_message_id() { return (int)MessageID::RequestFinished; }
    virtual StringView message_name() const override { return "RequestClient::RequestFinished"sv; }

    static ErrorOr<NonnullOwnPtr<RequestFinished>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto request_id = TRY((decoder.decode<u64>()));
        auto total_size = TRY((decoder.decode<u64>()));
        auto timing_info = TRY((decoder.decode<Requests::RequestTimingInfo>()));
        auto network_error = TRY((decoder.decode<Optional<Requests::NetworkError>>()));
        return make<RequestFinished>(move(request_id), move(total_size), move(timing_info), move(network_error));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 request_id, u64 total_size, Requests::RequestTimingInfo const& timing_info, Optional<Requests::NetworkError> const& network_error)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::RequestFinished));
        TRY(stream.encode(request_id));
        TRY(stream.encode(total_size));
        TRY(stream.encode(timing_info));
        TRY(stream.encode(network_error));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_request_id, m_total_size, m_timing_info, m_network_error);
    }

    u64 request_id() const { return m_request_id; }

    u64 total_size() const { return m_total_size; }

    Requests::RequestTimingInfo const& timing_info() const { return m_timing_info; }
    Requests::RequestTimingInfo take_timing_info() { return move(m_timing_info); }

    Optional<Requests::NetworkError> const& network_error() const { return m_network_error; }
    Optional<Requests::NetworkError> take_network_error() { return move(m_network_error); }

private:
    u64 m_request_id;
    u64 m_total_size;
    Requests::RequestTimingInfo m_timing_info;
    Optional<Requests::NetworkError> m_network_error;
};

class HeadersBecameAvailable final : public IPC::Message {
public:
    HeadersBecameAvailable(u64 request_id, Vector<HTTP::Header> response_headers, Optional<u32> status_code, Optional<String> reason_phrase, Optional<Core::AnonymousBuffer> javascript_bytecode, Optional<u64> javascript_bytecode_cache_vary_key)
        : m_request_id(move(request_id))
        , m_response_headers(move(response_headers))
        , m_status_code(move(status_code))
        , m_reason_phrase(move(reason_phrase))
        , m_javascript_bytecode(move(javascript_bytecode))
        , m_javascript_bytecode_cache_vary_key(move(javascript_bytecode_cache_vary_key))
    {
    }

    HeadersBecameAvailable(HeadersBecameAvailable const&) = default;
    HeadersBecameAvailable(HeadersBecameAvailable&&) = default;
    HeadersBecameAvailable& operator=(HeadersBecameAvailable const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::HeadersBecameAvailable; }
    static i32 static_message_id() { return (int)MessageID::HeadersBecameAvailable; }
    virtual StringView message_name() const override { return "RequestClient::HeadersBecameAvailable"sv; }

    static ErrorOr<NonnullOwnPtr<HeadersBecameAvailable>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto request_id = TRY((decoder.decode<u64>()));
        auto response_headers = TRY((decoder.decode<Vector<HTTP::Header>>()));
        auto status_code = TRY((decoder.decode<Optional<u32>>()));
        auto reason_phrase = TRY((decoder.decode<Optional<String>>()));
        auto javascript_bytecode = TRY((decoder.decode<Optional<Core::AnonymousBuffer>>()));
        auto javascript_bytecode_cache_vary_key = TRY((decoder.decode<Optional<u64>>()));
        return make<HeadersBecameAvailable>(move(request_id), move(response_headers), move(status_code), move(reason_phrase), move(javascript_bytecode), move(javascript_bytecode_cache_vary_key));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 request_id, ReadonlySpan<HTTP::Header> response_headers, Optional<u32> const& status_code, Optional<String> const& reason_phrase, Optional<Core::AnonymousBuffer> const& javascript_bytecode, Optional<u64> const& javascript_bytecode_cache_vary_key)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::HeadersBecameAvailable));
        TRY(stream.encode(request_id));
        TRY(stream.encode(response_headers));
        TRY(stream.encode(status_code));
        TRY(stream.encode(reason_phrase));
        TRY(stream.encode(javascript_bytecode));
        TRY(stream.encode(javascript_bytecode_cache_vary_key));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_request_id, m_response_headers, m_status_code, m_reason_phrase, m_javascript_bytecode, m_javascript_bytecode_cache_vary_key);
    }

    u64 request_id() const { return m_request_id; }

    Vector<HTTP::Header> const& response_headers() const { return m_response_headers; }
    Vector<HTTP::Header> take_response_headers() { return move(m_response_headers); }

    Optional<u32> const& status_code() const { return m_status_code; }
    Optional<u32> take_status_code() { return move(m_status_code); }

    Optional<String> const& reason_phrase() const { return m_reason_phrase; }
    Optional<String> take_reason_phrase() { return move(m_reason_phrase); }

    Optional<Core::AnonymousBuffer> const& javascript_bytecode() const { return m_javascript_bytecode; }
    Optional<Core::AnonymousBuffer> take_javascript_bytecode() { return move(m_javascript_bytecode); }

    Optional<u64> const& javascript_bytecode_cache_vary_key() const { return m_javascript_bytecode_cache_vary_key; }
    Optional<u64> take_javascript_bytecode_cache_vary_key() { return move(m_javascript_bytecode_cache_vary_key); }

private:
    u64 m_request_id;
    Vector<HTTP::Header> m_response_headers;
    Optional<u32> m_status_code;
    Optional<String> m_reason_phrase;
    Optional<Core::AnonymousBuffer> m_javascript_bytecode;
    Optional<u64> m_javascript_bytecode_cache_vary_key;
};

class RetrieveHttpCookie final : public IPC::Message {
public:
    RetrieveHttpCookie(int client_id, u64 request_id, ::RequestServer::RequestType request_type, URL::URL url)
        : m_client_id(move(client_id))
        , m_request_id(move(request_id))
        , m_request_type(move(request_type))
        , m_url(move(url))
    {
    }

    RetrieveHttpCookie(RetrieveHttpCookie const&) = default;
    RetrieveHttpCookie(RetrieveHttpCookie&&) = default;
    RetrieveHttpCookie& operator=(RetrieveHttpCookie const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::RetrieveHttpCookie; }
    static i32 static_message_id() { return (int)MessageID::RetrieveHttpCookie; }
    virtual StringView message_name() const override { return "RequestClient::RetrieveHttpCookie"sv; }

    static ErrorOr<NonnullOwnPtr<RetrieveHttpCookie>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto client_id = TRY((decoder.decode<int>()));
        auto request_id = TRY((decoder.decode<u64>()));
        auto request_type = TRY((decoder.decode<::RequestServer::RequestType>()));
        auto url = TRY((decoder.decode<URL::URL>()));
        return make<RetrieveHttpCookie>(move(client_id), move(request_id), move(request_type), move(url));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(int client_id, u64 request_id, ::RequestServer::RequestType const& request_type, URL::URL const& url)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::RetrieveHttpCookie));
        TRY(stream.encode(client_id));
        TRY(stream.encode(request_id));
        TRY(stream.encode(request_type));
        TRY(stream.encode(url));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_client_id, m_request_id, m_request_type, m_url);
    }

    int client_id() const { return m_client_id; }

    u64 request_id() const { return m_request_id; }

    ::RequestServer::RequestType const& request_type() const { return m_request_type; }
    ::RequestServer::RequestType take_request_type() { return move(m_request_type); }

    URL::URL const& url() const { return m_url; }
    URL::URL take_url() { return move(m_url); }

private:
    int m_client_id;
    u64 m_request_id;
    ::RequestServer::RequestType m_request_type;
    URL::URL m_url;
};

class WebsocketConnected final : public IPC::Message {
public:
    WebsocketConnected(u64 websocket_id)
        : m_websocket_id(move(websocket_id))
    {
    }

    WebsocketConnected(WebsocketConnected const&) = default;
    WebsocketConnected(WebsocketConnected&&) = default;
    WebsocketConnected& operator=(WebsocketConnected const&) = default;

    template<typename WrappedReturnType>
    requires(!SameAs<WrappedReturnType, u64>)
    WebsocketConnected(WrappedReturnType&& value)
        : m_websocket_id(forward<WrappedReturnType>(value))
    {
    }

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::WebsocketConnected; }
    static i32 static_message_id() { return (int)MessageID::WebsocketConnected; }
    virtual StringView message_name() const override { return "RequestClient::WebsocketConnected"sv; }

    static ErrorOr<NonnullOwnPtr<WebsocketConnected>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto websocket_id = TRY((decoder.decode<u64>()));
        return make<WebsocketConnected>(move(websocket_id));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 websocket_id)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::WebsocketConnected));
        TRY(stream.encode(websocket_id));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_websocket_id);
    }

    u64 websocket_id() const { return m_websocket_id; }

private:
    u64 m_websocket_id;
};

class WebsocketReceived final : public IPC::Message {
public:
    WebsocketReceived(u64 websocket_id, bool is_text, ByteBuffer data)
        : m_websocket_id(move(websocket_id))
        , m_is_text(move(is_text))
        , m_data(move(data))
    {
    }

    WebsocketReceived(WebsocketReceived const&) = default;
    WebsocketReceived(WebsocketReceived&&) = default;
    WebsocketReceived& operator=(WebsocketReceived const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::WebsocketReceived; }
    static i32 static_message_id() { return (int)MessageID::WebsocketReceived; }
    virtual StringView message_name() const override { return "RequestClient::WebsocketReceived"sv; }

    static ErrorOr<NonnullOwnPtr<WebsocketReceived>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto websocket_id = TRY((decoder.decode<u64>()));
        auto is_text = TRY((decoder.decode<bool>()));
        auto data = TRY((decoder.decode<ByteBuffer>()));
        return make<WebsocketReceived>(move(websocket_id), move(is_text), move(data));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 websocket_id, bool is_text, ReadonlyBytes data)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::WebsocketReceived));
        TRY(stream.encode(websocket_id));
        TRY(stream.encode(is_text));
        TRY(stream.encode(data));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_websocket_id, m_is_text, m_data);
    }

    u64 websocket_id() const { return m_websocket_id; }

    bool is_text() const { return m_is_text; }

    ByteBuffer const& data() const { return m_data; }
    ByteBuffer take_data() { return move(m_data); }

private:
    u64 m_websocket_id;
    bool m_is_text;
    ByteBuffer m_data;
};

class WebsocketErrored final : public IPC::Message {
public:
    WebsocketErrored(u64 websocket_id, i32 message)
        : m_websocket_id(move(websocket_id))
        , m_message(move(message))
    {
    }

    WebsocketErrored(WebsocketErrored const&) = default;
    WebsocketErrored(WebsocketErrored&&) = default;
    WebsocketErrored& operator=(WebsocketErrored const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::WebsocketErrored; }
    static i32 static_message_id() { return (int)MessageID::WebsocketErrored; }
    virtual StringView message_name() const override { return "RequestClient::WebsocketErrored"sv; }

    static ErrorOr<NonnullOwnPtr<WebsocketErrored>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto websocket_id = TRY((decoder.decode<u64>()));
        auto message = TRY((decoder.decode<i32>()));
        return make<WebsocketErrored>(move(websocket_id), move(message));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 websocket_id, i32 message)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::WebsocketErrored));
        TRY(stream.encode(websocket_id));
        TRY(stream.encode(message));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_websocket_id, m_message);
    }

    u64 websocket_id() const { return m_websocket_id; }

    i32 message() const { return m_message; }

private:
    u64 m_websocket_id;
    i32 m_message;
};

class WebsocketClosed final : public IPC::Message {
public:
    WebsocketClosed(u64 websocket_id, u16 code, ByteString reason, bool clean)
        : m_websocket_id(move(websocket_id))
        , m_code(move(code))
        , m_reason(move(reason))
        , m_clean(move(clean))
    {
    }

    WebsocketClosed(WebsocketClosed const&) = default;
    WebsocketClosed(WebsocketClosed&&) = default;
    WebsocketClosed& operator=(WebsocketClosed const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::WebsocketClosed; }
    static i32 static_message_id() { return (int)MessageID::WebsocketClosed; }
    virtual StringView message_name() const override { return "RequestClient::WebsocketClosed"sv; }

    static ErrorOr<NonnullOwnPtr<WebsocketClosed>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto websocket_id = TRY((decoder.decode<u64>()));
        auto code = TRY((decoder.decode<u16>()));
        auto reason = TRY((decoder.decode<ByteString>()));
        auto clean = TRY((decoder.decode<bool>()));
        return make<WebsocketClosed>(move(websocket_id), move(code), move(reason), move(clean));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 websocket_id, u16 code, StringView reason, bool clean)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::WebsocketClosed));
        TRY(stream.encode(websocket_id));
        TRY(stream.encode(code));
        TRY(stream.encode(reason));
        TRY(stream.encode(clean));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_websocket_id, m_code, m_reason, m_clean);
    }

    u64 websocket_id() const { return m_websocket_id; }

    u16 code() const { return m_code; }

    ByteString const& reason() const { return m_reason; }
    ByteString take_reason() { return move(m_reason); }

    bool clean() const { return m_clean; }

private:
    u64 m_websocket_id;
    u16 m_code;
    ByteString m_reason;
    bool m_clean;
};

class WebsocketReadyStateChanged final : public IPC::Message {
public:
    WebsocketReadyStateChanged(u64 websocket_id, u32 ready_state)
        : m_websocket_id(move(websocket_id))
        , m_ready_state(move(ready_state))
    {
    }

    WebsocketReadyStateChanged(WebsocketReadyStateChanged const&) = default;
    WebsocketReadyStateChanged(WebsocketReadyStateChanged&&) = default;
    WebsocketReadyStateChanged& operator=(WebsocketReadyStateChanged const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::WebsocketReadyStateChanged; }
    static i32 static_message_id() { return (int)MessageID::WebsocketReadyStateChanged; }
    virtual StringView message_name() const override { return "RequestClient::WebsocketReadyStateChanged"sv; }

    static ErrorOr<NonnullOwnPtr<WebsocketReadyStateChanged>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto websocket_id = TRY((decoder.decode<u64>()));
        auto ready_state = TRY((decoder.decode<u32>()));
        return make<WebsocketReadyStateChanged>(move(websocket_id), move(ready_state));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 websocket_id, u32 ready_state)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::WebsocketReadyStateChanged));
        TRY(stream.encode(websocket_id));
        TRY(stream.encode(ready_state));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_websocket_id, m_ready_state);
    }

    u64 websocket_id() const { return m_websocket_id; }

    u32 ready_state() const { return m_ready_state; }

private:
    u64 m_websocket_id;
    u32 m_ready_state;
};

class WebsocketSubprotocol final : public IPC::Message {
public:
    WebsocketSubprotocol(u64 websocket_id, ByteString subprotocol)
        : m_websocket_id(move(websocket_id))
        , m_subprotocol(move(subprotocol))
    {
    }

    WebsocketSubprotocol(WebsocketSubprotocol const&) = default;
    WebsocketSubprotocol(WebsocketSubprotocol&&) = default;
    WebsocketSubprotocol& operator=(WebsocketSubprotocol const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::WebsocketSubprotocol; }
    static i32 static_message_id() { return (int)MessageID::WebsocketSubprotocol; }
    virtual StringView message_name() const override { return "RequestClient::WebsocketSubprotocol"sv; }

    static ErrorOr<NonnullOwnPtr<WebsocketSubprotocol>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto websocket_id = TRY((decoder.decode<u64>()));
        auto subprotocol = TRY((decoder.decode<ByteString>()));
        return make<WebsocketSubprotocol>(move(websocket_id), move(subprotocol));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 websocket_id, StringView subprotocol)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::WebsocketSubprotocol));
        TRY(stream.encode(websocket_id));
        TRY(stream.encode(subprotocol));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_websocket_id, m_subprotocol);
    }

    u64 websocket_id() const { return m_websocket_id; }

    ByteString const& subprotocol() const { return m_subprotocol; }
    ByteString take_subprotocol() { return move(m_subprotocol); }

private:
    u64 m_websocket_id;
    ByteString m_subprotocol;
};

class WebsocketCertificateRequested final : public IPC::Message {
public:
    WebsocketCertificateRequested(u64 websocket_id)
        : m_websocket_id(move(websocket_id))
    {
    }

    WebsocketCertificateRequested(WebsocketCertificateRequested const&) = default;
    WebsocketCertificateRequested(WebsocketCertificateRequested&&) = default;
    WebsocketCertificateRequested& operator=(WebsocketCertificateRequested const&) = default;

    template<typename WrappedReturnType>
    requires(!SameAs<WrappedReturnType, u64>)
    WebsocketCertificateRequested(WrappedReturnType&& value)
        : m_websocket_id(forward<WrappedReturnType>(value))
    {
    }

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::WebsocketCertificateRequested; }
    static i32 static_message_id() { return (int)MessageID::WebsocketCertificateRequested; }
    virtual StringView message_name() const override { return "RequestClient::WebsocketCertificateRequested"sv; }

    static ErrorOr<NonnullOwnPtr<WebsocketCertificateRequested>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto websocket_id = TRY((decoder.decode<u64>()));
        return make<WebsocketCertificateRequested>(move(websocket_id));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 websocket_id)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::WebsocketCertificateRequested));
        TRY(stream.encode(websocket_id));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_websocket_id);
    }

    u64 websocket_id() const { return m_websocket_id; }

private:
    u64 m_websocket_id;
};

class CertificateRequested final : public IPC::Message {
public:
    CertificateRequested(u64 request_id)
        : m_request_id(move(request_id))
    {
    }

    CertificateRequested(CertificateRequested const&) = default;
    CertificateRequested(CertificateRequested&&) = default;
    CertificateRequested& operator=(CertificateRequested const&) = default;

    template<typename WrappedReturnType>
    requires(!SameAs<WrappedReturnType, u64>)
    CertificateRequested(WrappedReturnType&& value)
        : m_request_id(forward<WrappedReturnType>(value))
    {
    }

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::CertificateRequested; }
    static i32 static_message_id() { return (int)MessageID::CertificateRequested; }
    virtual StringView message_name() const override { return "RequestClient::CertificateRequested"sv; }

    static ErrorOr<NonnullOwnPtr<CertificateRequested>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto request_id = TRY((decoder.decode<u64>()));
        return make<CertificateRequested>(move(request_id));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 request_id)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::CertificateRequested));
        TRY(stream.encode(request_id));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_request_id);
    }

    u64 request_id() const { return m_request_id; }

private:
    u64 m_request_id;
};

class EstimatedCacheSize final : public IPC::Message {
public:
    EstimatedCacheSize(u64 cache_size_estimation_id, Requests::CacheSizes sizes)
        : m_cache_size_estimation_id(move(cache_size_estimation_id))
        , m_sizes(move(sizes))
    {
    }

    EstimatedCacheSize(EstimatedCacheSize const&) = default;
    EstimatedCacheSize(EstimatedCacheSize&&) = default;
    EstimatedCacheSize& operator=(EstimatedCacheSize const&) = default;

    static constexpr u32 ENDPOINT_MAGIC = 1502652576;

    virtual u32 endpoint_magic() const override { return ENDPOINT_MAGIC; }
    virtual i32 message_id() const override { return (int)MessageID::EstimatedCacheSize; }
    static i32 static_message_id() { return (int)MessageID::EstimatedCacheSize; }
    virtual StringView message_name() const override { return "RequestClient::EstimatedCacheSize"sv; }

    static ErrorOr<NonnullOwnPtr<EstimatedCacheSize>> decode(Stream& stream, Queue<IPC::Attachment>& attachments)
    {
        IPC::Decoder decoder { stream, attachments };
        auto cache_size_estimation_id = TRY((decoder.decode<u64>()));
        auto sizes = TRY((decoder.decode<Requests::CacheSizes>()));
        return make<EstimatedCacheSize>(move(cache_size_estimation_id), move(sizes));
    }

    static ErrorOr<IPC::MessageBuffer> static_encode(u64 cache_size_estimation_id, Requests::CacheSizes const& sizes)
    {
        IPC::MessageBuffer buffer;
        IPC::Encoder stream(buffer);
        TRY(stream.encode(ENDPOINT_MAGIC));
        TRY(stream.encode((int)MessageID::EstimatedCacheSize));
        TRY(stream.encode(cache_size_estimation_id));
        TRY(stream.encode(sizes));
        return buffer;
    }

    virtual ErrorOr<IPC::MessageBuffer> encode() const override
    {
        return static_encode(m_cache_size_estimation_id, m_sizes);
    }

    u64 cache_size_estimation_id() const { return m_cache_size_estimation_id; }

    Requests::CacheSizes const& sizes() const { return m_sizes; }
    Requests::CacheSizes take_sizes() { return move(m_sizes); }

private:
    u64 m_cache_size_estimation_id;
    Requests::CacheSizes m_sizes;
};

} // namespace Messages::RequestClient

template<typename LocalEndpoint, typename PeerEndpoint>
class RequestClientProxy {
public:
    // Used to disambiguate the constructor call.
    struct Tag { };

    RequestClientProxy(IPC::Connection<LocalEndpoint, PeerEndpoint>& connection, Tag)
        : m_connection(connection)
    {
    }

    void async_request_started(u64 request_id, IPC::File const& fd)
    {
        auto message_buffer = MUST(Messages::RequestClient::RequestStarted::static_encode(request_id, move(fd)));
        (void)m_connection.post_message(message_buffer);
    }

    void async_request_finished(u64 request_id, u64 total_size, Requests::RequestTimingInfo const& timing_info, Optional<Requests::NetworkError> const& network_error)
    {
        auto message_buffer = MUST(Messages::RequestClient::RequestFinished::static_encode(request_id, total_size, move(timing_info), move(network_error)));
        (void)m_connection.post_message(message_buffer);
    }

    void async_headers_became_available(u64 request_id, ReadonlySpan<HTTP::Header> response_headers, Optional<u32> const& status_code, Optional<String> const& reason_phrase, Optional<Core::AnonymousBuffer> const& javascript_bytecode, Optional<u64> const& javascript_bytecode_cache_vary_key)
    {
        auto message_buffer = MUST(Messages::RequestClient::HeadersBecameAvailable::static_encode(request_id, response_headers, move(status_code), move(reason_phrase), move(javascript_bytecode), move(javascript_bytecode_cache_vary_key)));
        (void)m_connection.post_message(message_buffer);
    }

    void async_retrieve_http_cookie(int client_id, u64 request_id, ::RequestServer::RequestType const& request_type, URL::URL const& url)
    {
        auto message_buffer = MUST(Messages::RequestClient::RetrieveHttpCookie::static_encode(client_id, request_id, move(request_type), move(url)));
        (void)m_connection.post_message(message_buffer);
    }

    void async_websocket_connected(u64 websocket_id)
    {
        auto message_buffer = MUST(Messages::RequestClient::WebsocketConnected::static_encode(websocket_id));
        (void)m_connection.post_message(message_buffer);
    }

    void async_websocket_received(u64 websocket_id, bool is_text, ReadonlyBytes data)
    {
        auto message_buffer = MUST(Messages::RequestClient::WebsocketReceived::static_encode(websocket_id, is_text, data));
        (void)m_connection.post_message(message_buffer);
    }

    void async_websocket_errored(u64 websocket_id, i32 message)
    {
        auto message_buffer = MUST(Messages::RequestClient::WebsocketErrored::static_encode(websocket_id, message));
        (void)m_connection.post_message(message_buffer);
    }

    void async_websocket_closed(u64 websocket_id, u16 code, StringView reason, bool clean)
    {
        auto message_buffer = MUST(Messages::RequestClient::WebsocketClosed::static_encode(websocket_id, code, reason, clean));
        (void)m_connection.post_message(message_buffer);
    }

    void async_websocket_ready_state_changed(u64 websocket_id, u32 ready_state)
    {
        auto message_buffer = MUST(Messages::RequestClient::WebsocketReadyStateChanged::static_encode(websocket_id, ready_state));
        (void)m_connection.post_message(message_buffer);
    }

    void async_websocket_subprotocol(u64 websocket_id, StringView subprotocol)
    {
        auto message_buffer = MUST(Messages::RequestClient::WebsocketSubprotocol::static_encode(websocket_id, subprotocol));
        (void)m_connection.post_message(message_buffer);
    }

    void async_websocket_certificate_requested(u64 websocket_id)
    {
        auto message_buffer = MUST(Messages::RequestClient::WebsocketCertificateRequested::static_encode(websocket_id));
        (void)m_connection.post_message(message_buffer);
    }

    void async_certificate_requested(u64 request_id)
    {
        auto message_buffer = MUST(Messages::RequestClient::CertificateRequested::static_encode(request_id));
        (void)m_connection.post_message(message_buffer);
    }

    void async_estimated_cache_size(u64 cache_size_estimation_id, Requests::CacheSizes const& sizes)
    {
        auto message_buffer = MUST(Messages::RequestClient::EstimatedCacheSize::static_encode(cache_size_estimation_id, move(sizes)));
        (void)m_connection.post_message(message_buffer);
    }

private:
    IPC::Connection<LocalEndpoint, PeerEndpoint>& m_connection;
};

template<typename LocalEndpoint, typename PeerEndpoint>
class RequestClientProxy;
class RequestClientStub;

class RequestClientEndpoint {
public:
    template<typename LocalEndpoint>
    using Proxy = RequestClientProxy<LocalEndpoint, RequestClientEndpoint>;
    using Stub = RequestClientStub;

    static u32 static_magic() { return 1502652576; }

    static ErrorOr<NonnullOwnPtr<IPC::Message>> decode_message(ReadonlyBytes buffer, [[maybe_unused]] Queue<IPC::Attachment>& attachments)
    {
        FixedMemoryStream stream { buffer };
        auto message_endpoint_magic = TRY(stream.read_value<u32>());

        if (message_endpoint_magic != static_magic())
            return Error::from_string_literal("Endpoint magic number mismatch, not my message!");

        auto message_id = TRY(stream.read_value<i32>());

        switch (message_id) {
        case (int)Messages::RequestClient::MessageID::RequestStarted:
            return Messages::RequestClient::RequestStarted::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::RequestFinished:
            return Messages::RequestClient::RequestFinished::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::HeadersBecameAvailable:
            return Messages::RequestClient::HeadersBecameAvailable::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::RetrieveHttpCookie:
            return Messages::RequestClient::RetrieveHttpCookie::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::WebsocketConnected:
            return Messages::RequestClient::WebsocketConnected::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::WebsocketReceived:
            return Messages::RequestClient::WebsocketReceived::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::WebsocketErrored:
            return Messages::RequestClient::WebsocketErrored::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::WebsocketClosed:
            return Messages::RequestClient::WebsocketClosed::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::WebsocketReadyStateChanged:
            return Messages::RequestClient::WebsocketReadyStateChanged::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::WebsocketSubprotocol:
            return Messages::RequestClient::WebsocketSubprotocol::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::WebsocketCertificateRequested:
            return Messages::RequestClient::WebsocketCertificateRequested::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::CertificateRequested:
            return Messages::RequestClient::CertificateRequested::decode(stream, attachments);
        case (int)Messages::RequestClient::MessageID::EstimatedCacheSize:
            return Messages::RequestClient::EstimatedCacheSize::decode(stream, attachments);
        default:
            return Error::from_string_literal("Failed to decode RequestClient message");
        }

        VERIFY_NOT_REACHED();
    }
};

class RequestClientStub : public IPC::Stub {
public:
    RequestClientStub() { }
    virtual ~RequestClientStub() override { }

    virtual u32 magic() const override { return 1502652576; }
    virtual ByteString name() const override { return "RequestClient"; }

    virtual ErrorOr<OwnPtr<IPC::MessageBuffer>> handle(NonnullOwnPtr<IPC::Message> message) override
    {
        switch (message->message_id()) {
        case (int)Messages::RequestClient::MessageID::RequestStarted:
            return handle_request_started(*message);
        case (int)Messages::RequestClient::MessageID::RequestFinished:
            return handle_request_finished(*message);
        case (int)Messages::RequestClient::MessageID::HeadersBecameAvailable:
            return handle_headers_became_available(*message);
        case (int)Messages::RequestClient::MessageID::RetrieveHttpCookie:
            return handle_retrieve_http_cookie(*message);
        case (int)Messages::RequestClient::MessageID::WebsocketConnected:
            return handle_websocket_connected(*message);
        case (int)Messages::RequestClient::MessageID::WebsocketReceived:
            return handle_websocket_received(*message);
        case (int)Messages::RequestClient::MessageID::WebsocketErrored:
            return handle_websocket_errored(*message);
        case (int)Messages::RequestClient::MessageID::WebsocketClosed:
            return handle_websocket_closed(*message);
        case (int)Messages::RequestClient::MessageID::WebsocketReadyStateChanged:
            return handle_websocket_ready_state_changed(*message);
        case (int)Messages::RequestClient::MessageID::WebsocketSubprotocol:
            return handle_websocket_subprotocol(*message);
        case (int)Messages::RequestClient::MessageID::WebsocketCertificateRequested:
            return handle_websocket_certificate_requested(*message);
        case (int)Messages::RequestClient::MessageID::CertificateRequested:
            return handle_certificate_requested(*message);
        case (int)Messages::RequestClient::MessageID::EstimatedCacheSize:
            return handle_estimated_cache_size(*message);
        default:
            return Error::from_string_literal("Unknown message ID for RequestClient endpoint");
        }
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_request_started(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::RequestStarted&>(message);
        request_started(request.request_id(), request.take_fd());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_request_finished(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::RequestFinished&>(message);
        request_finished(request.request_id(), request.total_size(), request.take_timing_info(), request.take_network_error());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_headers_became_available(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::HeadersBecameAvailable&>(message);
        headers_became_available(request.request_id(), request.take_response_headers(), request.take_status_code(), request.take_reason_phrase(), request.take_javascript_bytecode(), request.take_javascript_bytecode_cache_vary_key());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_retrieve_http_cookie(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::RetrieveHttpCookie&>(message);
        retrieve_http_cookie(request.client_id(), request.request_id(), request.take_request_type(), request.take_url());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_websocket_connected(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::WebsocketConnected&>(message);
        websocket_connected(request.websocket_id());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_websocket_received(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::WebsocketReceived&>(message);
        websocket_received(request.websocket_id(), request.is_text(), request.take_data());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_websocket_errored(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::WebsocketErrored&>(message);
        websocket_errored(request.websocket_id(), request.message());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_websocket_closed(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::WebsocketClosed&>(message);
        websocket_closed(request.websocket_id(), request.code(), request.take_reason(), request.clean());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_websocket_ready_state_changed(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::WebsocketReadyStateChanged&>(message);
        websocket_ready_state_changed(request.websocket_id(), request.ready_state());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_websocket_subprotocol(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::WebsocketSubprotocol&>(message);
        websocket_subprotocol(request.websocket_id(), request.take_subprotocol());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_websocket_certificate_requested(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::WebsocketCertificateRequested&>(message);
        websocket_certificate_requested(request.websocket_id());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_certificate_requested(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::CertificateRequested&>(message);
        certificate_requested(request.request_id());
        return nullptr;
    }

    NEVER_INLINE ErrorOr<OwnPtr<IPC::MessageBuffer>> handle_estimated_cache_size(IPC::Message& message)
    {
        auto& request = static_cast<Messages::RequestClient::EstimatedCacheSize&>(message);
        estimated_cache_size(request.cache_size_estimation_id(), request.take_sizes());
        return nullptr;
    }

    virtual void request_started(u64 request_id, IPC::File fd) = 0;
    virtual void request_finished(u64 request_id, u64 total_size, Requests::RequestTimingInfo timing_info, Optional<Requests::NetworkError> network_error) = 0;
    virtual void headers_became_available(u64 request_id, Vector<HTTP::Header> response_headers, Optional<u32> status_code, Optional<String> reason_phrase, Optional<Core::AnonymousBuffer> javascript_bytecode, Optional<u64> javascript_bytecode_cache_vary_key) = 0;
    virtual void retrieve_http_cookie(int client_id, u64 request_id, ::RequestServer::RequestType request_type, URL::URL url) = 0;
    virtual void websocket_connected(u64 websocket_id) = 0;
    virtual void websocket_received(u64 websocket_id, bool is_text, ByteBuffer data) = 0;
    virtual void websocket_errored(u64 websocket_id, i32 message) = 0;
    virtual void websocket_closed(u64 websocket_id, u16 code, ByteString reason, bool clean) = 0;
    virtual void websocket_ready_state_changed(u64 websocket_id, u32 ready_state) = 0;
    virtual void websocket_subprotocol(u64 websocket_id, ByteString subprotocol) = 0;
    virtual void websocket_certificate_requested(u64 websocket_id) = 0;
    virtual void certificate_requested(u64 request_id) = 0;
    virtual void estimated_cache_size(u64 cache_size_estimation_id, Requests::CacheSizes sizes) = 0;
};

#if defined(AK_COMPILER_CLANG)
#pragma clang diagnostic pop
#endif
