#include "config.h"
#include <ccan/asort/asort.h>
#include <ccan/bitmap/bitmap.h>
#include <common/amount.h>
#include <common/bolt11.h>
#include <common/clock_time.h>
#include <common/gossmods_listpeerchannels.h>
#include <common/json_stream.h>
#include <plugins/renepay/json.h>
#include <plugins/renepay/mcf.h>
#include <plugins/renepay/mods.h>
#include <plugins/renepay/payplugin.h>
#include <plugins/renepay/renepayconfig.h>
#include <plugins/renepay/route.h>
#include <plugins/renepay/routebuilder.h>
#include <plugins/renepay/routetracker.h>
#include <unistd.h>
#include <wire/bolt12_wiregen.h>

#define OP_NULL NULL
#define OP_CALL (void *)1
#define OP_IF (void *)2

void *payment_virtual_program[];

/* Advance the payment virtual machine */
struct command_result *payment_continue(struct payment *payment)
{
	assert(payment->exec_state != INVALID_STATE);
	void *op = payment_virtual_program[payment->exec_state++];

	if (op == OP_NULL) {
		plugin_err(pay_plugin->plugin,
			   "payment_continue reached the end of the virtual "
			   "machine execution.");
	} else if (op == OP_CALL) {
		const struct payment_modifier *mod =
		    (const struct payment_modifier *)
			payment_virtual_program[payment->exec_state++];

		if (mod == NULL)
			plugin_err(pay_plugin->plugin,
				   "payment_continue expected payment_modifier "
				   "but NULL found");

		plugin_log(pay_plugin->plugin, LOG_TRACE, "Calling modifier %s",
			   mod->name);
		return mod->step_cb(payment);
	} else if (op == OP_IF) {
		const struct payment_condition *cond =
		    (const struct payment_condition *)
			payment_virtual_program[payment->exec_state++];

		if (cond == NULL)
			plugin_err(pay_plugin->plugin,
				   "payment_continue expected pointer to "
				   "condition but NULL found");

		plugin_log(pay_plugin->plugin, LOG_TRACE,
			   "Calling payment condition %s", cond->name);

		const u64 position_iftrue =
			(intptr_t)payment_virtual_program[payment->exec_state++];

		if (cond->condition_cb(payment))
			payment->exec_state = position_iftrue;

		return payment_continue(payment);
	}
	plugin_err(pay_plugin->plugin, "payment_continue op code not defined");
	return NULL;
}


/* Generic handler for RPC failures that should end up failing the payment. */
static struct command_result *payment_rpc_failure(struct command *cmd,
						  const char *method UNUSED,
						  const char *buffer,
						  const jsmntok_t *toks,
						  struct payment *payment)
{
	const jsmntok_t *codetok = json_get_member(buffer, toks, "code");
	u32 errcode;
	if (codetok != NULL)
		json_to_u32(buffer, codetok, &errcode);
	else
		errcode = LIGHTNINGD;

	return payment_fail(
	    payment, errcode,
	    "Failing a partial payment due to a failed RPC call: %.*s",
	    json_tok_full_len(toks), json_tok_full(buffer, toks));
}

static void add_hintchan(struct payment *payment, const struct node_id *src,
			 const struct node_id *dst, u16 cltv_expiry_delta,
			 const struct short_channel_id scid, u32 fee_base_msat,
			 u32 fee_proportional_millionths,
			 const struct amount_msat *chan_htlc_min,
			 const struct amount_msat *chan_htlc_max);

/*****************************************************************************
 * previoussuccess
 *
 * Obtain a list of previous sendpay requests and check if
 * the current payment hash has already succeed.
 */

struct success_data {
	u64 parts, created_at, groupid;
	struct amount_msat deliver_msat, sent_msat;
	struct preimage preimage;
};

/* Extracts success data from listsendpays. */
static bool success_data_from_listsendpays(const char *buf,
					   const jsmntok_t *arr,
					   struct success_data *success)
{
	assert(success);

	size_t i;
	const char *err;
	const jsmntok_t *t;
	assert(arr && arr->type == JSMN_ARRAY);

	success->parts = 0;
	success->deliver_msat = AMOUNT_MSAT(0);
	success->sent_msat = AMOUNT_MSAT(0);

	json_for_each_arr(i, t, arr)
	{
		u64 groupid;
		struct amount_msat this_msat, this_sent;

		const jsmntok_t *status_tok = json_get_member(buf, t, "status");
		if (!status_tok)
			plugin_err(
			    pay_plugin->plugin,
			    "%s (line %d) missing status token from json.",
			    __func__, __LINE__);
		const char *status = json_strdup(tmpctx, buf, status_tok);
		if (!status)
			plugin_err(
			    pay_plugin->plugin,
			    "%s (line %d) failed to allocate status string.",
			    __func__, __LINE__);

		if (streq(status, "complete")) {
			/* FIXME we assume amount_msat is always present, but
			 * according to the documentation this field is
			 * optional. How do I interpret if amount_msat is
			 * missing? */
			err = json_scan(
			    tmpctx, buf, t,
			    "{groupid:%"
			    ",amount_msat:%"
			    ",amount_sent_msat:%"
			    ",created_at:%"
			    ",payment_preimage:%}",
			    JSON_SCAN(json_to_u64, &groupid),
			    JSON_SCAN(json_to_msat, &this_msat),
			    JSON_SCAN(json_to_msat, &this_sent),
			    JSON_SCAN(json_to_u64, &success->created_at),
			    JSON_SCAN(json_to_preimage, &success->preimage));

			if (err)
				plugin_err(pay_plugin->plugin,
					   "%s (line %d) json_scan of "
					   "listsendpay returns the "
					   "following error: %s",
					   __func__, __LINE__, err);
			success->groupid = groupid;
			/* Now we know the payment completed. */
			if (!amount_msat_add(&success->deliver_msat,
					     success->deliver_msat,
					     this_msat) ||
			    !amount_msat_add(&success->sent_msat,
					     success->sent_msat, this_sent))
				plugin_err(pay_plugin->plugin,
					   "%s (line %d) amount_msat overflow.",
					   __func__, __LINE__);

			success->parts++;
		}
	}

