/*
  This file is part of TALER
  (C) 2026 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_get-private-incoming-ID.c
 * @brief implement API for obtaining details about an expected incoming wire transfer
 * @author Christian Grothoff
 */
#include "taler/platform.h"
#include <jansson.h>
#include <taler/taler_json_lib.h>
#include "taler-merchant-httpd_get-private-incoming-ID.h"


/**
 * Function called with information about orders aggregated into
 * a wire transfer.
 * Generate a response (array entry) based on the given arguments.
 *
 * @param cls closure with a `json_t *` array to build up the response
 * @param order_id ID of the order that was paid and aggregated
 * @param remaining_deposit deposited amount minus any refunds
 * @param deposit_fee deposit fees paid to the exchange for the order
 */
static void
reconciliation_cb (void *cls,
                   const char *order_id,
                   const struct TALER_Amount *remaining_deposit,
                   const struct TALER_Amount *deposit_fee)
{
  json_t *rd = cls;
  json_t *r;

  r = GNUNET_JSON_PACK (
    GNUNET_JSON_pack_string ("order_id",
                             order_id),
    TALER_JSON_pack_amount ("remaining_deposit",
                            remaining_deposit),
    TALER_JSON_pack_amount ("deposit_fee",
                            deposit_fee));
  GNUNET_assert (0 ==
                 json_array_append_new (rd,
                                        r));
}


/**
 * Manages a GET /private/incoming call.
 *
 * @param rh context of the handler
 * @param connection the MHD connection to handle
 * @param[in,out] hc context with further information about the request
 * @return MHD result code
 */
MHD_RESULT
TMH_private_get_incoming_ID (const struct TMH_RequestHandler *rh,
                             struct MHD_Connection *connection,
                             struct TMH_HandlerContext *hc)
{
  unsigned long long serial_id;
  struct TALER_Amount wire_fee;
  bool no_fee;
  struct GNUNET_TIME_Timestamp expected_time;
  struct TALER_Amount expected_credit_amount;
  struct TALER_WireTransferIdentifierRawP wtid;
  struct TALER_FullPayto payto_uri;
  char *exchange_url = NULL;
  struct GNUNET_TIME_Timestamp execution_time;
  bool confirmed;

  {
    char dummy;

    if (1 !=
        sscanf (hc->infix,
                "%llu%c",
                &serial_id,
                &dummy))
    {
      GNUNET_break_op (0);
      return TALER_MHD_reply_with_error (
        connection,
        MHD_HTTP_BAD_REQUEST,
        TALER_EC_GENERIC_PARAMETER_MALFORMED,
        "transfer ID must be a number");
    }
  }

  TMH_db->preflight (TMH_db->cls);
  {
    struct TALER_MasterPublicKeyP master_pub;
    enum GNUNET_DB_QueryStatus qs;

    qs = TMH_db->lookup_expected_transfer (TMH_db->cls,
                                           hc->instance->settings.id,
                                           serial_id,
                                           &expected_time,
                                           &expected_credit_amount,
                                           &wtid,
                                           &payto_uri,
                                           &exchange_url,
                                           &execution_time,
                                           &confirmed,
                                           &master_pub);
    if (0 > qs)
    {
      /* Simple select queries should not cause serialization issues */
      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
      /* Always report on hard error as well to enable diagnostics */
      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
      return TALER_MHD_reply_with_error (connection,
                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
                                         "lookup_expected_transfer");
    }
    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    {
      GNUNET_break_op (0);
      return TALER_MHD_reply_with_error (
        connection,
        MHD_HTTP_NOT_FOUND,
        TALER_EC_MERCHANT_GENERIC_EXPECTED_TRANSFER_UNKNOWN,
        hc->infix);
    }

    {
      char *method;
      struct GNUNET_TIME_Timestamp start_date;
      struct GNUNET_TIME_Timestamp end_date;
      struct TALER_MasterSignatureP master_sig;
      struct TALER_WireFeeSet fees;

      method = TALER_payto_get_method (payto_uri.full_payto);
      qs = TMH_db->lookup_wire_fee (
        TMH_db->cls,
        &master_pub,
        method,
        expected_time,
        &fees,
        &start_date,
        &end_date,
        &master_sig);
      GNUNET_free (method);
      if (0 > qs)
      {
        /* Simple select queries should not cause serialization issues */
        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
        /* Always report on hard error as well to enable diagnostics */
        GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
        GNUNET_free (exchange_url);
        GNUNET_free (payto_uri.full_payto);
        return TALER_MHD_reply_with_error (connection,
                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
                                           "lookup_wire_fee");
      }
      no_fee = (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
      if (! no_fee)
        wire_fee = fees.wire;
    }

  }

  {
    enum GNUNET_DB_QueryStatus qs;
    json_t *rd;
    MHD_RESULT mret;

    rd = json_array ();
    GNUNET_assert (NULL != rd);
    qs = TMH_db->lookup_reconciliation_details (TMH_db->cls,
                                                hc->instance->settings.id,
                                                serial_id,
                                                &reconciliation_cb,
                                                rd);
    if (0 > qs)
    {
      /* Simple select queries should not cause serialization issues */
      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
      /* Always report on hard error as well to enable diagnostics */
      GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
      GNUNET_free (exchange_url);
      GNUNET_free (payto_uri.full_payto);
      return TALER_MHD_reply_with_error (connection,
                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
                                         TALER_EC_GENERIC_DB_FETCH_FAILED,
                                         "lookup_reconciliation_details");
    }

    mret = TALER_MHD_REPLY_JSON_PACK (
      connection,
      MHD_HTTP_OK,
      GNUNET_JSON_pack_allow_null (
        TALER_JSON_pack_amount (
          "expected_credit_amount",
          TALER_amount_is_valid (&expected_credit_amount)
          ? &expected_credit_amount
          : NULL)),
      GNUNET_JSON_pack_data_auto ("wtid",
                                  &wtid),
      TALER_JSON_pack_full_payto ("payto_uri",
                                  payto_uri),
      GNUNET_JSON_pack_string ("exchange_url",
                               exchange_url),
      GNUNET_JSON_pack_bool ("confirmed",
                             confirmed),
      GNUNET_JSON_pack_allow_null (
        GNUNET_JSON_pack_timestamp ("execution_time",
                                    execution_time)),
      GNUNET_JSON_pack_timestamp ("expected_time",
                                  expected_time),
      GNUNET_JSON_pack_allow_null (
        TALER_JSON_pack_amount ("wire_fee",
                                no_fee ? NULL : &wire_fee)),
      GNUNET_JSON_pack_array_steal ("reconciliation_details",
                                    rd));
    GNUNET_free (exchange_url);
    GNUNET_free (payto_uri.full_payto);
    return mret;
  }
}


/* end of taler-merchant-httpd_get-private-incoming-ID.c */
