//! Models for interacting with the Defined Networking API.

use base64_serde::base64_serde_type;
use serde::{Serialize, Deserialize};

/// The version 1 `DNClient` API endpoint
pub const ENDPOINT_V1: &str = "/v1/dnclient";

/// The `CheckForUpdate` message type
pub const CHECK_FOR_UPDATE: &str = "CheckForUpdate";
/// The `DoUpdate` message type
pub const DO_UPDATE: &str = "DoUpdate";

base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);

#[derive(Serialize, Deserialize)]
/// `RequestV1` is the version 1 `DNClient` request message.
pub struct RequestV1 {
    /// Version is always 1
    pub version: i32,
    #[serde(rename = "hostID")]
    /// The Host ID of this dnclient instance
    pub host_id: String,
    /// The counter last returned by the server
    pub counter: u32,
    /// A base64-encoded message. This must be previously base64-encoded, as the signature is signed over the base64-encoded data.
    pub message: String,
    #[serde(with = "Base64Standard")]
    /// An ed25519 signature over the `message`, which can be verified with the host's previously enrolled ed25519 public key
    pub signature: Vec<u8>
}

#[derive(Serialize, Deserialize)]
/// `RequestWrapper` wraps a `DNClient` request message. It consists of a
/// type and value, with the type string indicating how to interpret the value blob.
pub struct RequestWrapper {
    #[serde(rename = "type")]
    /// The type of the message. Used to determine how `value` is encoded
    pub message_type: String,
    #[serde(with = "Base64Standard")]
    /// A base64-encoded arbitrary message, the type of which is stated in `message_type`
    pub value: Vec<u8>,
    /// The timestamp of when this message was sent. Follows the format `%Y-%m-%dT%H:%M:%S.%f%:z`, or:
    /// <4-digit year>-<two-digit-month>-<two-digit-day>T<two-digit-hour, 24-hour>:<two-digit-minute>:<two-digit-second>.<nanoseconds, zero-padded><offset with semicolon>
    /// For example:
    /// `2023-03-29T09:56:42.380006369-04:00`
    /// would represent `29 March 03, 2023, 09:56:42.380006369 UTC-4`
    pub timestamp: String
}

#[derive(Serialize, Deserialize)]
/// `SignedResponseWrapper` contains a response message and a signature to validate inside `data`.
pub struct SignedResponseWrapper {
    /// The response data contained in this message
    pub data: SignedResponse
}

#[derive(Serialize, Deserialize)]
/// `SignedResponse` contains a response message and a signature to validate.
pub struct SignedResponse {
    /// The API version - always 1
    pub version: i32,
    #[serde(with = "Base64Standard")]
    /// The Base64-encoded message signed inside this message
    pub message: Vec<u8>,
    #[serde(with = "Base64Standard")]
    /// The ed25519 signature over the `message`
    pub signature: Vec<u8>
}

#[derive(Serialize, Deserialize)]
/// `CheckForUpdateResponseWrapper` contains a response to `CheckForUpdate` inside "data."
pub struct CheckForUpdateResponseWrapper {
    /// The response data contained in this message
    pub data: CheckForUpdateResponse
}

#[derive(Serialize, Deserialize)]
/// `CheckForUpdateResponse` is the response generated for a `CheckForUpdate` request.
pub struct CheckForUpdateResponse {
    #[serde(rename = "updateAvailable")]
    /// Set to true if a config update is available
    pub update_available: bool
}

#[derive(Serialize, Deserialize)]
/// `DoUpdateRequest` is the request sent for a `DoUpdate` request.
pub struct DoUpdateRequest {
    #[serde(rename = "edPubkeyPEM")]
    #[serde(with = "Base64Standard")]
    /// The new ed25519 public key that should be used for future API requests
    pub ed_pubkey_pem: Vec<u8>,
    #[serde(rename = "dhPubkeyPEM")]
    #[serde(with = "Base64Standard")]
    /// The new ECDH public key that the Nebula certificate should be signed for
    pub dh_pubkey_pem: Vec<u8>,
    #[serde(with = "Base64Standard")]
    /// A randomized value used to uniquely identify this request.
    /// The original client uses a randomized, 16-byte value here, which dnapi-rs replicates
    pub nonce: Vec<u8>
}