	return success->parts > 0;
}

static struct command_result *previoussuccess_done(struct command *cmd,
						   const char *method UNUSED,
						   const char *buf,
						   const jsmntok_t *result,
						   struct payment *payment)
{
	const jsmntok_t *arr = json_get_member(buf, result, "payments");
	if (!arr || arr->type != JSMN_ARRAY) {
		return payment_fail(
		    payment, LIGHTNINGD,
		    "Unexpected non-array result from listsendpays: %.*s",
		    json_tok_full_len(result), json_tok_full(buf, result));
	}

	struct success_data success;
	if (!success_data_from_listsendpays(buf, arr, &success)) {
		/* There are no success sendpays. */
		return payment_continue(payment);
	}

	payment->payment_info.start_time.ts.tv_sec = success.created_at;
	payment->payment_info.start_time.ts.tv_nsec = 0;
	payment->total_delivering = success.deliver_msat;
	payment->total_sent = success.sent_msat;
	payment->next_partid = success.parts + 1;
	payment->groupid = success.groupid;

	payment_note(payment, LOG_DBG,
		     "Payment completed by a previous sendpay.");
	return payment_success(payment, &success.preimage);
}

static struct command_result *previoussuccess_cb(struct payment *payment)
{
	struct command *cmd = payment_command(payment);
	assert(cmd);

	struct out_req *req = jsonrpc_request_start(
	    cmd, "listsendpays", previoussuccess_done,
	    payment_rpc_failure, payment);

	json_add_sha256(req->js, "payment_hash",
			&payment->payment_info.payment_hash);
	json_add_string(req->js, "status", "complete");
	return send_outreq(req);
}

REGISTER_PAYMENT_MODIFIER(previoussuccess, previoussuccess_cb);

/*****************************************************************************
 * initial_sanity_checks
 *
 * Some checks on a payment about to start.
 */
static struct command_result *initial_sanity_checks_cb(struct payment *payment)
{
	assert(amount_msat_is_zero(payment->total_sent));
	assert(amount_msat_is_zero(payment->total_delivering));
	assert(!payment->preimage);
	assert(tal_count(payment->cmd_array) == 1);

	return payment_continue(payment);
}

REGISTER_PAYMENT_MODIFIER(initial_sanity_checks, initial_sanity_checks_cb);

/*****************************************************************************
 * selfpay
 */

static struct command_result *selfpay_cb(struct payment *payment)
{
	/* A different approach to self-pay: create a fake channel from the
	 * bolt11 destination to the routing_destination (a fake node_id). */
	if (!payment->payment_info.blinded_paths) {
		struct amount_msat htlc_min = AMOUNT_MSAT(0);
		struct amount_msat htlc_max = AMOUNT_MSAT((u64)1000*100000000);
		struct short_channel_id scid = {.u64 = 0};
		add_hintchan(payment, &payment->payment_info.destination,
			     payment->routing_destination,
			     /* cltv delta = */ 0, scid,
			     /* base fee = */ 0,
			     /* ppm = */ 0, &htlc_min, &htlc_max);
	}
	return payment_continue(payment);
}

REGISTER_PAYMENT_MODIFIER(selfpay, selfpay_cb);

/*****************************************************************************
 * getmychannels
 *
 * Calls listpeerchannels to get and updated state of the local channels.
 */

static void
uncertainty_update_from_listpeerchannels(struct uncertainty *uncertainty,
				      const struct short_channel_id_dir *scidd,
				      struct amount_msat max, bool enabled,
				      const char *buf, const jsmntok_t *chantok)
{
	if (!enabled)
		return;

	struct amount_msat capacity, min, gap;
	const char *errmsg = json_scan(tmpctx, buf, chantok, "{total_msat:%}",
				       JSON_SCAN(json_to_msat, &capacity));
	if (errmsg)
		goto error;

	if (!uncertainty_add_channel(pay_plugin->uncertainty, scidd->scid,
				  capacity)) {
		errmsg = tal_fmt(
		    tmpctx,
		    "Unable to find/add scid=%s in the uncertainty network",
		    fmt_short_channel_id(tmpctx, scidd->scid));
		goto error;
	}

	if (!amount_msat_scale(&gap, capacity, 0.1) ||
	    !amount_msat_sub(&min, max, gap))
		min = AMOUNT_MSAT(0);

	// FIXME this does not include pending HTLC of ongoing payments!
	/* Allow a gap between min and max so that we don't use up all of our
	 * channels' spendable sats and avoid our local error:
	 * WIRE_TEMPORARY_CHANNEL_FAILURE: Capacity exceeded - HTLC fee: Xsat
	 *
	 * */
	if (!uncertainty_set_liquidity(pay_plugin->uncertainty, scidd, min,
				       max)) {
		errmsg = tal_fmt(
		    tmpctx,
		    "Unable to set liquidity to channel scidd=%s in the "
		    "uncertainty network.",
		    fmt_short_channel_id_dir(tmpctx, scidd));
		goto error;
	}
	return;

error:
	plugin_log(
	    pay_plugin->plugin, LOG_UNUSUAL,
	    "Failed to update local channel %s from listpeerchannels rpc: %s",
	    fmt_short_channel_id(tmpctx, scidd->scid),
	    errmsg);
}

