/*
  This file is part of TALER
  (C) 2020, 2021 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 General Public License for more details.

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

/**
 * @file taler-merchant-httpd_private-post-products-ID-lock.c
 * @brief implementing POST /products/$ID/lock request handling
 * @author Christian Grothoff
 */
#include "platform.h"
#include "taler-merchant-httpd_private-post-products-ID-lock.h"
#include "taler-merchant-httpd_helper.h"
#include <taler/taler_json_lib.h>


MHD_RESULT
TMH_private_post_products_ID_lock (
  const struct TMH_RequestHandler *rh,
  struct MHD_Connection *connection,
  struct TMH_HandlerContext *hc)
{
  struct TMH_MerchantInstance *mi = hc->instance;
  const char *product_id = hc->infix;
  enum GNUNET_DB_QueryStatus qs;
  const char *uuids;
  struct GNUNET_Uuid uuid;
  uint64_t quantity;
  bool quantity_missing;
  const char *unit_quantity = NULL;
  bool unit_quantity_missing = true;
  struct GNUNET_TIME_Relative duration;
  struct GNUNET_JSON_Specification spec[] = {
    GNUNET_JSON_spec_string ("lock_uuid",
                             &uuids),
    GNUNET_JSON_spec_relative_time ("duration",
                                    &duration),
    GNUNET_JSON_spec_mark_optional (
      GNUNET_JSON_spec_uint64 ("quantity",
                               &quantity),
      &quantity_missing),
    GNUNET_JSON_spec_mark_optional (
      GNUNET_JSON_spec_string ("unit_quantity",
                               &unit_quantity),
      &unit_quantity_missing),
    GNUNET_JSON_spec_end ()
  };

  GNUNET_assert (NULL != mi);
  GNUNET_assert (NULL != product_id);
  {
    enum GNUNET_GenericReturnValue res;

    res = TALER_MHD_parse_json_data (connection,
                                     hc->request_body,
                                     spec);
    if (GNUNET_OK != res)
      return (GNUNET_NO == res)
             ? MHD_YES
             : MHD_NO;
  }
  TMH_uuid_from_string (uuids,
                        &uuid);
  TMH_db->expire_locks (TMH_db->cls);
  {
    struct TALER_MERCHANTDB_ProductDetails pd = { 0 };
    size_t num_categories;
    uint64_t *categories;
    uint64_t normalized_quantity = 0;
    uint32_t normalized_quantity_frac = 0;

    if (quantity_missing && unit_quantity_missing)
    {
      quantity = 1;
      quantity_missing = false;
    }
    else if ( (! quantity_missing) &&
              (quantity > (uint64_t) INT64_MAX) )
    {
      GNUNET_break_op (0);
      return TALER_MHD_reply_with_error (connection,
                                         MHD_HTTP_BAD_REQUEST,
                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
                                         "quantity");
    }

    qs = TMH_db->lookup_product (TMH_db->cls,
                                 mi->settings.id,
                                 product_id,
                                 &pd,
                                 &num_categories,
                                 &categories);
    switch (qs)
    {
    case GNUNET_DB_STATUS_HARD_ERROR:
      GNUNET_break (0);
      TALER_MERCHANTDB_product_details_free (&pd);
      GNUNET_free (categories);
      return TALER_MHD_reply_with_error (
        connection,
        MHD_HTTP_INTERNAL_SERVER_ERROR,
        TALER_EC_GENERIC_DB_FETCH_FAILED,
        "lookup_product");
    case GNUNET_DB_STATUS_SOFT_ERROR:
      GNUNET_break (0);
      TALER_MERCHANTDB_product_details_free (&pd);
      GNUNET_free (categories);
      return TALER_MHD_reply_with_error (
        connection,
        MHD_HTTP_INTERNAL_SERVER_ERROR,
        TALER_EC_GENERIC_DB_SOFT_FAILURE,
        "lookup_product");
    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
      GNUNET_break_op (0);
      TALER_MERCHANTDB_product_details_free (&pd);
      GNUNET_free (categories);
      return TALER_MHD_reply_with_error (
        connection,
        MHD_HTTP_NOT_FOUND,
        TALER_EC_MERCHANT_GENERIC_PRODUCT_UNKNOWN,
        product_id);
    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
      break;
    }
    GNUNET_free (categories);
    {
      const char *eparam;
      if (GNUNET_OK !=
          TMH_process_quantity_inputs (TMH_VK_QUANTITY,
                                       pd.allow_fractional_quantity,
                                       quantity_missing,
                                       (int64_t) quantity,
                                       unit_quantity_missing,
                                       unit_quantity,
                                       &normalized_quantity,
                                       &normalized_quantity_frac,
                                       &eparam))
      {
        TALER_MERCHANTDB_product_details_free (&pd);
        GNUNET_break_op (0);
        return TALER_MHD_reply_with_error (connection,
                                           MHD_HTTP_BAD_REQUEST,
                                           TALER_EC_GENERIC_PARAMETER_MALFORMED,
                                           eparam);
      }
    }
    quantity = normalized_quantity;
    qs = TMH_db->lock_product (TMH_db->cls,
                               mi->settings.id,
                               product_id,
                               &uuid,
                               quantity,
                               normalized_quantity_frac,
                               GNUNET_TIME_relative_to_timestamp (duration));
    TALER_MERCHANTDB_product_details_free (&pd);
  }
  switch (qs)
  {
  case GNUNET_DB_STATUS_HARD_ERROR:
    GNUNET_break (0);
    return TALER_MHD_reply_with_error (
      connection,
      MHD_HTTP_INTERNAL_SERVER_ERROR,
      TALER_EC_GENERIC_DB_STORE_FAILED,
      NULL);
  case GNUNET_DB_STATUS_SOFT_ERROR:
    GNUNET_break (0);
    return TALER_MHD_reply_with_error (
      connection,
      MHD_HTTP_INTERNAL_SERVER_ERROR,
      TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
      "Serialization error for single-statment request");
  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    GNUNET_break_op (0);
    return TALER_MHD_reply_with_error (
      connection,
      MHD_HTTP_GONE,
      TALER_EC_MERCHANT_PRIVATE_POST_PRODUCTS_LOCK_INSUFFICIENT_STOCKS,
      product_id);
  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    return TALER_MHD_reply_static (connection,
                                   MHD_HTTP_NO_CONTENT,
                                   NULL,
                                   NULL,
                                   0);
  }
  GNUNET_assert (0);
  return MHD_NO;
}


/* end of taler-merchant-httpd_private-patch-products-ID-lock.c */