#[derive(Serialize, Deserialize)]
/// A server response to a `DoUpdateRequest`, with the updated config and key information
pub struct DoUpdateResponse {
    #[serde(with = "Base64Standard")]
    /// The base64-encoded Nebula config. It does **NOT** have a private-key, which must be inserted explicitly before Nebula can be ran
    pub config: Vec<u8>,
    /// The new config counter. It is unknown what the purpose of this is, but the original client keeps track of it and it is used later in the api
    pub counter: u32,
    #[serde(with = "Base64Standard")]
    /// The same base64-encoded nonce that was sent in the `DoUpdateRequest`.
    pub nonce: Vec<u8>,
    #[serde(rename = "trustedKeys")]
    #[serde(with = "Base64Standard")]
    /// A new set of trusted ed25519 keys that can be used by the server to sign messages.
    pub trusted_keys: Vec<u8>
}

/// The REST enrollment endpoint
pub const ENROLL_ENDPOINT: &str = "/v2/enroll";

#[derive(Serialize, Deserialize)]
/// `EnrollRequest` is issued to the `ENROLL_ENDPOINT` to enroll this `dnclient` with a dnapi organization
pub struct EnrollRequest {
    /// The enrollment code given by the API server.
    pub code: String,
    #[serde(rename = "dhPubkey")]
    #[serde(with = "Base64Standard")]
    /// The ECDH public-key that should be used to sign the Nebula certificate given to this node.
    pub dh_pubkey: Vec<u8>,
    #[serde(rename = "edPubkey")]
    #[serde(with = "Base64Standard")]
    /// The Ed25519 public-key that this node will use to sign messages sent to the API.
    pub ed_pubkey: Vec<u8>,
    /// The timestamp of when this request was sent. Follows the format `%Y-%m-%dT%H:%M:%S.%f%:z`, or:
    /// <4-digit year>-<two-digit-month>-<two-digit-day>T<two-digit-hour, 24-hour>:<two-digit-minute>:<two-digit-second>.<nanoseconds, zero-padded><offset with semicolon>
    /// For example:
    /// `2023-03-29T09:56:42.380006369-04:00`
    /// would represent `29 March 03, 2023, 09:56:42.380006369 UTC-4`
    pub timestamp: String
}


#[derive(Serialize, Deserialize)]
#[serde(untagged)]
/// The response to an `EnrollRequest`
pub enum EnrollResponse {
    /// A successful enrollment, with a `data` field pointing to an `EnrollResponseData`
    Success {
        /// The response data from this response
        data: EnrollResponseData
    },
    /// An unsuccessful enrollment, with an `errors` field pointing to an array of `APIError`s.
    Error {
        /// A list of `APIError`s that happened while trying to enroll. `APIErrors` is a type alias to `Vec<APIError>`
        errors: APIErrors
    }
}

#[derive(Serialize, Deserialize)]
/// The data included in an successful enrollment.
pub struct EnrollResponseData {
    #[serde(with = "Base64Standard")]
    /// The base64-encoded Nebula config. It does **NOT** have a private-key, which must be inserted explicitly before Nebula can be ran
    pub config: Vec<u8>,
    #[serde(rename = "hostID")]
    /// The server-side Host ID that this node now has.
    pub host_id: String,
    /// The new config counter. It is unknown what the purpose of this is, but the original client keeps track of it and it is used later in the api
    pub counter: u32,
    #[serde(rename = "trustedKeys")]
    #[serde(with = "Base64Standard")]
    /// A new set of trusted ed25519 keys that can be used by the server to sign messages.
    pub trusted_keys: Vec<u8>,
    /// The organization data that this node is now a part of
    pub organization: EnrollResponseDataOrg
}

#[derive(Serialize, Deserialize)]
/// The organization data that this node is now a part of
pub struct EnrollResponseDataOrg {
    /// The organization ID that this node is now a part of
    pub id: String,
    /// The name of the organization that this node is now a part of
    pub name: String
}

#[derive(Serialize, Deserialize)]
/// `APIError` represents a single error returned in an API error response.
pub struct APIError {
    /// The error code
    pub code: String,
    /// The human-readable error message
    pub message: String,
    /// An optional path to where the error occured
    pub path: Option<String>
}

/// A type alias to a array of `APIErrors`. Just for parity with dnapi.
pub type APIErrors = Vec<APIError>;