#pragma once

#include "session/network/key_types.hpp"
#include "session/network/session_network_types.hpp"
#include "session/platform.hpp"

namespace session::network::config {
struct FileServer {
    std::string scheme;
    std::string host;
    uint16_t port;
    std::string pubkey_hex;

    uint64_t max_file_size;
    bool use_stream_encryption;
};
}  // namespace session::network::config

namespace session::network::file_server {

extern const config::FileServer DEFAULT_CONFIG;

extern const std::string_view ENDPOINT_FILE;
extern const std::string_view FRAGMENT_PUBKEY;
extern const std::string_view FRAGMENT_STREAM_ENCRYPTION;

struct DownloadInfo {
    std::string scheme;
    std::string host;
    std::string file_id;
    std::optional<std::string> custom_pubkey_hex;  // If 'p' fragment present
    bool wants_stream_decryption;                  // If 'd' fragment present
};

/// API: file_server/parse_download_url
///
/// Parses a url to extract the information required to download the file.
///
/// Inputs:
/// - `url` -- [in] url to parse.
///
/// Outputs:
/// - returns struct containing the information required to download the file.
std::optional<DownloadInfo> parse_download_url(std::string_view url);

/// API: file_server/generate_download_url
///
/// Generates a download url to the configured file server for a given file id.
///
/// Inputs:
/// - `file_id` -- [in] id for the file generated by uploading to the file server.
/// - `config` -- [in] file server configuration to use to generate the download url.
///
/// Outputs:
/// - returns url which can be used to download the file.
std::string generate_download_url(std::string_view file_id, const config::FileServer& config);

/// API: file_server/parse_http_date
///
/// Parses a HTTP date header.
///
/// Inputs:
/// - `date_str` -- [in] date header to parse.
///
/// Outputs:
/// - the parsed `sys_seconds`, or `std::nullopt` if it failed to parse.
std::optional<std::chrono::sys_seconds> parse_http_date(std::string_view date_str);

/// API: file_server/to_request
///
/// Accumulates all of the data for an `UploadRequest` and creates a standard `Request` for it.
///
/// Inputs:
/// - `upload_id` -- [in] id for the request.
/// - `config` -- [in] configuration for the file server that the request should be sent to.
/// - `upload_request` -- [in] `UploadRequest` to convert into a standard `Request`.
///
/// Outputs:
/// - returns a one-shot (non streaming) request which can be sent to a file server.
Request to_request(
        const std::string& upload_id,
        const config::FileServer& config,
        UploadRequest upload_request);

/// API: file_server/to_request
///
/// Accumulates all of the data for an `DownloadRequest` and creates a standard `Request` for it.
///
/// Inputs:
/// - `download_id` -- [in] id for the request.
/// - `config` -- [in] configuration for the file server that the request should be sent to.
/// - `download_request` -- [in] `DownloadRequest` to convert into a standard `Request`.
///
/// Outputs:
/// - returns a one-shot (non streaming) request which can be sent to a file server.
Request to_request(
        const std::string& download_id,
        const config::FileServer& config,
        DownloadRequest download_request);

/// API: file_server/parse_upload_response
///
/// Parses the JSON response body from a file server upload request into a `file_metadata` struct.
/// The `upload_size` parameter is used as a fallback if the response does not include a `size`
/// field.
///
/// Inputs:
/// - `body` -- [in] response body to parse.
/// - `upload_size` -- [in] fallback size to use if the response does not include one.
///
/// Outputs:
/// - returns the parsed `file_metadata`.
/// - throws `std::runtime_error` if the response is missing required fields or cannot be parsed.
file_metadata parse_upload_response(const std::string& body, size_t upload_size);

/// API: file_server/parse_download_response
///
/// Parses the headers and body from a file server download response into a `file_metadata` struct,
/// extracting the `file_id` from the original `download_url`.
///
/// Inputs:
/// - `download_url` -- [in] the original download URL, used to extract the `file_id`.
/// - `headers` -- [in] response headers.
/// - `body` -- [in] response body containing the raw file data.
///
/// Outputs:
/// - returns a pair of the parsed `file_metadata` and the raw file data.
/// - throws `invalid_url_exception` if the URL cannot be parsed.
std::pair<file_metadata, std::vector<unsigned char>> parse_download_response(
        std::string_view download_url,
        const std::vector<std::pair<std::string, std::string>>& headers,
        const std::string& body);

/// API: file_server/get_client_version
///
/// Constructs a request to retrieve the version information for the given platform.
///
/// Inputs:
/// - `platform` -- [in] the platform to retrieve the client version for.
/// - `seckey` -- [in] the users ed25519 secret key (to generated blinded auth).
/// - `request_timeout` -- [in] timeout in milliseconds to use for the request.  This won't take any
/// pre-flight operations into account so the request will never timeout if pre-flight operations
/// never complete.
/// - `overall_timeout` -- [in] timeout in milliseconds to use for the request and any pre-flight
/// operations that may need to occur (eg. path building).  This value takes presedence over
/// `request_timeout` if provided, the request itself will be given a timeout of this value
/// subtracting however long the pre-flight operations took.
Request get_client_version(
        Platform platform,
        network::ed25519_seckey seckey,
        std::chrono::milliseconds request_timeout,
        std::optional<std::chrono::milliseconds> overall_timeout = std::nullopt);

}  // namespace session::network::file_server
