#include "config.h"

#define RENEPAY_UNITTEST // logs are written in /tmp/debug.txt
#include "../payment.c"
#include "../flow.c"
#include "../uncertainty.c"
#include "../mcf.c"

#include <ccan/array_size/array_size.h>
#include <ccan/read_write_all/read_write_all.h>
#include <common/bigsize.h>
#include <common/channel_id.h>
#include <common/gossip_store.h>
#include <common/node_id.h>
#include <common/setup.h>
#include <common/wireaddr.h>
#include <stdio.h>
#include <wire/onion_wiregen.h>

/* AUTOGENERATED MOCKS START */
/* 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_finished */
struct command_result *command_finished(struct command *cmd UNNEEDED, struct json_stream *response)

{ fprintf(stderr, "command_finished called!\n"); abort(); }
/* Generated stub for disabledmap_add_channel */
void disabledmap_add_channel(struct disabledmap *p UNNEEDED,
			     struct short_channel_id_dir scidd UNNEEDED)
{ fprintf(stderr, "disabledmap_add_channel called!\n"); abort(); }
/* Generated stub for disabledmap_add_node */
void disabledmap_add_node(struct disabledmap *p UNNEEDED, struct node_id node UNNEEDED)
{ fprintf(stderr, "disabledmap_add_node called!\n"); abort(); }
/* Generated stub for disabledmap_channel_is_warned */
bool disabledmap_channel_is_warned(struct disabledmap *p UNNEEDED,
				   struct short_channel_id_dir scidd UNNEEDED)
{ fprintf(stderr, "disabledmap_channel_is_warned called!\n"); abort(); }
/* Generated stub for disabledmap_new */
struct disabledmap *disabledmap_new(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "disabledmap_new called!\n"); abort(); }
/* Generated stub for disabledmap_reset */
void disabledmap_reset(struct disabledmap *p UNNEEDED)
{ fprintf(stderr, "disabledmap_reset called!\n"); abort(); }
/* Generated stub for disabledmap_warn_channel */
void disabledmap_warn_channel(struct disabledmap *p UNNEEDED,
			      struct short_channel_id_dir scidd UNNEEDED)
{ fprintf(stderr, "disabledmap_warn_channel called!\n"); abort(); }
/* Generated stub for json_add_payment */
void json_add_payment(struct json_stream *s UNNEEDED, const struct payment *payment UNNEEDED)
{ fprintf(stderr, "json_add_payment 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 new_routetracker */
struct routetracker *new_routetracker(const tal_t *ctx UNNEEDED, struct payment *payment UNNEEDED)
{ fprintf(stderr, "new_routetracker called!\n"); abort(); }
/* Generated stub for pay_plugin */
struct pay_plugin *pay_plugin;
/* Generated stub for plugin_err */
void   plugin_err(struct plugin *p UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "plugin_err called!\n"); abort(); }
/* Generated stub for plugin_log */
void plugin_log(struct plugin *p UNNEEDED, enum log_level l UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "plugin_log called!\n"); abort(); }
/* Generated stub for plugin_notify_message */
void plugin_notify_message(struct command *cmd UNNEEDED,
			   enum log_level level UNNEEDED,
			   const char *fmt UNNEEDED, ...)