static void gossmod_cb(struct gossmap_localmods *mods,
		       const struct node_id *self,
		       const struct node_id *peer,
		       const struct short_channel_id_dir *scidd,
		       struct amount_msat capacity_msat,
		       struct amount_msat htlcmin,
		       struct amount_msat htlcmax,
		       struct amount_msat spendable,
		       struct amount_msat max_total_htlc,
		       struct amount_msat fee_base,
		       u32 fee_proportional,
		       u16 cltv_delta,
		       bool enabled,
		       const char *buf,
		       const jsmntok_t *chantok,
		       struct payment *payment)
{
	struct amount_msat min, max;

	if (scidd->dir == node_id_idx(self, peer)) {
		/* local channels can send up to what's spendable but there is a
		 * limit also the total amount in-flight */
		min = AMOUNT_MSAT(0);
		max = amount_msat_min(spendable, max_total_htlc);
	} else {
		/* remote channels can send up no more than spendable */
		min = htlcmin;
		max = amount_msat_min(spendable, htlcmax);
	}

	/* FIXME: features? */
	gossmap_local_addchan(mods, self, peer, scidd->scid, capacity_msat,
			      NULL);
	gossmap_local_updatechan(mods, scidd,
				 &enabled,
				 &min, &max,
				 &fee_base, &fee_proportional, &cltv_delta);

	/* Is it disabled? */
	if (!enabled)
		payment_disable_chan(payment, *scidd, LOG_DBG,
				     "listpeerchannels says not enabled");

	/* Also update the uncertainty network by fixing the liquidity of the
	 * outgoing channel. If we try to set the liquidity of the incoming
	 * channel as well we would have conflicting information because our
	 * knowledge model does not take into account channel reserves. */
	if (scidd->dir == node_id_idx(self, peer))
		uncertainty_update_from_listpeerchannels(
		    pay_plugin->uncertainty, scidd, max, enabled, buf, chantok);
}

static struct command_result *getmychannels_done(struct command *cmd,
						 const char *method UNUSED,
						 const char *buf,
						 const jsmntok_t *result,
						 struct payment *payment)
{
	// FIXME: should local gossmods be global (ie. member of pay_plugin) or
	// local (ie. member of payment)?
	payment->local_gossmods = gossmods_from_listpeerchannels(
	    payment, &pay_plugin->my_id, buf, result, /* zero_rates = */ true,
	    gossmod_cb, payment);

	return payment_continue(payment);
}

static struct command_result *getmychannels_cb(struct payment *payment)
{
	struct command *cmd = payment_command(payment);
	if (!cmd)
		plugin_err(pay_plugin->plugin,
			   "getmychannels_pay_mod: cannot get a valid cmd.");

	struct out_req *req = jsonrpc_request_start(
	    cmd, "listpeerchannels", getmychannels_done,
	    payment_rpc_failure, payment);
	return send_outreq(req);
}

REGISTER_PAYMENT_MODIFIER(getmychannels, getmychannels_cb);

/*****************************************************************************
 * refreshgossmap
 *
 * Update the gossmap.
 */

static struct command_result *refreshgossmap_cb(struct payment *payment)
{
	assert(pay_plugin->gossmap); // gossmap must be already initialized
	assert(payment);
	assert(payment->local_gossmods);

	bool gossmap_changed = gossmap_refresh(pay_plugin->gossmap);

	if (gossmap_changed) {
		gossmap_apply_localmods(pay_plugin->gossmap,
					payment->local_gossmods);
		int skipped_count = uncertainty_update(pay_plugin->uncertainty,
						       pay_plugin->gossmap);
		gossmap_remove_localmods(pay_plugin->gossmap,
					 payment->local_gossmods);
		if (skipped_count)
			plugin_log(
			    pay_plugin->plugin, LOG_UNUSUAL,
			    "%s: uncertainty was updated but %d channels have "
			    "been ignored.",
			    __func__, skipped_count);
	}
	return payment_continue(payment);
}

REGISTER_PAYMENT_MODIFIER(refreshgossmap, refreshgossmap_cb);

/*****************************************************************************
 * routehints
 *
 * Use route hints from the invoice to update the local gossmods and uncertainty
 * network.
 */

static void uncertainty_remove_channel(struct chan_extra *ce,
				       struct uncertainty *uncertainty)
{
	chan_extra_map_del(uncertainty->chan_extra_map, ce);
}

