/*
  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 testing_api_cmd_get_unit.c
 * @brief command to test GET /private/units/$ID
 * @author Bohdan Potuzhnyi
 */
#include "platform.h"
#include <jansson.h>
#include <taler/taler_testing_lib.h>
#include "taler_merchant_service.h"
#include "taler_merchant_testing_lib.h"


/**
 * State for a GET /private/units/$ID command.
 */
struct GetUnitState
{
  /**
   * In-flight request handle.
   */
  struct TALER_MERCHANT_UnitGetHandle *ugh;

  /**
   * Interpreter context.
   */
  struct TALER_TESTING_Interpreter *is;

  /**
   * Merchant backend base URL.
   */
  const char *merchant_url;

  /**
   * Unit identifier to fetch.
   */
  const char *unit_id;

  /**
   * Expected HTTP status.
   */
  unsigned int http_status;

  /**
   * Optional command label providing expected traits.
   */
  const char *reference;
};


/**
 * Compare response @a entry with traits from @a ref_cmd.
 */
static bool
unit_matches_reference (const struct TALER_MERCHANT_UnitEntry *entry,
                        const struct TALER_TESTING_Command *ref_cmd)
{
  const char *unit_id;

  if (GNUNET_OK !=
      TALER_TESTING_get_trait_unit_id (ref_cmd,
                                       &unit_id))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Unit trait resolution failed for reference `%s'\n",
                ref_cmd->label);
    return false;
  }
  if (0 != strcmp (entry->unit,
                   unit_id))
  {
    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                "Unit mismatch: expected id %s got %s\n",
                unit_id,
                entry->unit);
    return false;
  }

  {
    const char *unit_name_long = NULL;

    if (GNUNET_OK ==
        TALER_TESTING_get_trait_unit_name_long (ref_cmd,
                                                &unit_name_long))
    {
      if ( (NULL != unit_name_long) &&
           (0 != strcmp (entry->unit_name_long,
                         unit_name_long)) )
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Unit %s mismatch: expected long label '%s' got '%s'\n",
                    entry->unit,
                    unit_name_long,
                    entry->unit_name_long);
        return false;
      }
    }
  }
  {
    const char *unit_name_short = NULL;

    if (GNUNET_OK ==
        TALER_TESTING_get_trait_unit_name_short (ref_cmd,
                                                 &unit_name_short))
    {
      if ( (NULL != unit_name_short) &&
           (0 != strcmp (entry->unit_name_short,
                         unit_name_short)) )
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Unit %s mismatch: expected short label '%s' got '%s'\n",
                    entry->unit,
                    unit_name_short,
                    entry->unit_name_short);
        return false;
      }
    }
  }
  {
    const bool *unit_allow_fraction = NULL;

    if (GNUNET_OK ==
        TALER_TESTING_get_trait_unit_allow_fraction (ref_cmd,
                                                     &unit_allow_fraction))
    {
      if ( (NULL != unit_allow_fraction) &&
           (*unit_allow_fraction != entry->unit_allow_fraction) )
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Unit %s mismatch: expected allow_fraction %d got %d\n",
                    entry->unit,
                    (int) *unit_allow_fraction,
                    (int) entry->unit_allow_fraction);
        return false;
      }
    }
  }
  {
    const uint32_t *unit_precision_level = NULL;

    if (GNUNET_OK ==
        TALER_TESTING_get_trait_unit_precision_level (ref_cmd,
                                                      &unit_precision_level))
    {
      if ( (NULL != unit_precision_level) &&
           (*unit_precision_level != entry->unit_precision_level) )
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Unit %s mismatch: expected precision %u got %u\n",
                    entry->unit,
                    (unsigned int) *unit_precision_level,
                    (unsigned int) entry->unit_precision_level);
        return false;
      }
    }
  }
  {
    const bool *unit_active = NULL;

    if (GNUNET_OK ==
        TALER_TESTING_get_trait_unit_active (ref_cmd,
                                             &unit_active))
    {
      if ( (NULL != unit_active) &&
           (*unit_active != entry->unit_active) )
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Unit %s mismatch: expected active flag %d got %d\n",
                    entry->unit,
                    (int) *unit_active,
                    (int) entry->unit_active);
        return false;
      }
    }
  }
  {
    const json_t *unit_name_long_i18n = NULL;

    if (GNUNET_OK ==
        TALER_TESTING_get_trait_unit_name_long_i18n (ref_cmd,
                                                     &unit_name_long_i18n))
    {
      if ( (NULL != unit_name_long_i18n) &&
           ( (NULL == entry->unit_name_long_i18n) ||
             (1 != json_equal (unit_name_long_i18n,
                               entry->unit_name_long_i18n)) ) )
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Unit %s mismatch: long_i18n differs\n",
                    entry->unit);
        return false;
      }
    }
  }
  {
    const json_t *unit_name_short_i18n = NULL;

    if (GNUNET_OK ==
        TALER_TESTING_get_trait_unit_name_short_i18n (ref_cmd,
                                                      &unit_name_short_i18n))
    {
      if ( (NULL != unit_name_short_i18n) &&
           ( (NULL == entry->unit_name_short_i18n) ||
             (1 != json_equal (unit_name_short_i18n,
                               entry->unit_name_short_i18n)) ) )
      {
        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                    "Unit %s mismatch: short_i18n differs\n",
                    entry->unit);
        return false;
      }
    }
  }
  return true;
}


/**
 * Completion callback.
 */
static void
get_unit_cb (void *cls,
             const struct TALER_MERCHANT_UnitGetResponse *ugr)
{
  struct GetUnitState *gug = cls;

  gug->ugh = NULL;
  if (gug->http_status != ugr->hr.http_status)
  {
    TALER_TESTING_unexpected_status_with_body (gug->is,
                                               ugr->hr.http_status,
                                               gug->http_status,
                                               ugr->hr.reply);
    return;
  }
  if ( (MHD_HTTP_OK == ugr->hr.http_status) &&
       (NULL != gug->reference) )
  {
    const struct TALER_TESTING_Command *ref_cmd;

    ref_cmd = TALER_TESTING_interpreter_lookup_command (gug->is,
                                                        gug->reference);
    if (NULL == ref_cmd)
    {
      GNUNET_break (0);
      TALER_TESTING_interpreter_fail (gug->is);
      return;
    }
    if (! unit_matches_reference (&ugr->details.ok.unit,
                                  ref_cmd))
    {
      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "GET /private/units/%s response does not match expectation from `%s'\n",
                  gug->unit_id,
                  gug->reference);
      TALER_TESTING_interpreter_fail (gug->is);
      return;
    }
  }
  TALER_TESTING_interpreter_next (gug->is);
}


