/*
  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 util/mfa.c
 * @brief helper functions for MFA processing
 * @author Christian Grothoff
 */
#include "platform.h"
#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_json_lib.h>
#include <taler/taler_util.h>
#include <taler/taler_json_lib.h>
#include <taler_merchant_util.h>
#include <jansson.h>

/**
 * Mapping from critical operation enum to string.
 */
static const char *co_strings[] = {
  [TALER_MERCHANT_MFA_CO_NONE] = NULL,
  [TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION] = "instance_provision",
  [TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION] = "account_config",
  [TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION] = "auth_config",
  [TALER_MERCHANT_MFA_CO_INSTANCE_DELETION] = "instance_deletion",
  [TALER_MERCHANT_MFA_CO_AUTH_TOKEN_CREATION] = "auth_token_creation"
};

/**
 * Mapping from MFA channel enum to string.
 */
static const char *channel_strings[] = {
  [TALER_MERCHANT_MFA_CHANNEL_NONE] = NULL,
  [TALER_MERCHANT_MFA_CHANNEL_SMS] = "sms",
  [TALER_MERCHANT_MFA_CHANNEL_EMAIL] = "email",
  [TALER_MERCHANT_MFA_CHANNEL_TOTP] = "totp"
};


const char *
TALER_MERCHANT_MFA_co_to_string (
  enum TALER_MERCHANT_MFA_CriticalOperation co)
{
  if ( (co < 0) ||
       (co >= sizeof (co_strings) / sizeof (co_strings[0])) )
  {
    GNUNET_break (0);
    return NULL;
  }
  return co_strings[co];
}


enum TALER_MERCHANT_MFA_CriticalOperation
TALER_MERCHANT_MFA_co_from_string (const char *str)
{
  if (NULL == str)
    return TALER_MERCHANT_MFA_CO_NONE;
  for (unsigned int i = 1;
       i < sizeof (co_strings) / sizeof (co_strings[0]);
       i++)
  {
    if (0 == strcmp (str,
                     co_strings[i]))
      return (enum TALER_MERCHANT_MFA_CriticalOperation) i;
  }
  GNUNET_break (0);
  return TALER_MERCHANT_MFA_CO_NONE;
}


const char *
TALER_MERCHANT_MFA_co2s (
  enum TALER_MERCHANT_MFA_CriticalOperation co)
{
  static const char *co_s[] = {
    [TALER_MERCHANT_MFA_CO_NONE] = NULL,
    [TALER_MERCHANT_MFA_CO_INSTANCE_PROVISION] = "create new instance",
    [TALER_MERCHANT_MFA_CO_ACCOUNT_CONFIGURATION] = "configure bank accounts",
    [TALER_MERCHANT_MFA_CO_AUTH_CONFIGURATION] =
      "change authentication configuration",
    [TALER_MERCHANT_MFA_CO_INSTANCE_DELETION] = "delete instance",
    [TALER_MERCHANT_MFA_CO_AUTH_TOKEN_CREATION] = "create authentication token"
  };

  if ( (co < 0) ||
       (co >= sizeof (co_s) / sizeof (co_s[0])) )
  {
    GNUNET_break (0);
    return NULL;
  }
  return co_s[co];
}


const char *
TALER_MERCHANT_MFA_channel_to_string (
  enum TALER_MERCHANT_MFA_Channel ch)
{
  if ( (ch < 0) ||
       (ch >= sizeof (channel_strings) / sizeof (channel_strings[0])) )
  {
    GNUNET_break (0);
    return NULL;
  }
  return channel_strings[ch];
}


enum TALER_MERCHANT_MFA_Channel
TALER_MERCHANT_MFA_channel_from_string (const char *str)
{
  if (NULL == str)
    return TALER_MERCHANT_MFA_CHANNEL_NONE;
  for (unsigned int i = 1;
       i < sizeof (channel_strings) / sizeof (channel_strings[0]);
       i++)
  {
    if (0 == strcmp (str,
                     channel_strings[i]))
      return (enum TALER_MERCHANT_MFA_Channel) i;
  }
  GNUNET_break (0);
  return TALER_MERCHANT_MFA_CHANNEL_NONE;
}


void
TALER_MERCHANT_mfa_body_hash (
  const json_t *body,
  const struct TALER_MERCHANT_MFA_BodySalt *salt,
  struct TALER_MERCHANT_MFA_BodyHash *h_body)
{
  char *json_str;
  struct GNUNET_HashCode hash;
  struct GNUNET_HashContext *hc;

  if (NULL == body)
  {
    json_str = NULL;
  }
  else
  {
    json_str = json_dumps (body,
                           JSON_COMPACT | JSON_SORT_KEYS);
    GNUNET_assert (NULL != json_str);
  }
  hc = GNUNET_CRYPTO_hash_context_start ();
  GNUNET_CRYPTO_hash_context_read (hc,
                                   salt,
                                   sizeof (*salt));
  if (NULL != json_str)
  {
    GNUNET_CRYPTO_hash_context_read (hc,
                                     json_str,
                                     strlen (json_str));
    free (json_str);
  }
  GNUNET_CRYPTO_hash_context_finish (hc,
                                     &hash);

  GNUNET_static_assert (sizeof (*h_body) <= sizeof (hash));
  /* Truncate to short hash */
  memcpy (&h_body->hash,
          &hash,
          sizeof (*h_body));
}