static void add_hintchan(struct payment *payment, const struct node_id *src,
			 const struct node_id *dst, u16 cltv_expiry_delta,
			 const struct short_channel_id scid, u32 fee_base_msat,
			 u32 fee_proportional_millionths,
			 const struct amount_msat *chan_htlc_min,
			 const struct amount_msat *chan_htlc_max)
{
	assert(payment);
	assert(payment->local_gossmods);

	const char *errmsg;
	struct chan_extra *ce =
	    uncertainty_find_channel(pay_plugin->uncertainty, scid);

	if (!ce) {
		struct short_channel_id_dir scidd;
		/* We assume any HTLC is allowed */
		struct amount_msat htlc_min = AMOUNT_MSAT(0), htlc_max = MAX_CAPACITY;

		if (chan_htlc_min)
			htlc_min = *chan_htlc_min;
		if (chan_htlc_max)
			htlc_max = *chan_htlc_max;

		struct amount_msat fee_base = amount_msat(fee_base_msat);
		bool enabled = true;
		scidd.scid = scid;
		scidd.dir = node_id_idx(src, dst);

		/* This channel is not public, we don't know his capacity
		 One possible solution is set the capacity to
		 MAX_CAP and the state to [0,MAX_CAP]. Alternatively we could
		 the capacity to amount and state to [amount,amount], but that
		 wouldn't work if the recepient provides more than one hints
		 telling us to partition the payment in multiple routes. */
		ce = uncertainty_add_channel(pay_plugin->uncertainty, scid,
					  MAX_CAPACITY);
		if (!ce) {
			errmsg = tal_fmt(tmpctx,
					 "Unable to find/add scid=%s in the "
					 "local uncertainty network",
					 fmt_short_channel_id(tmpctx, scid));
			goto function_error;
		}
		/* FIXME: features? */
		if (!gossmap_local_addchan(payment->local_gossmods, src, dst,
					   scid, MAX_CAPACITY, NULL) ||
		    !gossmap_local_updatechan(
			payment->local_gossmods, &scidd,
			&enabled, &htlc_min, &htlc_max,
			&fee_base, &fee_proportional_millionths,
			&cltv_expiry_delta)) {
			errmsg = tal_fmt(
			    tmpctx,
			    "Failed to update scid=%s in the local_gossmods.",
			    fmt_short_channel_id(tmpctx, scid));
			goto function_error;
		}
		/* We want these channel hints destroyed when the local_gossmods
		 * are freed. */
		/* FIXME: these hints are global in the uncertainty network if
		 * two payments happen concurrently we will have race
		 * conditions. The best way to avoid this is to use askrene and
		 * it's layered API. */
		tal_steal(payment->local_gossmods, ce);
		tal_add_destructor2(ce, uncertainty_remove_channel,
				    pay_plugin->uncertainty);
	} else {
		/* The channel is pubic and we already keep track of it in the
		 * gossmap and uncertainty network. It would be wrong to assume
		 * that this channel has sufficient capacity to forward the
		 * entire payment! Doing so leads to knowledge updates in which
		 * the known min liquidity is greater than the channel's
		 * capacity. */
	}

	return;

function_error:
	plugin_log(pay_plugin->plugin, LOG_UNUSUAL,
		   "Failed to update hint channel %s: %s",
		   fmt_short_channel_id(tmpctx, scid),
		   errmsg);
}

static struct command_result *routehints_done(struct command *cmd UNUSED,
					      const char *method UNUSED,
					      const char *buf UNUSED,
					      const jsmntok_t *result UNUSED,
					      struct payment *payment)
{
	// FIXME are there route hints for B12?
	assert(payment);
	assert(payment->local_gossmods);

	const struct node_id *destination = &payment->payment_info.destination;
	struct route_info **routehints = payment->payment_info.routehints;
	assert(routehints);
	const size_t nhints = tal_count(routehints);
	/* Hints are added to the local_gossmods. */
	for (size_t i = 0; i < nhints; i++) {
		/* Each one, presumably, leads to the destination */
		const struct route_info *r = routehints[i];
		const struct node_id *end = destination;

		for (int j = tal_count(r) - 1; j >= 0; j--) {
			add_hintchan(payment, &r[j].pubkey, end,
				     r[j].cltv_expiry_delta,
				     r[j].short_channel_id, r[j].fee_base_msat,
				     r[j].fee_proportional_millionths,
				     NULL, NULL);
			end = &r[j].pubkey;
		}
	}

	/* Add hints to the uncertainty network. */
	gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods);
	int skipped_count =
	    uncertainty_update(pay_plugin->uncertainty, pay_plugin->gossmap);
	gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods);
	if (skipped_count)
		plugin_log(pay_plugin->plugin, LOG_UNUSUAL,
			   "%s: uncertainty was updated but %d channels have "
			   "been ignored.",
			   __func__, skipped_count);

	return payment_continue(payment);
}

static struct command_result *routehints_cb(struct payment *payment)
{
	if (payment->payment_info.routehints == NULL)
		return payment_continue(payment);
	struct command *cmd = payment_command(payment);
	assert(cmd);
	struct out_req *req = jsonrpc_request_start(
	    cmd, "waitblockheight", routehints_done,
	    payment_rpc_failure, payment);
	json_add_num(req->js, "blockheight", 0);
	return send_outreq(req);
}

REGISTER_PAYMENT_MODIFIER(routehints, routehints_cb);


/*****************************************************************************
 * blindedhints
 *
 * Similar to routehints but for bolt12 invoices: create fake channel that
 * connect the blinded path entry point to the destination node.
 */

static struct command_result *blindedhints_cb(struct payment *payment)
{
	if (payment->payment_info.blinded_paths == NULL)
		return payment_continue(payment);

	struct payment_info *pinfo = &payment->payment_info;
	struct short_channel_id scid;
	struct node_id src;

	for (size_t i = 0; i < tal_count(pinfo->blinded_paths); i++) {
		const struct blinded_payinfo *payinfo =
		    pinfo->blinded_payinfos[i];
		const struct blinded_path *path = pinfo->blinded_paths[i];

		scid.u64 = i; // a fake scid
		node_id_from_pubkey(&src, &path->first_node_id.pubkey);

		add_hintchan(payment, &src, payment->routing_destination,
			     payinfo->cltv_expiry_delta, scid,
			     payinfo->fee_base_msat,
			     payinfo->fee_proportional_millionths,
			     &payinfo->htlc_minimum_msat,
			     &payinfo->htlc_maximum_msat);
	}
	return payment_continue(payment);
}

REGISTER_PAYMENT_MODIFIER(blindedhints, blindedhints_cb);


/*****************************************************************************
 * compute_routes
 *
 * Compute the payment routes.
 */

static struct command_result *compute_routes_cb(struct payment *payment)
{
	assert(payment->status == PAYMENT_PENDING);
	struct routetracker *routetracker = payment->routetracker;
	assert(routetracker);