/**
 * Issue GET request.
 */
static void
get_unit_run (void *cls,
              const struct TALER_TESTING_Command *cmd,
              struct TALER_TESTING_Interpreter *is)
{
  struct GetUnitState *gug = cls;

  gug->is = is;
  gug->ugh = TALER_MERCHANT_unit_get (
    TALER_TESTING_interpreter_get_context (is),
    gug->merchant_url,
    gug->unit_id,
    &get_unit_cb,
    gug);
  if (NULL == gug->ugh)
  {
    GNUNET_break (0);
    TALER_TESTING_interpreter_fail (is);
  }
}


/**
 * Cleanup.
 */
static void
get_unit_cleanup (void *cls,
                  const struct TALER_TESTING_Command *cmd)
{
  struct GetUnitState *gug = cls;

  if (NULL != gug->ugh)
  {
    TALER_MERCHANT_unit_get_cancel (gug->ugh);
    gug->ugh = NULL;
  }
  GNUNET_free (gug);
}


struct TALER_TESTING_Command
TALER_TESTING_cmd_merchant_get_unit (const char *label,
                                     const char *merchant_url,
                                     const char *unit_id,
                                     unsigned int http_status,
                                     const char *reference)
{
  struct GetUnitState *gug;

  gug = GNUNET_new (struct GetUnitState);
  gug->merchant_url = merchant_url;
  gug->unit_id = unit_id;
  gug->http_status = http_status;
  gug->reference = reference;

  {
    struct TALER_TESTING_Command cmd = {
      .cls = gug,
      .label = label,
      .run = &get_unit_run,
      .cleanup = &get_unit_cleanup
    };

    return cmd;
  }
}


/* end of testing_api_cmd_get_unit.c */
