/*
  This file is part of TALER
  Copyright (C) 2025 Taler Systems SA

  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.

  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 Affero General Public License for more details.

  You should have received a copy of the GNU Affero General
  Public License along with TALER; see the file COPYING.  If not,
  see <http://www.gnu.org/licenses/>
*/
/**
 * @file taler-exchange-httpd_blinding-prepare.c
 * @brief Handle /blinding-prepare requests
 * @author Özgür Kesim
 */
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include <jansson.h>
#include "taler_json_lib.h"
#include "taler_mhd_lib.h"
#include "taler-exchange-httpd_blinding-prepare.h"
#include "taler-exchange-httpd_responses.h"
#include "taler-exchange-httpd_keys.h"

MHD_RESULT
TEH_handler_blinding_prepare (struct TEH_RequestContext *rc,
                              const json_t *root,
                              const char *const args[])
{
  struct TALER_BlindingMasterSeedP blinding_seed;
  const char *cipher;
  const char *operation;
  const json_t *j_nks;
  size_t num;
  bool is_melt;
  struct GNUNET_JSON_Specification spec[] = {
    GNUNET_JSON_spec_string ("cipher",
                             &cipher),
    GNUNET_JSON_spec_string ("operation",
                             &operation),
    GNUNET_JSON_spec_fixed_auto ("seed",
                                 &blinding_seed),
    GNUNET_JSON_spec_array_const ("nks",
                                  &j_nks),
    GNUNET_JSON_spec_end ()
  };

  (void) args;
  {
    enum GNUNET_GenericReturnValue res;

    res = TALER_MHD_parse_json_data (rc->connection,
                                     root,
                                     spec);
    if (GNUNET_OK != res)
      return (GNUNET_SYSERR == res)
        ? MHD_NO
        : MHD_YES;
  }
  if (0 == strcmp (operation,
                   "melt"))
  {
    is_melt = true;
  }
  else if (0 == strcmp (operation,
                        "withdraw"))
  {
    is_melt = false;
  }
  else
  {
    GNUNET_break_op (0);
    return TALER_MHD_reply_with_error (rc->connection,
                                       MHD_HTTP_BAD_REQUEST,
                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
                                       "operation");
  }

  num = json_array_size (j_nks);
  if ((0 == num) ||
      (TALER_MAX_COINS < num))
  {
    GNUNET_break_op (0);
    return TALER_MHD_reply_with_error (rc->connection,
                                       MHD_HTTP_BAD_REQUEST,
                                       TALER_EC_EXCHANGE_GENERIC_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
                                       "nks");
  }
  if (0 != strcmp (cipher, "CS"))
  {
    GNUNET_break_op (0);
    return TALER_MHD_reply_with_error (rc->connection,
                                       MHD_HTTP_BAD_REQUEST,
                                       TALER_EC_GENERIC_PARAMETER_MALFORMED,
                                       " cipher");
  }

  {
    uint32_t cs_indices[num];
    struct  TALER_DenominationHashP h_denom_pubs[num];
    struct GNUNET_CRYPTO_CsSessionNonce nonces[num];

    for (size_t i = 0; i < num; i++)
    {
      enum GNUNET_GenericReturnValue res;
      struct GNUNET_JSON_Specification nks_spec[] = {
        GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
                                     &h_denom_pubs[i]),
        GNUNET_JSON_spec_uint32 ("coin_offset",
                                 &cs_indices[i]),
        GNUNET_JSON_spec_end ()
      };

      res = TALER_MHD_parse_json_array (rc->connection,
                                        j_nks,
                                        nks_spec,
                                        i,
                                        -1);
      if (GNUNET_OK != res)
        return (GNUNET_SYSERR == res)
          ? MHD_NO
          : MHD_YES;

      if (TALER_MAX_COINS < cs_indices[i])
      {
        GNUNET_break_op (0);
        return TALER_MHD_reply_with_error (rc->connection,
                                           MHD_HTTP_BAD_REQUEST,
                                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
                                           "nks");
      }
    }

    TALER_cs_derive_nonces_from_seed (&blinding_seed,
                                      is_melt,
                                      num,
                                      cs_indices,
                                      nonces);
    {
      struct TEH_KeyStateHandle *ksh;
      struct GNUNET_CRYPTO_CSPublicRPairP r_pubs[num];
      size_t err_idx;
      enum TALER_ErrorCode ec;

      ksh = TEH_keys_get_state ();
      if (NULL == ksh)
        return TALER_MHD_reply_with_error (rc->connection,
                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
                                           TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
                                           NULL);

      ec =
        TEH_keys_denomination_cs_batch_r_pub (ksh,
                                              num,
                                              h_denom_pubs,
                                              nonces,
                                              is_melt,
                                              r_pubs,
                                              &err_idx);
      switch (ec)
      {
      case TALER_EC_NONE:
        break;
      case TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN:
        return TEH_RESPONSE_reply_unknown_denom_pub_hash (
          rc->connection,
          &h_denom_pubs[err_idx]);
        break;
      case  TALER_EC_EXCHANGE_GENERIC_INVALID_DENOMINATION_CIPHER_FOR_OPERATION:
        return TEH_RESPONSE_reply_invalid_denom_cipher_for_operation (
          rc->connection,
          &h_denom_pubs[err_idx]);
        break;
      case TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED:
        return TEH_RESPONSE_reply_expired_denom_pub_hash (
          rc->connection,
          &h_denom_pubs[err_idx],
          ec,
          "blinding-prepare");
        break;
      case TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE:
        return TEH_RESPONSE_reply_expired_denom_pub_hash (
          rc->connection,
          &h_denom_pubs[err_idx],
          ec,
          "blinding-prepare");
        break;
      default:
        GNUNET_break (0);
        return TALER_MHD_reply_with_ec (rc->connection,
                                        ec,
                                        NULL);
        break;
      }

      /* Finally, create the response */
      {
        struct TALER_BlindingPrepareResponse response = {
          .num = num,
          .cipher = GNUNET_CRYPTO_BSA_CS,
          .details.cs = r_pubs,
        };

        return TALER_MHD_REPLY_JSON_PACK (
          rc->connection,
          MHD_HTTP_OK,
          TALER_JSON_pack_blinding_prepare_response (NULL,
                                                     &response));
      }
    }
  }
}


/* end of taler-exchange-httpd_blinding_prepare.c */
