#include "config.h"
#include "../libplugin-pay.c"
#include <bitcoin/chainparams.h>
#include <common/gossip_store.h>
#include <common/setup.h>
#include <common/utils.h>
#include <stdio.h>
#include <unistd.h>

/* AUTOGENERATED MOCKS START */
/* Generated stub for command_finished */
struct command_result *command_finished(struct command *cmd UNNEEDED, struct json_stream *response UNNEEDED)
{ fprintf(stderr, "command_finished called!\n"); abort(); }
/* Generated stub for command_still_pending */
struct command_result *command_still_pending(struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_still_pending called!\n"); abort(); }
/* Generated stub for deprecated_apis */
bool deprecated_apis;
/* Generated stub for feature_offered */
bool feature_offered(const u8 *features UNNEEDED, size_t f UNNEEDED)
{ fprintf(stderr, "feature_offered called!\n"); abort(); }
/* Generated stub for fromwire_bigsize */
bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_bigsize called!\n"); abort(); }
/* Generated stub for fromwire_channel_id */
bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
			 struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "fromwire_channel_id called!\n"); abort(); }
/* Generated stub for json_add_amount_msat_compat */
void json_add_amount_msat_compat(struct json_stream *result UNNEEDED,
				 struct amount_msat msat UNNEEDED,
				 const char *rawfieldname UNNEEDED,
				 const char *msatfieldname)

{ fprintf(stderr, "json_add_amount_msat_compat called!\n"); abort(); }
/* Generated stub for json_add_amount_msat_only */
void json_add_amount_msat_only(struct json_stream *result UNNEEDED,
			  const char *msatfieldname UNNEEDED,
			  struct amount_msat msat)