{ fprintf(stderr, "plugin_notify_message called!\n"); abort(); }
/* Generated stub for routetracker_cleanup */
void routetracker_cleanup(struct routetracker *routetracker UNNEEDED)
{ fprintf(stderr, "routetracker_cleanup called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

static u8 empty_map[] = {
	0
};

static const char* print_flows(
		const tal_t *ctx,
		const char *desc,
		const struct gossmap *gossmap,
		struct chan_extra_map* chan_extra_map,
		struct flow **flows)
{
	tal_t *this_ctx = tal(ctx,tal_t);
	double tot_prob =
	    flowset_probability(tmpctx, flows, gossmap, chan_extra_map, false, NULL);
	assert(tot_prob>=0);
	char *buff = tal_fmt(ctx,"%s: %zu subflows, prob %2lf\n", desc, tal_count(flows),tot_prob);
	for (size_t i = 0; i < tal_count(flows); i++) {
		struct amount_msat fee, delivered;
		tal_append_fmt(&buff,"   ");
		for (size_t j = 0; j < tal_count(flows[i]->path); j++) {
			struct short_channel_id scid
				= gossmap_chan_scid(gossmap,
						    flows[i]->path[j]);
			tal_append_fmt(&buff,"%s%s", j ? "->" : "",
			       fmt_short_channel_id(this_ctx, scid));
		}
		delivered = flows[i]->amount;
		if (!flow_fee(&fee, flows[i]))
		{
			abort();
		}
		tal_append_fmt(&buff," prob %.2f, %s delivered with fee %s\n",
		       flows[i]->success_prob,
		       fmt_amount_msat(this_ctx, delivered),
		       fmt_amount_msat(this_ctx, fee));
	}

	tal_free(this_ctx);
	return buff;
}

static void remove_file(char *fname) { assert(!remove(fname)); }

int main(int argc, char *argv[])
{
	int fd;
	char *gossfile;
	struct gossmap *gossmap;
	struct node_id l1, l2, l3, l4;
	struct short_channel_id scid12, scid13, scid24, scid34;
	struct gossmap_localmods *mods;
	struct chan_extra_map *chan_extra_map;

	char *errmsg;

	common_setup(argv[0]);

	fd = tmpdir_mkstemp(tmpctx, "run-not_mcf-diamond.XXXXXX", &gossfile);
	tal_add_destructor(gossfile, remove_file);
	assert(write_all(fd, empty_map, sizeof(empty_map)));

	gossmap = gossmap_load(tmpctx, gossfile, NULL, NULL);
	assert(gossmap);

	/* These are in ascending order, for easy direction setting */
	assert(node_id_from_hexstr("022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59", 66, &l1));
	assert(node_id_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518", 66, &l2));
	assert(node_id_from_hexstr("035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d", 66, &l3));
	assert(node_id_from_hexstr("0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199", 66, &l4));
	assert(short_channel_id_from_str("1x2x0", 5, &scid12));
	assert(short_channel_id_from_str("1x3x0", 5, &scid13));
	assert(short_channel_id_from_str("2x4x0", 5, &scid24));
	assert(short_channel_id_from_str("3x4x0", 5, &scid34));

	mods = gossmap_localmods_new(tmpctx);

	/* 1->2->4 has capacity 10k sat, 1->3->4 has capacity 5k sat (lower fee!) */
	assert(gossmap_local_addchan(mods, &l1, &l2, scid12, AMOUNT_MSAT(10000000), NULL));
	assert(gossmap_local_setchan(mods, scid12,
				     /*htlc_min=*/ AMOUNT_MSAT(0),
				     /*htlc_max=*/ AMOUNT_MSAT(10000000),
				     /*base_fee=*/ AMOUNT_MSAT(0),
				     /*ppm_fee =*/ 1001,
				     /* delay  =*/ 5,
				     /* enabled=*/ true,
				     /* dir    =*/ 0));
	assert(gossmap_local_addchan(mods, &l2, &l4, scid24, AMOUNT_MSAT(10000000), NULL));
	assert(gossmap_local_setchan(mods, scid24,
				     AMOUNT_MSAT(0),
				     AMOUNT_MSAT(10000000),
				     AMOUNT_MSAT(0), 1002, 5,
				     true,
				     0));
	assert(gossmap_local_addchan(mods, &l1, &l3, scid13, AMOUNT_MSAT(5000000), NULL));
	assert(gossmap_local_setchan(mods, scid13,
				     AMOUNT_MSAT(0),
				     AMOUNT_MSAT(5000000),
				     AMOUNT_MSAT(0), 503, 5,
				     true,
				     0));
	assert(gossmap_local_addchan(mods, &l3, &l4, scid34, AMOUNT_MSAT(5000000), NULL));
	assert(gossmap_local_setchan(mods, scid34,
				     AMOUNT_MSAT(0),
				     AMOUNT_MSAT(5000000),
				     AMOUNT_MSAT(0), 504, 5,
				     true,
				     0));

	gossmap_apply_localmods(gossmap, mods);
	chan_extra_map = tal(tmpctx, struct chan_extra_map);
	chan_extra_map_init(chan_extra_map);
	/* The local chans have no "capacity", so set them manually. */
	new_chan_extra(chan_extra_map,
			    scid12,
			    AMOUNT_MSAT(10000000));
	new_chan_extra(chan_extra_map,
			    scid24,
			    AMOUNT_MSAT(10000000));
	new_chan_extra(chan_extra_map,
			    scid13,
			    AMOUNT_MSAT(5000000));
	new_chan_extra(chan_extra_map,
			    scid34,
			    AMOUNT_MSAT(5000000));

	bitmap *disabled = tal_arrz(
	    tmpctx, bitmap, 2 * BITMAP_NWORDS(gossmap_max_chan_idx(gossmap)));

	struct flow **flows;
	flows = minflow(tmpctx, gossmap,
			gossmap_find_node(gossmap, &l1),
			gossmap_find_node(gossmap, &l4),
			chan_extra_map,
			disabled,
			/* Half the capacity */
			AMOUNT_MSAT(1000000), // 1000 sats
			 /* max_fee = */ AMOUNT_MSAT(10000), // 10 sats
			 /* min probability = */ 0.8, // 80%
			 /* base probability = */ 1.0,
			 /* delay fee factor = */ 0,
			 /* base fee penalty */ 0,
			 /* prob cost factor = */ 1,
			 &errmsg);
	if (!flows) {
		printf("Minflow has failed with: %s", errmsg);
		assert(0 && "minflow failed");
	}

	printf("%s\n",
		print_flows(tmpctx,"Simple minflow", gossmap,chan_extra_map, flows));

	common_shutdown();
}
