/*
 This file is part of GNU Taler
 (C) 2024 Taler Systems S.A.

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU Affero General Public License as published by the Free Software
 Foundation; either version 3, or (at your option) any later version.

 GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

 You should have received a copy of the GNU Affero General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>

 SPDX-License-Identifier: AGPL-3.0-or-later
 */

import { codecForAmountString } from "./amounts.js";
import {
  Codec,
  buildCodecForObject,
  buildCodecForUnion,
  codecForNumber,
  codecOptional,
} from "./codec.js";
import {
  codecForString,
  codecForConstString,
} from "./index.js";
import {
  codecForDuration,
  codecForTimestamp,
} from "./time.js";
import {
  AmountString,
  codecForEddsaPublicKey,
  EddsaPublicKeyString,
  EddsaSignatureString,
  HashCodeString,
  Integer,
  RelativeTime,
  Timestamp,
} from "./types-taler-common.js";

export const codecForTalerMailboxConfigResponse =
  (): Codec<TalerMailboxConfigResponse> =>
    buildCodecForObject<TalerMailboxConfigResponse>()
      .property("version", codecForString())
      .property("name", codecForConstString("taler-mailbox"))
      .property("monthly_fee", codecForAmountString())
      .property("registration_update_fee", codecForAmountString())
      .property("message_body_bytes", codecForNumber())
      .property("message_response_limit", codecForNumber())
      .property("delivery_period", codecForDuration)
      .build("TalerMailboxApi.VersionResponse");


export interface TalerMailboxConfigResponse {
  // libtool-style representation of the Mailbox protocol version, see
  // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
  // The format is "current:revision:age".
  version: string;

  // Name of the protocol.
  name: "taler-mailbox";

  // Fixed size of message body.
  message_body_bytes: Integer;

  // Maximum number of messages in a
  // single response.
  message_response_limit: Integer;

  // How long will the service store a message
  // before giving up on delivery?
  delivery_period: RelativeTime;

  // How much is the cost of a single
  // registration (update) of a mailbox
  // May be 0 for a free update/registration.
  registration_update_fee: AmountString;

  // How much is the cost of a single
  // registration period (30 days) of a mailbox
  // May be 0 for a free registration.
  monthly_fee: AmountString;
}

export type MailboxRegisterResult =
  | MailboxRegisterOk
  | MailboxRegisterPaymentRequired;

export interface MailboxRegisterOk {
  status: "ok";
}

export interface MailboxRegisterPaymentRequired {
  status: "payment-required";
  talerUri?: string;
}

export const codecForMailboxRegisterOk = (): Codec<MailboxRegisterOk> =>
  buildCodecForObject<MailboxRegisterOk>()
    .property("status", codecForConstString("ok"))
    .build("MailboxRegisterOk");

export const codecForMailboxRegisterPaymentRequired =
  (): Codec<MailboxRegisterPaymentRequired> =>
    buildCodecForObject<MailboxRegisterPaymentRequired>()
      .property("status", codecForConstString("payment-required"))
      .property("talerUri", codecOptional(codecForString()))
      .build("MailboxRegisterPaymentRequired");

export const codecForMailboxRegisterResult =
  (): Codec<MailboxRegisterResult> =>
    buildCodecForUnion<MailboxRegisterResult>()
      .discriminateOn("status")
      .alternative("ok", codecForMailboxRegisterOk())
      .alternative(
        "payment-required",
        codecForMailboxRegisterPaymentRequired(),
      )
      .build("MailboxRegisterResult");


export const codecForTalerMailboxRegisterRequest =
  (): Codec<MailboxRegisterRequest> =>
buildCodecForObject<MailboxRegisterRequest>()
      .property("mailbox_metadata", codecForTalerMailboxMetadata())
      .property("signature", codecForString())
      .build("TalerMailboxApi.MailboxRegisterRequest");


export interface MailboxRegisterRequest {

  // Mailbox metadata.
  mailbox_metadata: MailboxMetadata;

  // Signature by the mailbox's signing key affirming
  // the update of keys, of purpuse
  // TALER_SIGNATURE_WALLET_MAILBOX_UPDATE_KEYS.
  // The signature is created over the SHA-512 hash
  // of (encryptionKeyType||encryptionKey||expiration)
  signature: EddsaSignatureString;

}

export const codecForTalerMailboxMetadata =
  (): Codec<MailboxMetadata> =>
buildCodecForObject<MailboxMetadata>()
      .property("signingKey", codecForEddsaPublicKey())
      .property("signingKeyType", codecForConstString("EdDSA"))
      .property("encryptionKey", codecForString())
      .property("encryptionKeyType", codecForConstString("X25519"))
      .property("expiration", codecForTimestamp)
      .build("TalerMailboxApi.MailboxMessageKeys");


export interface MailboxMetadata {

  // The mailbox signing key.
  // Note that $H_MAILBOX == H(singingKey).
  // Note also how this key cannot be updated
  // as it identifies the mailbox.
  signingKey: EddsaPublicKeyString;

  // Type of key.
  // Optional, as currently only
  // EdDSA keys are supported.
  signingKeyType?: string;

  // The mailbox encryption key.
  // This is an HPKE public key
  // in the X25519 format for use
  // in a X25519-DHKEM (RFC 9180).
  // Base32 crockford-encoded.
  encryptionKey: string;

  // Type of key.
  // Optional, as currently only
  // X25519 keys are supported.
  encryptionKeyType?: string;

  // Expiration of this mapping.
  expiration: Timestamp;
}


export const codecForTalerMailboxRateLimitedResponse =
  (): Codec<MailboxRateLimitedResponse> =>
    buildCodecForObject<MailboxRateLimitedResponse>()
      .property("code", codecForNumber())
      .property("retry_delay", codecForDuration)
      .property("hint", codecForString())
      .build("TalerMailboxApi.MailboxRateLimitedResponse");

export interface MailboxRateLimitedResponse {

  // Taler error code, TALER_EC_MAILBOX_DELIVERY_RATE_LIMITED.
  code: number;

  // When the client should retry.
  retry_delay: RelativeTime;

  // The human readable error message.
  hint: string;

}
