/* plugins/bkpr/test/run-report.c */
#include "config.h"
#include <assert.h>
#include <ccan/str/str.h>
#include <ccan/tal/str/str.h>
#include <common/amount.h>
#include <common/json_parse.h>
#include <common/setup.h>
#include <plugins/bkpr/bookkeeper.h>

#include "../report.c"

/* AUTOGENERATED MOCKS START */
/* Generated stub for bkpr_of */
struct bkpr *bkpr_of(struct plugin *plugin UNNEEDED)
{ fprintf(stderr, "bkpr_of called!\n"); abort(); }
/* Generated stub for command_check_done */
struct command_result *command_check_done(struct command *cmd)

{ fprintf(stderr, "command_check_done called!\n"); abort(); }
/* Generated stub for command_check_only */
bool command_check_only(const struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_check_only called!\n"); abort(); }
/* Generated stub for command_deprecated_in_ok */
bool command_deprecated_in_ok(struct command *cmd UNNEEDED,
			      const char *param UNNEEDED,
			      const char *depr_start UNNEEDED,
			      const char *depr_end UNNEEDED)
{ fprintf(stderr, "command_deprecated_in_ok called!\n"); abort(); }
/* Generated stub for command_dev_apis */
bool command_dev_apis(const struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_dev_apis called!\n"); abort(); }
/* Generated stub for command_fail */
struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED,
				    const char *fmt UNNEEDED, ...)

{ fprintf(stderr, "command_fail called!\n"); abort(); }
/* Generated stub for command_filter_ptr */
struct json_filter **command_filter_ptr(struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_filter_ptr called!\n"); abort(); }
/* Generated stub for command_finished */
struct command_result *command_finished(struct command *cmd UNNEEDED, struct json_stream *response)

{ fprintf(stderr, "command_finished called!\n"); abort(); }
/* Generated stub for command_log */
void command_log(struct command *cmd UNNEEDED, enum log_level level UNNEEDED,
		 const char *fmt UNNEEDED, ...)

{ fprintf(stderr, "command_log called!\n"); abort(); }
/* Generated stub for command_set_usage */
void command_set_usage(struct command *cmd UNNEEDED, const char *usage UNNEEDED)
{ fprintf(stderr, "command_set_usage called!\n"); abort(); }
/* Generated stub for command_usage_only */
bool command_usage_only(const struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_usage_only called!\n"); abort(); }
/* Generated stub for currencyrate_str */
const char *currencyrate_str(const tal_t *ctx UNNEEDED,
			     const struct bkpr *bkpr UNNEEDED,
			     u64 timestamp UNNEEDED,
			     const struct amount_msat *msat UNNEEDED)
{ fprintf(stderr, "currencyrate_str called!\n"); abort(); }
/* Generated stub for jsonrpc_stream_success */
struct json_stream *jsonrpc_stream_success(struct command *cmd)

{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
/* Generated stub for list_income_events */
struct income_event **list_income_events(const tal_t *ctx UNNEEDED,
					 const struct bkpr *bkpr UNNEEDED,
					 struct command *cmd UNNEEDED,
					 u64 start_time UNNEEDED,
					 u64 end_time UNNEEDED,
					 bool consolidate_fees UNNEEDED)
{ fprintf(stderr, "list_income_events called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

/* Helper: make a literal-only format with one entry */
static struct report_format *make_literal(const tal_t *ctx, const char *s)
{
	struct report_format *f = tal(ctx, struct report_format);
	f->fmt = tal_arr(f, typeof(*f->fmt), 1);
	f->str = tal_arr(f, const char *, 1);
	f->ifset = tal_arr(f, struct report_format *, 1);
	f->ifnotset = tal_arr(f, struct report_format *, 1);
	f->fmt[0] = NULL;
	f->str[0] = tal_strdup(f, s);
	f->ifset[0] = NULL;
	f->ifnotset[0] = NULL;
	return f;
}

static void test_format_event_literal_only(void)
{
	struct report_format *f = tal(tmpctx, struct report_format);
	struct income_event *e = tal(tmpctx, struct income_event);
	struct bkpr *bkpr = tal(tmpctx, struct bkpr);
	char *s;

	f->fmt = tal_arr(f, typeof(*f->fmt), 1);
	f->str = tal_arr(f, const char *, 1);
	f->ifset = tal_arr(f, struct report_format *, 1);
	f->ifnotset = tal_arr(f, struct report_format *, 1);

	f->fmt[0] = NULL;
	f->str[0] = tal_strdup(f->str, "hello");
	f->ifset[0] = NULL;
	f->ifnotset[0] = NULL;

	s = format_event(tmpctx, f, REPORT_FMT_NONE, bkpr, e);
	assert(streq(s, "hello"));
}

static void test_format_event_simple_tag(void)
{
	struct report_format *f = tal(tmpctx, struct report_format);
	struct income_event *e = tal(tmpctx, struct income_event);
	struct bkpr *bkpr = tal(tmpctx, struct bkpr);
	char *s;

	e->tag = "invoice";

	f->fmt = tal_arr(f, typeof(*f->fmt), 3);
	f->str = tal_arr(f, const char *, 3);
	f->ifset = tal_arr(f, struct report_format *, 3);
	f->ifnotset = tal_arr(f, struct report_format *, 3);

	f->fmt[0] = NULL;
	f->str[0] = tal_strdup(f->str, "tag=");
	f->ifset[0] = NULL;
	f->ifnotset[0] = NULL;

	f->fmt[1] = report_fmt_tag;
	f->str[1] = NULL;
	f->ifset[1] = NULL;
	f->ifnotset[1] = NULL;

	f->fmt[2] = NULL;
	f->str[2] = tal_strdup(f->str, "!");
	f->ifset[2] = NULL;
	f->ifnotset[2] = NULL;

	s = format_event(tmpctx, f, REPORT_FMT_NONE, bkpr, e);
	assert(streq(s, "tag=invoice!"));
}

/* {payment_id:NONE} - if-not-set fallback */
static void test_format_event_fallback(void)
{
	struct report_format *f = tal(tmpctx, struct report_format);
	struct income_event *e = tal(tmpctx, struct income_event);
	struct bkpr *bkpr = tal(tmpctx, struct bkpr);
	char *s;

	f->fmt = tal_arr(f, typeof(*f->fmt), 1);
	f->str = tal_arr(f, const char *, 1);
	f->ifset = tal_arr(f, struct report_format *, 1);
	f->ifnotset = tal_arr(f, struct report_format *, 1);

	f->fmt[0] = report_fmt_payment_id;
	f->str[0] = NULL;
	f->ifset[0] = NULL;
	f->ifnotset[0] = make_literal(f, "NONE");

	e->payment_id = NULL;
	s = format_event(tmpctx, f, REPORT_FMT_NONE, bkpr, e);
	assert(streq(s, "NONE"));
}

/* {description:{txid:LAST}} - nested if-not-set */
static void test_format_event_nested_fallback(void)
{
	struct report_format *f = tal(tmpctx, struct report_format);
	struct report_format *ifnotset1 = tal(f, struct report_format);
	struct income_event *e = tal(tmpctx, struct income_event);
	struct bkpr *bkpr = tal(tmpctx, struct bkpr);
	char *s;

	e->txid = NULL;
	e->desc = NULL;

	ifnotset1->fmt = tal_arr(ifnotset1, typeof(*ifnotset1->fmt), 1);
	ifnotset1->str = tal_arr(ifnotset1, const char *, 1);
	ifnotset1->ifset = tal_arr(ifnotset1, struct report_format *, 1);
	ifnotset1->ifnotset = tal_arr(ifnotset1, struct report_format *, 1);
	ifnotset1->fmt[0] = report_fmt_txid;
	ifnotset1->str[0] = NULL;
	ifnotset1->ifset[0] = NULL;
	ifnotset1->ifnotset[0] = make_literal(ifnotset1, "LAST");

	f->fmt = tal_arr(f, typeof(*f->fmt), 1);
	f->str = tal_arr(f, const char *, 1);
	f->ifset = tal_arr(f, struct report_format *, 1);
	f->ifnotset = tal_arr(f, struct report_format *, 1);
	f->fmt[0] = report_fmt_desc;
	f->str[0] = NULL;
	f->ifset[0] = NULL;
	f->ifnotset[0] = ifnotset1;

	s = format_event(tmpctx, f, REPORT_FMT_NONE, bkpr, e);
	assert(streq(s, "LAST"));
}

static void test_format_event_csv_escape_value(void)
{
	struct report_format *f = tal(tmpctx, struct report_format);
	struct income_event *e = tal(tmpctx, struct income_event);
	struct bkpr *bkpr = tal(tmpctx, struct bkpr);
	char *s;

	e->desc = "hello, \"world\"";

	f->fmt = tal_arr(f, typeof(*f->fmt), 1);
	f->str = tal_arr(f, const char *, 1);
	f->ifset = tal_arr(f, struct report_format *, 1);
	f->ifnotset = tal_arr(f, struct report_format *, 1);

	f->fmt[0] = report_fmt_desc;
	f->str[0] = NULL;
	f->ifset[0] = NULL;
	f->ifnotset[0] = NULL;

	s = format_event(tmpctx, f, REPORT_FMT_CSV, bkpr, e);
	assert(streq(s, "\"hello, \"\"world\"\"\""));
}

static void test_format_event_missing_no_fallback(void)
{
	struct report_format *f = tal(tmpctx, struct report_format);
	struct income_event *e = tal(tmpctx, struct income_event);
	struct bkpr *bkpr = tal(tmpctx, struct bkpr);
	char *s;

	e->payment_id = NULL;

	f->fmt = tal_arr(f, typeof(*f->fmt), 3);
	f->str = tal_arr(f, const char *, 3);
	f->ifset = tal_arr(f, struct report_format *, 3);
	f->ifnotset = tal_arr(f, struct report_format *, 3);

	f->fmt[0] = NULL;
	f->str[0] = tal_strdup(f->str, "A");
	f->ifset[0] = NULL;
	f->ifnotset[0] = NULL;

	f->fmt[1] = report_fmt_payment_id;
	f->str[1] = NULL;
	f->ifset[1] = NULL;
	f->ifnotset[1] = NULL;

	f->fmt[2] = NULL;
	f->str[2] = tal_strdup(f->str, "B");
	f->ifset[2] = NULL;
	f->ifnotset[2] = NULL;

	s = format_event(tmpctx, f, REPORT_FMT_NONE, bkpr, e);
	assert(streq(s, "AB"));
}

/* {credit?+{credit}:0} - ifset wraps value, ifnotset is "0" */
static void test_format_event_ifset(void)
{
	struct report_format *f = tal(tmpctx, struct report_format);
	struct report_format *ifset = tal(f, struct report_format);
	struct income_event *e = tal(tmpctx, struct income_event);
	struct bkpr *bkpr = tal(tmpctx, struct bkpr);
	char *s;

	/* ifset format: "+{credit}" */
	ifset->fmt = tal_arr(ifset, typeof(*ifset->fmt), 2);
	ifset->str = tal_arr(ifset, const char *, 2);
	ifset->ifset = tal_arr(ifset, struct report_format *, 2);
	ifset->ifnotset = tal_arr(ifset, struct report_format *, 2);
	ifset->fmt[0] = NULL;
	ifset->str[0] = tal_strdup(ifset, "+");
	ifset->ifset[0] = NULL;
	ifset->ifnotset[0] = NULL;
	ifset->fmt[1] = report_fmt_credit;
	ifset->str[1] = NULL;
	ifset->ifset[1] = NULL;
	ifset->ifnotset[1] = NULL;

	f->fmt = tal_arr(f, typeof(*f->fmt), 1);
	f->str = tal_arr(f, const char *, 1);
	f->ifset = tal_arr(f, struct report_format *, 1);
	f->ifnotset = tal_arr(f, struct report_format *, 1);
	f->fmt[0] = report_fmt_credit;
	f->str[0] = NULL;
	f->ifset[0] = ifset;
	f->ifnotset[0] = make_literal(f, "0");

	/* Non-zero credit: ifset fires, output "+<amount>" */
	e->credit = AMOUNT_MSAT(100000000);
	s = format_event(tmpctx, f, REPORT_FMT_NONE, bkpr, e);
	assert(strstr(s, "+"));

	/* Zero credit: ifnotset fires, output "0" */
	e->credit = AMOUNT_MSAT(0);
	s = format_event(tmpctx, f, REPORT_FMT_NONE, bkpr, e);
	assert(streq(s, "0"));
}

/* {credit} with no conditionals: zero outputs "0" (ZERO_AMOUNT passthrough) */
static void test_format_event_zero_amount_no_conditionals(void)
{
	struct report_format *f = tal(tmpctx, struct report_format);
	struct income_event *e = tal(tmpctx, struct income_event);
	struct bkpr *bkpr = tal(tmpctx, struct bkpr);
	char *s;

	f->fmt = tal_arr(f, typeof(*f->fmt), 1);
	f->str = tal_arr(f, const char *, 1);
	f->ifset = tal_arr(f, struct report_format *, 1);
	f->ifnotset = tal_arr(f, struct report_format *, 1);
	f->fmt[0] = report_fmt_credit;
	f->str[0] = NULL;
	f->ifset[0] = NULL;
	f->ifnotset[0] = NULL;

	e->credit = AMOUNT_MSAT(0);
	s = format_event(tmpctx, f, REPORT_FMT_NONE, bkpr, e);
	assert(streq(s, "0"));
}

int main(int argc, char *argv[])
{
	common_setup(argv[0]);
	test_format_event_literal_only();
	test_format_event_simple_tag();
	test_format_event_fallback();
	test_format_event_nested_fallback();
	test_format_event_csv_escape_value();
	test_format_event_missing_no_fallback();
	test_format_event_ifset();
	test_format_event_zero_amount_no_conditionals();

	common_shutdown();
}