{ fprintf(stderr, "json_add_amount_msat_only called!\n"); abort(); }
/* Generated stub for json_add_hex_talarr */
void json_add_hex_talarr(struct json_stream *result UNNEEDED,
			 const char *fieldname UNNEEDED,
			 const tal_t *data UNNEEDED)
{ fprintf(stderr, "json_add_hex_talarr called!\n"); abort(); }
/* Generated stub for json_add_invstring */
void json_add_invstring(struct json_stream *result UNNEEDED, const char *invstring UNNEEDED)
{ fprintf(stderr, "json_add_invstring called!\n"); abort(); }
/* Generated stub for json_add_node_id */
void json_add_node_id(struct json_stream *response UNNEEDED,
				const char *fieldname UNNEEDED,
				const struct node_id *id UNNEEDED)
{ fprintf(stderr, "json_add_node_id called!\n"); abort(); }
/* Generated stub for json_add_num */
void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
		  unsigned int value UNNEEDED)
{ fprintf(stderr, "json_add_num called!\n"); abort(); }
/* Generated stub for json_add_preimage */
void json_add_preimage(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
		     const struct preimage *preimage UNNEEDED)
{ fprintf(stderr, "json_add_preimage called!\n"); abort(); }
/* Generated stub for json_add_secret */
void json_add_secret(struct json_stream *response UNNEEDED,
		     const char *fieldname UNNEEDED,
		     const struct secret *secret UNNEEDED)
{ fprintf(stderr, "json_add_secret called!\n"); abort(); }
/* Generated stub for json_add_sha256 */
void json_add_sha256(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
		     const struct sha256 *hash UNNEEDED)
{ fprintf(stderr, "json_add_sha256 called!\n"); abort(); }
/* Generated stub for json_add_short_channel_id */
void json_add_short_channel_id(struct json_stream *response UNNEEDED,
			       const char *fieldname UNNEEDED,
			       const struct short_channel_id *id UNNEEDED)
{ fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); }
/* Generated stub for json_add_string */
void json_add_string(struct json_stream *js UNNEEDED,
		     const char *fieldname UNNEEDED,
		     const char *str TAKES UNNEEDED)
{ fprintf(stderr, "json_add_string called!\n"); abort(); }
/* Generated stub for json_add_timeabs */
void json_add_timeabs(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
		      struct timeabs t UNNEEDED)
{ fprintf(stderr, "json_add_timeabs called!\n"); abort(); }
/* Generated stub for json_add_u32 */
void json_add_u32(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
		  uint32_t value UNNEEDED)
{ fprintf(stderr, "json_add_u32 called!\n"); abort(); }
/* Generated stub for json_add_u64 */
void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
		  uint64_t value UNNEEDED)
{ fprintf(stderr, "json_add_u64 called!\n"); abort(); }
/* Generated stub for json_array_end */
void json_array_end(struct json_stream *js UNNEEDED)
{ fprintf(stderr, "json_array_end called!\n"); abort(); }
/* Generated stub for json_array_start */
void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED)
{ fprintf(stderr, "json_array_start called!\n"); abort(); }
/* Generated stub for json_get_member */
const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED,
				 const char *label UNNEEDED)
{ fprintf(stderr, "json_get_member called!\n"); abort(); }
/* Generated stub for json_next */
const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_next called!\n"); abort(); }
/* Generated stub for json_object_end */
void json_object_end(struct json_stream *js UNNEEDED)
{ fprintf(stderr, "json_object_end called!\n"); abort(); }
/* Generated stub for json_object_start */
void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
{ fprintf(stderr, "json_object_start called!\n"); abort(); }
/* Generated stub for json_strdup */
char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_strdup called!\n"); abort(); }
/* Generated stub for json_to_bool */
bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED)
{ fprintf(stderr, "json_to_bool called!\n"); abort(); }
/* Generated stub for json_to_createonion_response */
struct createonion_response *json_to_createonion_response(const tal_t *ctx UNNEEDED,
							  const char *buffer UNNEEDED,
							  const jsmntok_t *toks UNNEEDED)
{ fprintf(stderr, "json_to_createonion_response called!\n"); abort(); }
/* Generated stub for json_to_int */
bool json_to_int(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, int *num UNNEEDED)
{ fprintf(stderr, "json_to_int called!\n"); abort(); }
/* Generated stub for json_to_listpeers_result */
struct listpeers_result *json_to_listpeers_result(const tal_t *ctx UNNEEDED,
						  const char *buffer UNNEEDED,
						  const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_to_listpeers_result called!\n"); abort(); }
/* Generated stub for json_to_msat */
bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
		  struct amount_msat *msat UNNEEDED)
{ fprintf(stderr, "json_to_msat called!\n"); abort(); }
/* Generated stub for json_to_node_id */
bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
			       struct node_id *id UNNEEDED)
{ fprintf(stderr, "json_to_node_id called!\n"); abort(); }
/* Generated stub for json_to_number */
bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
		    unsigned int *num UNNEEDED)
{ fprintf(stderr, "json_to_number called!\n"); abort(); }
/* Generated stub for json_to_preimage */
bool json_to_preimage(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct preimage *preimage UNNEEDED)
{ fprintf(stderr, "json_to_preimage called!\n"); abort(); }
/* Generated stub for json_to_sat */
bool json_to_sat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
		 struct amount_sat *sat UNNEEDED)
{ fprintf(stderr, "json_to_sat called!\n"); abort(); }
/* Generated stub for json_to_short_channel_id */
bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
			      struct short_channel_id *scid UNNEEDED)
{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); }
/* Generated stub for json_to_u16 */
bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
                 uint16_t *num UNNEEDED)
{ fprintf(stderr, "json_to_u16 called!\n"); abort(); }
/* Generated stub for json_to_u32 */
bool json_to_u32(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u32 *num UNNEEDED)
{ fprintf(stderr, "json_to_u32 called!\n"); abort(); }
/* Generated stub for json_to_u64 */
bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED)
{ fprintf(stderr, "json_to_u64 called!\n"); abort(); }
/* Generated stub for json_tok_bin_from_hex */
u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED)
{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); }
/* Generated stub for json_tok_full */
const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED)
{ fprintf(stderr, "json_tok_full called!\n"); abort(); }
/* Generated stub for json_tok_full_len */
int json_tok_full_len(const jsmntok_t *t UNNEEDED)
{ fprintf(stderr, "json_tok_full_len called!\n"); abort(); }
/* Generated stub for json_tok_streq */
bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED)
{ fprintf(stderr, "json_tok_streq called!\n"); abort(); }
/* Generated stub for jsonrpc_request_start_ */
struct out_req *jsonrpc_request_start_(struct plugin *plugin UNNEEDED,
				       struct command *cmd UNNEEDED,
				       const char *method UNNEEDED,
				       struct command_result *(*cb)(struct command *command UNNEEDED,
								    const char *buf UNNEEDED,
								    const jsmntok_t *result UNNEEDED,
								    void *arg) UNNEEDED,
				       struct command_result *(*errcb)(struct command *command UNNEEDED,
								       const char *buf UNNEEDED,
								       const jsmntok_t *result UNNEEDED,
								       void *arg) UNNEEDED,
				       void *arg UNNEEDED)
{ fprintf(stderr, "jsonrpc_request_start_ called!\n"); abort(); }
/* Generated stub for jsonrpc_stream_fail */
struct json_stream *jsonrpc_stream_fail(struct command *cmd UNNEEDED,
					int code UNNEEDED,
					const char *err UNNEEDED)
{ fprintf(stderr, "jsonrpc_stream_fail called!\n"); abort(); }
/* Generated stub for jsonrpc_stream_success */
struct json_stream *jsonrpc_stream_success(struct command *cmd UNNEEDED)
{ fprintf(stderr, "jsonrpc_stream_success called!\n"); abort(); }
/* Generated stub for notleak_ */
void *notleak_(void *ptr UNNEEDED, bool plus_children UNNEEDED)
{ fprintf(stderr, "notleak_ called!\n"); abort(); }
/* 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_notification_end */
void plugin_notification_end(struct plugin *plugin UNNEEDED,
			     struct json_stream *stream TAKES UNNEEDED)
{ fprintf(stderr, "plugin_notification_end called!\n"); abort(); }
/* Generated stub for plugin_notification_start */
struct json_stream *plugin_notification_start(struct plugin *plugins UNNEEDED,
					      const char *method UNNEEDED)
{ fprintf(stderr, "plugin_notification_start called!\n"); abort(); }
/* Generated stub for random_select */
bool random_select(double weight UNNEEDED, double *tot_weight UNNEEDED)
{ fprintf(stderr, "random_select called!\n"); abort(); }
/* Generated stub for send_outreq */
struct command_result *send_outreq(struct plugin *plugin UNNEEDED,
				   const struct out_req *req UNNEEDED)
{ fprintf(stderr, "send_outreq called!\n"); abort(); }
/* Generated stub for towire_bigsize */
void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED)
{ fprintf(stderr, "towire_bigsize called!\n"); abort(); }
/* Generated stub for towire_channel_id */
void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED)
{ fprintf(stderr, "towire_channel_id called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

#ifndef SUPERVERBOSE
#define SUPERVERBOSE(...)
#endif

static void write_to_store(int store_fd, const u8 *msg)
{
	struct gossip_hdr hdr;

	hdr.len = cpu_to_be32(tal_count(msg));
	/* We don't actually check these! */
	hdr.crc = 0;
	hdr.timestamp = 0;
	assert(write(store_fd, &hdr, sizeof(hdr)) == sizeof(hdr));
	assert(write(store_fd, msg, tal_count(msg)) == tal_count(msg));
}

static void update_connection(int store_fd,
			      const struct node_id *from,
			      const struct node_id *to,
			      const struct short_channel_id *scid,
			      struct amount_msat min,
			      struct amount_msat max,
			      u32 base_fee, s32 proportional_fee,
			      u32 delay,
			      bool disable)
{
	secp256k1_ecdsa_signature dummy_sig;
	u8 *msg;

	/* So valgrind doesn't complain */
	memset(&dummy_sig, 0, sizeof(dummy_sig));

	msg = towire_channel_update_option_channel_htlc_max(tmpctx,
							    &dummy_sig,
							    &chainparams->genesis_blockhash,
							    scid, 0,
							    ROUTING_OPT_HTLC_MAX_MSAT,
							    node_id_idx(from, to)
							    + (disable ? ROUTING_FLAGS_DISABLED : 0),
							    delay,
							    min,
							    base_fee,
							    proportional_fee,
							    max);

	write_to_store(store_fd, msg);
}

static void add_connection(int store_fd,
			   const struct node_id *from,
			   const struct node_id *to,
			   const struct short_channel_id *scid,
			   struct amount_msat min,
			   struct amount_msat max,
			   u32 base_fee, s32 proportional_fee,
			   u32 delay)
{
	secp256k1_ecdsa_signature dummy_sig;
	struct secret not_a_secret;
	struct pubkey dummy_key;
	u8 *msg;
	const struct node_id *ids[2];

	/* So valgrind doesn't complain */
	memset(&dummy_sig, 0, sizeof(dummy_sig));
	memset(&not_a_secret, 1, sizeof(not_a_secret));
	pubkey_from_secret(&not_a_secret, &dummy_key);

	if (node_id_cmp(from, to) > 0) {
		ids[0] = to;
		ids[1] = from;
	} else {
		ids[0] = from;
		ids[1] = to;
	}
	msg = towire_channel_announcement(tmpctx, &dummy_sig, &dummy_sig,
					  &dummy_sig, &dummy_sig,
					  /* features */ NULL,
					  &chainparams->genesis_blockhash,
					  scid,
					  ids[0], ids[1],
					  &dummy_key, &dummy_key);
	write_to_store(store_fd, msg);

	update_connection(store_fd, from, to, scid, min, max,
			  base_fee, proportional_fee,
			  delay, false);
}

static void node_id_from_privkey(const struct privkey *p, struct node_id *id)
{
	struct pubkey k;
	pubkey_from_privkey(p, &k);
	node_id_from_pubkey(id, &k);
}

#define NUM_NODES (ROUTING_MAX_HOPS + 1)

/* We create an arrangement of nodes, each node N connected to N+1 and
 * to node 1.  The cost for each N to N+1 route is 1, for N to 1 is
 * 2^N.  That means it's always cheapest to go the longer route */
int main(int argc, char *argv[])
{
	struct node_id ids[NUM_NODES];
	int store_fd;
	struct payment *p;
	struct payment_modifier **mods;
	char gossip_version = GOSSIP_STORE_VERSION;
	char *gossipfilename;

	common_setup(argv[0]);
	chainparams = chainparams_for_network("regtest");
	store_fd = tmpdir_mkstemp(tmpctx, "run-route-overlong.XXXXXX", &gossipfilename);
	assert(write(store_fd, &gossip_version, sizeof(gossip_version))
	       == sizeof(gossip_version));

	global_gossmap = gossmap_load(tmpctx, gossipfilename, NULL);

	for (size_t i = 0; i < NUM_NODES; i++) {
		struct privkey tmp;
		memset(&tmp, i+1, sizeof(tmp));
		node_id_from_privkey(&tmp, &ids[i]);
	}

	mods = tal_arrz(tmpctx, struct payment_modifier *, 1);
	p = payment_new(mods, tal(tmpctx, struct command), NULL, mods);

	for (size_t i = 1; i < NUM_NODES; i++) {
		struct short_channel_id scid;

		if (!mk_short_channel_id(&scid, i, i-1, 0))
			abort();
		add_connection(store_fd, &ids[i-1], &ids[i], &scid,
			       AMOUNT_MSAT(0),
			       AMOUNT_MSAT(1000000 * 1000),
			       0, 0, 0);
		SUPERVERBOSE("Joining %s to %s, fee %u\n",
			     type_to_string(tmpctx, struct node_id, &ids[i-1]),
			     type_to_string(tmpctx, struct node_id, &ids[i]),
			     0);

		if (i <= 2)
			continue;
		if (!mk_short_channel_id(&scid, i, 1, 0))
			abort();
		add_connection(store_fd, &ids[1], &ids[i], &scid,
			       AMOUNT_MSAT(0),
			       AMOUNT_MSAT(1000000 * 1000),
			       1 << i, 0, 0);
		SUPERVERBOSE("Joining %s to %s, fee %u\n",
			     type_to_string(tmpctx, struct node_id, &ids[1]),
			     type_to_string(tmpctx, struct node_id, &ids[i]),
			     1 << i);
	}

	assert(gossmap_refresh(global_gossmap, NULL));
	for (size_t i = ROUTING_MAX_HOPS; i > 2; i--) {
		struct gossmap_node *dst, *src;
		struct route_hop *r;
		const char *errmsg;
		SUPERVERBOSE("%s -> %s:\n",
			     type_to_string(tmpctx, struct node_id, &ids[0]),
			     type_to_string(tmpctx, struct node_id, &ids[NUM_NODES-1]));

		src = gossmap_find_node(global_gossmap, &ids[0]);
		dst = gossmap_find_node(global_gossmap, &ids[NUM_NODES-1]);
		r = route(tmpctx, global_gossmap, src, dst, AMOUNT_MSAT(1000), 0, 0.0,
			  i - 1, p, &errmsg);
		assert(r);
		/* FIXME: We naively fall back on shortest, rather
		 * than biassing! */
		assert(tal_count(r) == 2);
	}

	common_shutdown();
	return 0;
}