	if (routetracker->computed_routes &&
	    tal_count(routetracker->computed_routes))
		plugin_err(pay_plugin->plugin,
			   "%s: no previously computed routes expected.",
			   __func__);

	struct amount_msat feebudget, fees_spent, remaining;

	/* Total feebudget  */
	if (!amount_msat_sub(&feebudget, payment->payment_info.maxspend,
			     payment->payment_info.amount))
		plugin_err(pay_plugin->plugin, "%s: fee budget is negative?",
			   __func__);

	/* Fees spent so far */
	if (!amount_msat_sub(&fees_spent, payment->total_sent,
			     payment->total_delivering))
		plugin_err(pay_plugin->plugin,
			   "%s: total_delivering is greater than total_sent?",
			   __func__);

	/* Remaining fee budget. */
	if (!amount_msat_deduct(&feebudget, fees_spent))
		feebudget = AMOUNT_MSAT(0);

	/* How much are we still trying to send? */
	if (!amount_msat_sub(&remaining, payment->payment_info.amount,
			     payment->total_delivering) ||
	    amount_msat_is_zero(remaining)) {
		plugin_log(pay_plugin->plugin, LOG_UNUSUAL,
			   "%s: Payment is pending with full amount already "
			   "committed. We skip the computation of new routes.",
			   __func__);
		return payment_continue(payment);
	}

	enum jsonrpc_errcode errcode;
	const char *err_msg = NULL;

	gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods);

	/* get_routes returns the answer, we assign it to the computed_routes,
	 * that's why we need to tal_free the older array. Maybe it would be
	 * better to pass computed_routes as a reference? */
	routetracker->computed_routes = tal_free(routetracker->computed_routes);

	/* Send get_routes a note that it should discard the last hop because we
	 * are actually solving a multiple destinations problem. */
	bool blinded_destination = true;

	// TODO: add an algorithm selector here
	/* We let this return an unlikely path, as it's better to try  once than
	 * simply refuse.  Plus, models are not truth! */
	routetracker->computed_routes = get_routes(
					    routetracker,
					    &payment->payment_info,
					    &pay_plugin->my_id,
					    payment->routing_destination,
					    pay_plugin->gossmap,
					    pay_plugin->uncertainty,
					    payment->disabledmap,
					    remaining,
					    feebudget,
					    &payment->next_partid,
					    payment->groupid,
					    blinded_destination,
					    &errcode,
					    &err_msg);

	/* Otherwise the error message remains a child of the routetracker. */
	err_msg = tal_steal(tmpctx, err_msg);

	gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods);

	/* Couldn't feasible route, we stop. */
	if (!routetracker->computed_routes ||
	    tal_count(routetracker->computed_routes) == 0) {
		if (err_msg == NULL)
			err_msg = tal_fmt(
			    tmpctx, "get_routes returned NULL error message");
		return payment_fail(payment, errcode, "%s", err_msg);
	}

	return payment_continue(payment);
}

REGISTER_PAYMENT_MODIFIER(compute_routes, compute_routes_cb);

/*****************************************************************************
 * send_routes
 *
 * This payment modifier takes the payment routes and starts the payment
 * request calling sendpay.
 */

static struct command_result *waitblockheight_done(struct command *cmd,
						   const char *method UNUSED,
						   const char *buf,
						   const jsmntok_t *result,
						   struct payment *payment)
{
	const char *err;
	struct command *aux_cmd;
	struct route *route;
	struct routetracker *routetracker;

	err = json_scan(tmpctx, buf, result, "{blockheight:%}",
			JSON_SCAN(json_to_u32, &payment->blockheight));
	payment->blockheight += 1;

	if (err) {
		plugin_err(pay_plugin->plugin,
			   "Failed to read blockheight from waitblockheight "
			   "response: %s",
			   err);
		return payment_continue(payment);
	}

	routetracker = payment->routetracker;
	assert(routetracker);
	if (!routetracker->computed_routes ||
	    tal_count(routetracker->computed_routes) == 0) {
		plugin_log(pay_plugin->plugin, LOG_UNUSUAL,
			   "%s: there are no routes to send, skipping.",
			   __func__);
		return payment_continue(payment);
	}
	for (size_t i = 0; i < tal_count(routetracker->computed_routes); i++) {
		aux_cmd = aux_command(cmd);
		route = routetracker->computed_routes[i];

		route_sendpay_request(aux_cmd, take(route), payment);

		payment_note(payment, LOG_INFORM,
			     "Sent route request: partid=%" PRIu64
			     " amount=%s prob=%.3lf fees=%s delay=%u path=%s",
			     route->key.partid,
			     fmt_amount_msat(tmpctx, route_delivers(route)),
			     route->success_prob,
			     fmt_amount_msat(tmpctx, route_fees(route)),
			     route_delay(route), fmt_route_path(tmpctx, route));
	}
	tal_resize(&routetracker->computed_routes, 0);
	return payment_continue(payment);
}

static struct command_result *send_routes_cb(struct payment *payment)
{
	struct command *cmd;
	struct out_req *req;
	assert(payment);
	cmd = payment_command(payment);
	if (!cmd)
		plugin_err(pay_plugin->plugin,
			   "send_routes_pay_mod: cannot get a valid cmd.");
	req =
	    jsonrpc_request_start(cmd, "waitblockheight", waitblockheight_done,
				  payment_rpc_failure, payment);
	json_add_num(req->js, "blockheight", 0);
	return send_outreq(req);
}

REGISTER_PAYMENT_MODIFIER(send_routes, send_routes_cb);

/*****************************************************************************
 * sleep
 *
 * The payment main thread sleeps for some time.
 */

static struct command_result *sleep_done(struct command *cmd, struct payment *payment)
{
	struct command_result *ret;
	payment->waitresult_timer = NULL;
	ret = timer_complete(cmd);
	payment_continue(payment);
	return ret;
}

