/*
   This file is part of Challenger
   Copyright (C) 2023 Taler Systems SA

   Challenger 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.

   Challenger 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
   Challenger; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
/**
 * @file src/challengerdb/do_solve_challenge.c
 * @brief Implementation of the do_solve_challenge function for Postgres
 * @author Christian Grothoff
 */
#include "platform.h"
#include <taler/taler_error_codes.h>
#include <taler/taler_dbevents.h>
#include <taler/taler_pq_lib.h>
#include "do_solve_challenge.h"
#include "pg_helper.h"


enum GNUNET_DB_QueryStatus
CHALLENGERDB_do_solve_challenge (
  struct CHALLENGERDB_PostgresContext *ctx,
  const struct CHALLENGER_ValidationNonceP *nonce,
  uint32_t new_pin,
  bool *solved,
  bool *exhausted,
  bool *no_challenge,
  char **state,
  uint32_t *addr_left,
  uint32_t *auth_attempts_left,
  uint32_t *pin_transmissions_left,
  char **client_redirect_uri)
{
  struct GNUNET_TIME_Absolute now
    = GNUNET_TIME_absolute_get ();
  struct GNUNET_PQ_QueryParam params[] = {
    GNUNET_PQ_query_param_auto_from_type (nonce),
    GNUNET_PQ_query_param_uint32 (&new_pin),
    GNUNET_PQ_query_param_absolute_time (&now),
    GNUNET_PQ_query_param_end
  };
  bool not_found;
  struct GNUNET_PQ_ResultSpec rs[] = {
    GNUNET_PQ_result_spec_bool ("not_found",
                                &not_found),
    GNUNET_PQ_result_spec_bool ("solved",
                                solved),
    GNUNET_PQ_result_spec_bool ("exhausted",
                                exhausted),
    GNUNET_PQ_result_spec_bool ("no_challenge",
                                no_challenge),
    GNUNET_PQ_result_spec_uint32 ("address_attempts_left",
                                  addr_left),
    GNUNET_PQ_result_spec_uint32 ("auth_attempts_left",
                                  auth_attempts_left),
    GNUNET_PQ_result_spec_uint32 ("pin_transmissions_left",
                                  pin_transmissions_left),
    GNUNET_PQ_result_spec_allow_null (
      GNUNET_PQ_result_spec_string ("client_redirect_uri",
                                    client_redirect_uri),
      NULL),
    GNUNET_PQ_result_spec_allow_null (
      GNUNET_PQ_result_spec_string ("state",
                                    state),
      NULL),
    GNUNET_PQ_result_spec_end
  };
  enum GNUNET_DB_QueryStatus qs;

  *client_redirect_uri = NULL;
  PREPARE (ctx,
           "do_solve_challenge",
           "SELECT "
           " out_not_found AS not_found"
           ",out_solved AS solved"
           ",out_exhausted AS exhausted"
           ",out_no_challenge AS no_challenge"
           ",out_state AS state"
           ",out_address_attempts_left AS address_attempts_left"
           ",out_auth_attempts_left AS auth_attempts_left"
           ",out_pin_transmissions_left AS pin_transmissions_left"
           ",out_client_redirect_uri AS client_redirect_uri"
           " FROM challenger_do_validate_and_solve_pin"
           " ($1,$2,$3);");
  qs = GNUNET_PQ_eval_prepared_singleton_select (ctx->conn,
                                                 "do_solve_challenge",
                                                 params,
                                                 rs);
  if (qs <= 0)
    return qs;
  if (not_found)
    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
  /* On the solved path the stored procedure returns the -1 sentinel (kept in
     the DB to mark the challenge as solved), which the uint32 result spec reads
     as 4294967295.  That value is not meaningful to the caller (which only
     reads it when !solved); normalize it to 0 to avoid leaking the sentinel. */
  if (*solved)
    *auth_attempts_left = 0;
  return qs;
}
