/*
   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 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 backenddb/pg_lookup_units.c
 * @brief Implementation of the lookup_units function for Postgres
 * @author Bohdan Potuzhnyi
 */
#include "platform.h"
#include <taler/taler_error_codes.h>
#include <taler/taler_dbevents.h>
#include <taler/taler_pq_lib.h>
#include "pg_lookup_units.h"
#include "pg_helper.h"


/**
 * Context used for TMH_PG_lookup_units().
 */
struct LookupUnitsContext
{
  TALER_MERCHANTDB_UnitsCallback cb;
  void *cb_cls;
  bool extract_failed;
};


static void
lookup_units_cb (void *cls,
                 PGresult *result,
                 unsigned int num_results)
{
  struct LookupUnitsContext *luc = cls;

  for (unsigned int i = 0; i<num_results; i++)
  {
    struct TALER_MERCHANTDB_UnitDetails ud = { 0 };
    struct GNUNET_PQ_ResultSpec rs[] = {
      GNUNET_PQ_result_spec_uint64 ("unit_serial",
                                    &ud.unit_serial),
      GNUNET_PQ_result_spec_string ("unit",
                                    &ud.unit),
      GNUNET_PQ_result_spec_string ("unit_name_long",
                                    &ud.unit_name_long),
      GNUNET_PQ_result_spec_string ("unit_name_short",
                                    &ud.unit_name_short),
      TALER_PQ_result_spec_json ("unit_name_long_i18n",
                                 &ud.unit_name_long_i18n),
      TALER_PQ_result_spec_json ("unit_name_short_i18n",
                                 &ud.unit_name_short_i18n),
      GNUNET_PQ_result_spec_bool ("unit_allow_fraction",
                                  &ud.unit_allow_fraction),
      GNUNET_PQ_result_spec_uint32 ("unit_precision_level",
                                    &ud.unit_precision_level),
      GNUNET_PQ_result_spec_bool ("unit_active",
                                  &ud.unit_active),
      GNUNET_PQ_result_spec_bool ("unit_builtin",
                                  &ud.unit_builtin),
      GNUNET_PQ_result_spec_end
    };

    if (GNUNET_OK !=
        GNUNET_PQ_extract_result (result,
                                  rs,
                                  i))
    {
      GNUNET_break (0);
      luc->extract_failed = true;
      return;
    }
    luc->cb (luc->cb_cls,
             ud.unit_serial,
             &ud);
    GNUNET_PQ_cleanup_result (rs);
  }
}


enum GNUNET_DB_QueryStatus
TMH_PG_lookup_units (void *cls,
                     const char *instance_id,
                     TALER_MERCHANTDB_UnitsCallback cb,
                     void *cb_cls)
{
  struct PostgresClosure *pg = cls;
  struct LookupUnitsContext luc = {
    .cb = cb,
    .cb_cls = cb_cls,
    .extract_failed = false
  };
  struct GNUNET_PQ_QueryParam params[] = {
    GNUNET_PQ_query_param_string (instance_id),
    GNUNET_PQ_query_param_end
  };
  enum GNUNET_DB_QueryStatus qs;

  check_connection (pg);
  PREPARE (pg,
           "lookup_units",
           "WITH mi AS ("
           "  SELECT merchant_serial FROM merchant_instances WHERE merchant_id=$1"
           ")"
           "SELECT cu.unit_serial"
           "      ,cu.unit"
           "      ,cu.unit_name_long"
           "      ,cu.unit_name_short"
           "      ,cu.unit_name_long_i18n"
           "      ,cu.unit_name_short_i18n"
           "      ,cu.unit_allow_fraction"
           "      ,cu.unit_precision_level"
           "      ,cu.unit_active"
           "      ,FALSE AS unit_builtin"
           "  FROM merchant_custom_units cu"
           "  JOIN mi ON cu.merchant_serial = mi.merchant_serial"
           " UNION ALL "
           "SELECT bu.unit_serial"
           "      ,bu.unit"
           "      ,bu.unit_name_long"
           "      ,bu.unit_name_short"
           "      ,bu.unit_name_long_i18n"
           "      ,bu.unit_name_short_i18n"
           "      ,COALESCE(bo.override_allow_fraction, bu.unit_allow_fraction)"
           "      ,COALESCE(bo.override_precision_level, bu.unit_precision_level)"
           "      ,COALESCE(bo.override_active, bu.unit_active)"
           "      ,TRUE AS unit_builtin"
           "  FROM merchant_builtin_units bu"
           "  JOIN mi ON TRUE"
           "  LEFT JOIN merchant_builtin_unit_overrides bo"
           "    ON bo.builtin_unit_serial = bu.unit_serial"
           "   AND bo.merchant_serial = mi.merchant_serial"
           " ORDER BY unit");
  qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn,
                                             "lookup_units",
                                             params,
                                             &lookup_units_cb,
                                             &luc);
  if (luc.extract_failed)
    return GNUNET_DB_STATUS_HARD_ERROR;
  return qs;
}