static struct command_result *sleep_cb(struct payment *payment)
{
	struct command *cmd = payment_command(payment);
	assert(cmd);
	assert(payment->waitresult_timer == NULL);
	payment->waitresult_timer
		= command_timer(cmd,
				time_from_msec(COLLECTOR_TIME_WINDOW_MSEC),
				sleep_done, payment);
	return command_still_pending(cmd);
}

REGISTER_PAYMENT_MODIFIER(sleep, sleep_cb);

/*****************************************************************************
 * collect_results
 */

static struct command_result *collect_results_cb(struct payment *payment)
{
	assert(payment);
	payment->have_results = false;
	payment->retry = false;

	/* pending sendpay callbacks should be zero */
	if (!routetracker_have_results(payment->routetracker))
		return payment_continue(payment);

	/* all sendpays have been sent, look for success */
	struct preimage *payment_preimage = NULL;
	enum jsonrpc_errcode final_error = LIGHTNINGD;
	const char *final_msg = NULL;

	payment_collect_results(payment, &payment_preimage, &final_error, &final_msg);

	if (payment_preimage) {
		/* If we have the preimage that means one succeed, we
		 * inmediately finish the payment. */
		if (!amount_msat_greater_eq(payment->total_delivering,
					    payment->payment_info.amount)) {
			plugin_log(
			    pay_plugin->plugin, LOG_UNUSUAL,
			    "%s: received a success sendpay for this "
			    "payment but the total delivering amount %s "
			    "is less than the payment amount %s.",
			    __func__,
			    fmt_amount_msat(tmpctx, payment->total_delivering),
			    fmt_amount_msat(tmpctx,
					    payment->payment_info.amount));
		}
		return payment_success(payment, take(payment_preimage));
	}
	if (final_msg) {
		/* We received a sendpay result with a final error message, we
		 * inmediately finish the payment. */
		return payment_fail(payment, final_error, "%s", final_msg);
	}

	if (amount_msat_greater_eq(payment->total_delivering,
				   payment->payment_info.amount)) {
		/* There are no succeeds but we are still pending delivering the
		 * entire payment. We still need to collect more results. */
		payment->have_results = false;
		payment->retry = false;
	} else {
		/* We have some failures so that now we are short of
		 * total_delivering, we may retry. */
		payment->have_results = true;

		// FIXME: we seem to always retry here if we don't fail
		// inmediately. But I am going to leave this variable here,
		// cause we might decide in the future to put some conditions on
		// retries, like a maximum number of retries.
		payment->retry = true;
	}

	return payment_continue(payment);
}

REGISTER_PAYMENT_MODIFIER(collect_results, collect_results_cb);

/*****************************************************************************
 * end
 *
 * The default ending of a payment.
 */
static struct command_result *end_done(struct command *cmd UNUSED,
				       const char *method UNUSED,
				       const char *buf UNUSED,
				       const jsmntok_t *result UNUSED,
				       struct payment *payment)
{
	return payment_fail(payment, PAY_STOPPED_RETRYING,
			    "Payment execution ended without success.");
}
static struct command_result *end_cb(struct payment *payment)
{
	struct command *cmd = payment_command(payment);
	assert(cmd);
	struct out_req *req =
	    jsonrpc_request_start(cmd, "waitblockheight", end_done,
				  payment_rpc_failure, payment);
	json_add_num(req->js, "blockheight", 0);
	return send_outreq(req);
}

REGISTER_PAYMENT_MODIFIER(end, end_cb);

/*****************************************************************************
 * checktimeout
 *
 * Fail the payment if we have exceeded the timeout.
 */

static struct command_result *checktimeout_cb(struct payment *payment)
{
	if (time_after(clock_time(), payment->payment_info.stop_time)) {
		return payment_fail(payment, PAY_STOPPED_RETRYING, "Timed out");
	}
	return payment_continue(payment);
}

REGISTER_PAYMENT_MODIFIER(checktimeout, checktimeout_cb);

/*****************************************************************************
 * pendingsendpays
 *
 * Obtain a list of sendpays, add up the amount of those pending and decide
 * which groupid and partid we should use next. If there is a "complete" sendpay
 * we should return payment_success inmediately.
 */

static int cmp_u64(const u64 *a, const u64 *b, void *unused)
{
	if (*a < *b)
		return -1;
	if (*a > *b)
		return 1;
	return 0;
}

static struct command_result *pendingsendpays_done(struct command *cmd,
						   const char *method UNUSED,
						   const char *buf,
						   const jsmntok_t *result,
						   struct payment *payment)
{
	size_t i;
	const char *err;
	const jsmntok_t *t, *arr;

	/* Data for pending payments, this will be the one
	 * who's result gets replayed if we end up suspending. */
	bool has_pending = false;
	u64 unused_groupid;
	u64 pending_group_id COMPILER_WANTS_INIT("12.3.0-17ubuntu1 -O3");
	u64 max_pending_partid = 0;
	struct amount_msat pending_sent = AMOUNT_MSAT(0),
			   pending_msat = AMOUNT_MSAT(0);

	arr = json_get_member(buf, result, "payments");
	if (!arr || arr->type != JSMN_ARRAY) {
		return payment_fail(
		    payment, LIGHTNINGD,
		    "Unexpected non-array result from listsendpays: %.*s",
		    json_tok_full_len(result), json_tok_full(buf, result));
	}

