/*
  This file is part of TALER
  Copyright (C) 2023, 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 merchant-tools/taler-merchant-passwd.c
 * @brief Reset access tokens for instances.
 * @author Christian Grothoff
 */
#include "platform.h"
#include <taler/taler_util.h>
#include <taler/taler_dbevents.h>
#include <gnunet/gnunet_util_lib.h>
#include "taler_merchant_util.h"
#include "taler_merchantdb_lib.h"
#include "taler_merchantdb_lib.h"

/**
 * Instance to set password for.
 */
static char *instance;

/**
 * Return value from main().
 */
static int global_ret;

/**
 * Main function that will be run.
 *
 * @param cls closure
 * @param args remaining command-line arguments
 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
 * @param config configuration
 */
static void
run (void *cls,
     char *const *args,
     const char *cfgfile,
     const struct GNUNET_CONFIGURATION_Handle *config)
{
  struct TALER_MERCHANTDB_Plugin *plugin;
  struct GNUNET_CONFIGURATION_Handle *cfg;
  const char *pw = args[0];
  struct TALER_MERCHANTDB_InstanceAuthSettings ias;
  enum GNUNET_DB_QueryStatus qs;

  if (NULL != args[1])
  {
    fprintf (stderr,
             "Superfluous command-line option `%s' specified\n",
             args[1]);
    global_ret = -1;
    return;
  }
  if (NULL == pw)
    pw = getenv ("TALER_MERCHANT_PASSWORD");
  if (NULL == pw)
  {
    fprintf (stderr,
             "New password not specified (pass on command-line or via TALER_MERCHANT_PASSWORD)\n");
    global_ret = -1;
    return;
  }
  if (NULL == instance)
    instance = GNUNET_strdup ("admin");
  cfg = GNUNET_CONFIGURATION_dup (config);
  if (NULL ==
      (plugin = TALER_MERCHANTDB_plugin_load (cfg)))
  {
    fprintf (stderr,
             "Failed to initialize database plugin.\n");
    global_ret = 1;
    GNUNET_CONFIGURATION_destroy (cfg);
    return;
  }

  GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
                              &ias.auth_salt,
                              sizeof (ias.auth_salt));
  TALER_merchant_instance_auth_hash_with_salt (&ias.auth_hash,
                                               &ias.auth_salt,
                                               pw);
  if (GNUNET_OK !=
      plugin->connect (plugin->cls))
  {
    fprintf (stderr,
             "Failed to connect to database. Consider running taler-merchant-dbinit!\n");
    global_ret = 1;
    TALER_MERCHANTDB_plugin_unload (plugin);
    GNUNET_CONFIGURATION_destroy (cfg);
    return;
  }
  qs = plugin->update_instance_auth (plugin->cls,
                                     instance,
                                     &ias);
  switch (qs)
  {
  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    {
      struct GNUNET_DB_EventHeaderP es = {
        .size = ntohs (sizeof (es)),
        .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
      };

      plugin->event_notify (plugin->cls,
                            &es,
                            instance,
                            strlen (instance) + 1);
    }
    break;
  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    if (0 ==
        strcmp (instance,
                "admin"))
    {
      struct TALER_MerchantPrivateKeyP merchant_priv;
      struct TALER_MerchantPublicKeyP merchant_pub;
      struct TALER_MERCHANTDB_InstanceSettings is = {
        .id = (char *) "admin",
        .name = (char *) "Administrator",
        .use_stefan = true,
        .address = json_object (),
        .jurisdiction = json_object (),
      };

      if (GNUNET_OK !=
          GNUNET_CONFIGURATION_get_value_time (config,
                                               "merchant",
                                               "DEFAULT_WIRE_TRANSFER_DELAY",
                                               &is.default_wire_transfer_delay)
          )
      {
        is.default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS;
      }
      if (GNUNET_OK !=
          GNUNET_CONFIGURATION_get_value_time (config,
                                               "merchant",
                                               "DEFAULT_PAY_DELAY",
                                               &is.default_pay_delay))
      {
        is.default_pay_delay = GNUNET_TIME_UNIT_DAYS;
      }
      if (GNUNET_OK !=
          GNUNET_CONFIGURATION_get_value_time (config,
                                               "merchant",
                                               "DEFAULT_REFUND_DELAY",
                                               &is.default_refund_delay))
      {
        is.default_refund_delay = GNUNET_TIME_relative_multiply (
          GNUNET_TIME_UNIT_DAYS,
          15);
      }

      GNUNET_CRYPTO_eddsa_key_create (&merchant_priv.eddsa_priv);
      GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv.eddsa_priv,
                                          &merchant_pub.eddsa_pub);
      qs = plugin->insert_instance (plugin->cls,
                                    &merchant_pub,
                                    &merchant_priv,
                                    &is,
                                    &ias,
                                    false);
      json_decref (is.address);
      json_decref (is.jurisdiction);
      switch (qs)
      {
      case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
        fprintf (stderr,
                 "`%s' instance created with default settings\n",
                 instance);
        break;
      case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
        GNUNET_break (0);
        break;
      case GNUNET_DB_STATUS_SOFT_ERROR:
      case GNUNET_DB_STATUS_HARD_ERROR:
        fprintf (stderr,
                 "Internal database error.\n");
        global_ret = 3;
        break;
      }
      {
        struct GNUNET_DB_EventHeaderP es = {
          .size = ntohs (sizeof (es)),
          .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
        };

        plugin->event_notify (plugin->cls,
                              &es,
                              instance,
                              strlen (instance) + 1);
      }
    }
    else
    {
      fprintf (stderr,
               "Instance `%s' unknown, cannot reset token\n",
               instance);
      global_ret = 2;
    }
    break;
  case GNUNET_DB_STATUS_SOFT_ERROR:
  case GNUNET_DB_STATUS_HARD_ERROR:
    fprintf (stderr,
             "Internal database error.\n");
    global_ret = 3;
    break;
  }
  TALER_MERCHANTDB_plugin_unload (plugin);
  GNUNET_CONFIGURATION_destroy (cfg);
}


/**
 * The main function of the database initialization tool.
 * Used to initialize the Taler Exchange's database.
 *
 * @param argc number of arguments from the command line
 * @param argv command line arguments
 * @return 0 ok, 1 on error
 */
int
main (int argc,
      char *const *argv)
{
  struct GNUNET_GETOPT_CommandLineOption options[] = {
    GNUNET_GETOPT_option_string ('i',
                                 "instance",
                                 "ID",
                                 "which instance to reset the password of",
                                 &instance),

    GNUNET_GETOPT_option_version (PACKAGE_VERSION "-" VCS_VERSION),
    GNUNET_GETOPT_OPTION_END
  };
  enum GNUNET_GenericReturnValue ret;

  ret = GNUNET_PROGRAM_run (
    TALER_MERCHANT_project_data (),
    argc, argv,
    "taler-merchant-passwd",
    gettext_noop ("Reset instance password"),
    options,
    &run, NULL);
  if (GNUNET_SYSERR == ret)
    return 3;
  if (GNUNET_NO == ret)
    return 0;
  return global_ret;
}


/* end of taler-merchant-passwd.c */