	struct success_data success;
	if (success_data_from_listsendpays(buf, arr, &success)) {
		/* Have success data, hence the payment is complete, we stop. */
		payment->payment_info.start_time.ts.tv_sec = success.created_at;
		payment->payment_info.start_time.ts.tv_nsec = 0;
		payment->total_delivering = success.deliver_msat;
		payment->total_sent = success.sent_msat;
		payment->next_partid = success.parts + 1;
		payment->groupid = success.groupid;

		payment_note(payment, LOG_DBG,
			     "%s: Payment completed before computing the next "
			     "round of routes.",
			     __func__);
		return payment_success(payment, &success.preimage);
	}

	u64 *groupid_arr = tal_arr(tmpctx, u64, 0);

	// find if there is one pending group
	json_for_each_arr(i, t, arr)
	{
		u64 groupid;
		const char *status;

		err = json_scan(tmpctx, buf, t,
				"{status:%"
				",groupid:%}",
				JSON_SCAN_TAL(tmpctx, json_strdup, &status),
				JSON_SCAN(json_to_u64, &groupid));

		if (err)
			plugin_err(pay_plugin->plugin,
				   "%s json_scan of listsendpay returns the "
				   "following error: %s",
				   __func__, err);

		if (streq(status, "pending")) {
			has_pending = true;
			pending_group_id = groupid;
		}
		tal_arr_expand(&groupid_arr, groupid);
	}
	assert(tal_count(groupid_arr) == arr->size);

	/* We need two loops to get the highest partid for a groupid that has
	 * pending sendpays. */
	json_for_each_arr(i, t, arr)
	{
		u64 partid = 0, groupid;
		struct amount_msat this_msat, this_sent;
		const char *status;

		// FIXME we assume amount_msat is always present, but according
		// to the documentation this field is optional. How do I
		// interpret if amount_msat is missing?
		err = json_scan(tmpctx, buf, t,
				"{status:%"
				",partid?:%"
				",groupid:%"
				",amount_msat:%"
				",amount_sent_msat:%}",
				JSON_SCAN_TAL(tmpctx, json_strdup, &status),
				JSON_SCAN(json_to_u64, &partid),
				JSON_SCAN(json_to_u64, &groupid),
				JSON_SCAN(json_to_msat, &this_msat),
				JSON_SCAN(json_to_msat, &this_sent));

		if (err)
			plugin_err(pay_plugin->plugin,
				   "%s json_scan of listsendpay returns the "
				   "following error: %s",
				   __func__, err);

		if (has_pending && groupid == pending_group_id &&
		    partid > max_pending_partid)
			max_pending_partid = partid;

		/* status could be completed, pending or failed */
		if (streq(status, "pending")) {
			/* If we have more than one pending group, something
			 * went wrong! */
			if (groupid != pending_group_id)
				return payment_fail(
				    payment, PAY_STATUS_UNEXPECTED,
				    "Multiple pending groups for this "
				    "payment.");

			if (!amount_msat_add(&pending_msat, pending_msat,
					     this_msat) ||
			    !amount_msat_add(&pending_sent, pending_sent,
					     this_sent))
				plugin_err(pay_plugin->plugin,
					   "%s (line %d) amount_msat overflow.",
					   __func__, __LINE__);
		}
		assert(!streq(status, "complete"));
	}

	/* find the first unused groupid */
	unused_groupid = 1;
	asort(groupid_arr, tal_count(groupid_arr), cmp_u64, NULL);
	for (i = 0; i < tal_count(groupid_arr); i++) {
		if (unused_groupid < groupid_arr[i])
			break;
		if (unused_groupid == groupid_arr[i])
			unused_groupid++;
	}

	if (has_pending) {
		/* Continue where we left off? */
		payment->groupid = pending_group_id;
		payment->next_partid = max_pending_partid + 1;
		payment->total_sent = pending_sent;
		payment->total_delivering = pending_msat;

		plugin_log(pay_plugin->plugin, LOG_DBG,
			   "There are pending sendpays to this invoice. "
			   "groupid = %" PRIu64 " "
			   "delivering = %s, "
			   "last_partid = %" PRIu64,
			   pending_group_id,
			   fmt_amount_msat(tmpctx, payment->total_delivering),
			   max_pending_partid);
	} else {
		/* There are no pending nor completed sendpays, get me the last
		 * sendpay group. */
		payment->groupid = unused_groupid;
		payment->next_partid = 1;
		payment->total_sent = AMOUNT_MSAT(0);
		payment->total_delivering = AMOUNT_MSAT(0);
	}

	return payment_continue(payment);
}

static struct command_result *pendingsendpays_cb(struct payment *payment)
{
	struct command *cmd = payment_command(payment);
	assert(cmd);

	struct out_req *req = jsonrpc_request_start(
	    cmd, "listsendpays", pendingsendpays_done,
	    payment_rpc_failure, payment);

	json_add_sha256(req->js, "payment_hash",
			&payment->payment_info.payment_hash);
	return send_outreq(req);
}

REGISTER_PAYMENT_MODIFIER(pendingsendpays, pendingsendpays_cb);

/*****************************************************************************
 * knowledgerelax
 *
 * Reduce the knowledge of the network as time goes by.
 */

static struct command_result *knowledgerelax_cb(struct payment *payment)
{
	const u64 now_sec = clock_time().ts.tv_sec;
	enum renepay_errorcode err = uncertainty_relax(
	    pay_plugin->uncertainty, now_sec - pay_plugin->last_time);
	if (err)
		plugin_err(pay_plugin->plugin,
			   "uncertainty_relax failed with error %s",
			   renepay_errorcode_name(err));
	pay_plugin->last_time = now_sec;
	return payment_continue(payment);
}

REGISTER_PAYMENT_MODIFIER(knowledgerelax, knowledgerelax_cb);

/*****************************************************************************
 * channelfilter
 *
 * Disable some channels. The possible motivations are:
 * - avoid the overhead of unproductive routes that go through channels with
 * very low max_htlc that would lead us to a payment partition with too
 * many HTCLs,
 * - avoid channels with very small capacity as well, for which the probability
 * of success is always small anyways,
 * - discard channels with very high base fee that would break our cost
 * estimation,
 * - avoid high latency tor nodes.
 * All combined should reduce the size of the network we explore hopefully
 * reducing the runtime of the MCF solver (FIXME: I should measure this
 * eventually).
 * FIXME: shall we set these threshold parameters as plugin options?
 */

static struct command_result *channelfilter_cb(struct payment *payment)
{
	assert(payment);
	assert(pay_plugin->gossmap);
	const double HTLC_MAX_FRACTION = 0.01; // 1%
	const u64 HTLC_MAX_STOP_MSAT = 1000000000; // 1M sats

	u64 disabled_count = 0;


	u64 htlc_max_threshold = HTLC_MAX_FRACTION * payment->payment_info
		.amount.millisatoshis; /* Raw: a fraction of this amount. */
	/* Don't exclude channels with htlc_max above HTLC_MAX_STOP_MSAT even if
	 * that represents a fraction of the payment smaller than
	 * HTLC_MAX_FRACTION. */
	htlc_max_threshold = MIN(htlc_max_threshold, HTLC_MAX_STOP_MSAT);

	gossmap_apply_localmods(pay_plugin->gossmap, payment->local_gossmods);
	for (const struct gossmap_node *node =
		 gossmap_first_node(pay_plugin->gossmap);
	     node; node = gossmap_next_node(pay_plugin->gossmap, node)) {
		for (size_t i = 0; i < node->num_chans; i++) {
			int dir;
			const struct gossmap_chan *chan = gossmap_nth_chan(
			    pay_plugin->gossmap, node, i, &dir);
			const u64 htlc_max =
			    fp16_to_u64(chan->half[dir].htlc_max);
			if (htlc_max < htlc_max_threshold) {
				struct short_channel_id_dir scidd = {
				    .scid = gossmap_chan_scid(
					pay_plugin->gossmap, chan),
				    .dir = dir};
				disabledmap_add_channel(payment->disabledmap,
							scidd);
				disabled_count++;
			}
		}
	}
	gossmap_remove_localmods(pay_plugin->gossmap, payment->local_gossmods);
	// FIXME: prune the network over other parameters, eg. capacity,
	// fees, ...
	plugin_log(pay_plugin->plugin, LOG_DBG,
		   "channelfilter: disabling %" PRIu64 " channels.",
		   disabled_count);
	return payment_continue(payment);
}

REGISTER_PAYMENT_MODIFIER(channelfilter, channelfilter_cb);

/*****************************************************************************
 * alwaystrue
 *
 * A funny payment condition that always returns true.
 */
static bool alwaystrue_cb(const struct payment *payment) { return true; }

REGISTER_PAYMENT_CONDITION(alwaystrue, alwaystrue_cb);

/*****************************************************************************
 * nothaveresults
 *
 * A payment condition that returns true if the payment has not yet
 * collected enough results to decide whether the payment has succeed,
 * failed or need retrying.
 */
static bool nothaveresults_cb(const struct payment *payment)
{
	return !payment->have_results;
}

REGISTER_PAYMENT_CONDITION(nothaveresults, nothaveresults_cb);

/*****************************************************************************
 * retry
 *
 * A payment condition that returns true if we should retry the payment.
 */
static bool retry_cb(const struct payment *payment) { return payment->retry; }

REGISTER_PAYMENT_CONDITION(retry, retry_cb);

/*****************************************************************************
 * Virtual machine
 *
 * The plugin API is based on function calls. This makes is difficult to
 * summarize all payment steps into one function, because the workflow
 * is distributed across multiple functions. The default pay plugin
 * implements a "state machine" for each payment attempt/part and that
 * improves a lot the code readability and modularity. Based on that
 * idea renepay has its own state machine for the whole payment. We go
 * one step further by adding not just function calls (or payment
 * modifiers with OP_CALL) but also conditions with OP_IF that allows
 * for instance to have loops. Renepay's "program" is nicely summarized
 * in the following set of instructions:
 */
// TODO
// add shadow route
// add check pre-approved invoice
void *payment_virtual_program[] = {
    /*0*/ OP_CALL, &previoussuccess_pay_mod,
    /*2*/ OP_CALL, &knowledgerelax_pay_mod,
    /*4*/ OP_CALL, &getmychannels_pay_mod,
    /*6*/ OP_CALL, &selfpay_pay_mod,
    /*8*/ OP_CALL, &refreshgossmap_pay_mod,
    /*10*/ OP_CALL, &routehints_pay_mod,
    /*12*/ OP_CALL, &blindedhints_pay_mod,
    /*14*/OP_CALL, &channelfilter_pay_mod,
    // TODO shadow_additions
    /* do */
	    /*16*/ OP_CALL, &pendingsendpays_pay_mod,
	    /*18*/ OP_CALL, &checktimeout_pay_mod,
	    /*20*/ OP_CALL, &refreshgossmap_pay_mod,
	    /*22*/ OP_CALL, &compute_routes_pay_mod,
	    /*24*/ OP_CALL, &send_routes_pay_mod,
	    /*do*/
		    /*26*/ OP_CALL, &sleep_pay_mod,
		    /*28*/ OP_CALL, &collect_results_pay_mod,
	    /*while*/
	    /*30*/ OP_IF, &nothaveresults_pay_cond, (void *)26,
    /* while */
    /*33*/ OP_IF, &retry_pay_cond, (void *)16,
    /*36*/ OP_CALL, &end_pay_mod, /* safety net, default failure if reached */
    /*38*/ NULL};
