#include "config.h"

#include "../queries.c"
#include <ccan/array_size/array_size.h>
#include <ccan/str/hex/hex.h>
#include <common/blinding.h>
#include <common/channel_type.h>
#include <common/clock_time.h>
#include <common/ecdh.h>
#include <common/json_parse.h>
#include <common/json_stream.h>
#include <common/memleak.h>
#include <common/onionreply.h>
#include <common/setup.h>
#include <stdio.h>

#ifdef NDEBUG
#error "assert required for tests"
#endif

/* AUTOGENERATED MOCKS START */
/* Generated stub for peer_supplied_query_response */
void peer_supplied_query_response(struct daemon *daemon UNNEEDED,
				  const struct node_id *source_peer UNNEEDED,
				  size_t amount UNNEEDED)
{ fprintf(stderr, "peer_supplied_query_response called!\n"); abort(); }
/* Generated stub for queue_peer_msg */
void queue_peer_msg(struct daemon *daemon UNNEEDED,
		    const struct node_id *peer UNNEEDED,
		    const u8 *msg TAKES UNNEEDED)
{ fprintf(stderr, "queue_peer_msg called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

void status_fmt(enum log_level level UNNEEDED,
		const struct node_id *node_id UNNEEDED,
		const char *fmt UNNEEDED, ...)
{
}

/* These we can reproduce */
static const char *test_vectors_nozlib[] = {
	"{\n"
	"  \"msg\" : {\n"
	"    \"type\" : \"QueryChannelRange\",\n"
	"    \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n"
	"    \"firstBlockNum\" : 100000,\n"
	"    \"numberOfBlocks\" : 1500,\n"
	"    \"extensions\" : [ ]\n"
	"  },\n"
	"  \"hex\" : \"01070f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206000186a0000005dc\"\n"
	"}\n",
	"{\n"
	"  \"msg\" : {\n"
	"    \"type\" : \"QueryChannelRange\",\n"
	"    \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n"
	"    \"firstBlockNum\" : 35000,\n"
	"    \"numberOfBlocks\" : 100,\n"
	"    \"extensions\" : [ \"WANT_TIMESTAMPS | WANT_CHECKSUMS\" ]\n"
	"  },\n"
	"  \"hex\" : \"01070f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206000088b800000064010103\"\n"
	"}\n",
	"{\n"
	"  \"msg\" : {\n"
	"    \"type\" : \"ReplyChannelRange\",\n"
	"    \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n"
	"    \"firstBlockNum\" : 122334,\n"
	"    \"numberOfBlocks\" : 1500,\n"
	"    \"complete\" : 1,\n"
	"    \"shortChannelIds\" : {\n"
	"      \"encoding\" : \"UNCOMPRESSED\",\n"
	"      \"array\" : [ \"0x0x12355\", \"0x7x30934\", \"0x70x57793\" ]\n"
	"    },\n"
	"    \"timestamps\" : {\n"
	"      \"encoding\" : \"UNCOMPRESSED\",\n"
	"      \"timestamps\" : [ {\n"
	"        \"timestamp1\" : 164545,\n"
	"        \"timestamp2\" : 948165\n"
	"      }, {\n"
	"        \"timestamp1\" : 489645,\n"
	"        \"timestamp2\" : 4786864\n"
	"      }, {\n"
	"        \"timestamp1\" : 46456,\n"
	"        \"timestamp2\" : 9788415\n"
	"      } ]\n"
	"    },\n"
	"    \"checksums\" : {\n"
	"      \"checksums\" : [ {\n"
	"        \"checksum1\" : 1111,\n"
	"        \"checksum2\" : 2222\n"
	"      }, {\n"
	"        \"checksum1\" : 3333,\n"
	"        \"checksum2\" : 4444\n"
	"      }, {\n"
	"        \"checksum1\" : 5555,\n"
	"        \"checksum2\" : 6666\n"
	"      } ]\n"
	"    }\n"
	"  },\n"
	"  \"hex\" : \"01080f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e22060001ddde000005dc01001900000000000000304300000000000778d6000000000046e1c1011900000282c1000e77c5000778ad00490ab00000b57800955bff031800000457000008ae00000d050000115c000015b300001a0a\"\n"
	"}\n",
	"{\n"
	"  \"msg\" : {\n"
	"    \"type\" : \"QueryShortChannelIds\",\n"
	"    \"chainHash\" : \"0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206\",\n"
	"    \"shortChannelIds\" : {\n"
	"      \"encoding\" : \"UNCOMPRESSED\",\n"
	"      \"array\" : [ \"0x0x142\", \"0x0x15465\", \"0x69x42692\" ]\n"
	"    },\n"
	"    \"extensions\" : [ ]\n"
	"  },\n"
	"  \"hex\" : \"01050f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206001900000000000000008e0000000000003c69000000000045a6c4\"\n"
	"}\n",
};

static void get_chainhash(const char *test_vector,
			  const jsmntok_t *obj,
			  struct bitcoin_blkid *chain_hash)
{
	const jsmntok_t *tok = json_get_member(test_vector, obj, "chainHash");
	size_t hexlen = tok->end - tok->start;
	assert(hex_decode(test_vector + tok->start, hexlen,
			  chain_hash, sizeof(*chain_hash)));
}

static u8 *get_scid_array(const tal_t *ctx,
			  const char *test_vector,
			  const jsmntok_t *obj)
{
	const jsmntok_t *scids, *arr, *encoding, *t;
	size_t i;
	u8 *encoded;

	scids = json_get_member(test_vector, obj, "shortChannelIds");
	arr = json_get_member(test_vector, scids, "array");

	encoded = encoding_start(ctx, true);
	encoding = json_get_member(test_vector, scids, "encoding");
	json_for_each_arr(i, t, arr) {
		struct short_channel_id scid;
		assert(json_to_short_channel_id(test_vector, t, &scid));
		encoding_add_short_channel_id(&encoded, scid);
	}
	assert(json_tok_streq(test_vector, encoding, "UNCOMPRESSED"));

	return encoded;
}

static u8 *test_query_channel_range(const char *test_vector, const jsmntok_t *obj)
{
	struct bitcoin_blkid chain_hash;
	u32 firstBlockNum, numberOfBlocks;
	const jsmntok_t *opt, *t;
	struct tlv_query_channel_range_tlvs *tlvs
		= tlv_query_channel_range_tlvs_new(NULL);
	u8 *msg;
	size_t i;

	get_chainhash(test_vector, obj, &chain_hash);
	assert(json_to_number(test_vector, json_get_member(test_vector, obj, "firstBlockNum"), &firstBlockNum));
	assert(json_to_number(test_vector, json_get_member(test_vector, obj, "numberOfBlocks"), &numberOfBlocks));
	opt = json_get_member(test_vector, obj, "extensions");
	json_for_each_arr(i, t, opt) {
		assert(json_tok_streq(test_vector, t,
				      "WANT_TIMESTAMPS | WANT_CHECKSUMS"));
		tlvs->query_option = tal(tlvs, bigsize_t);
		*tlvs->query_option =
			QUERY_ADD_TIMESTAMPS | QUERY_ADD_CHECKSUMS;
	}
	msg = towire_query_channel_range(NULL, &chain_hash, firstBlockNum, numberOfBlocks, tlvs);

	tal_free(tlvs);
	return msg;
}

static u8 *test_reply_channel_range(const char *test_vector, const jsmntok_t *obj)
{
	struct bitcoin_blkid chain_hash;
	u32 firstBlockNum, numberOfBlocks, complete;
	const jsmntok_t *opt, *t;
	size_t i;
	u8 *msg;
	u8 *encoded_scids;

	u8 *ctx = tal(NULL, u8);
	struct tlv_reply_channel_range_tlvs *tlvs
		= tlv_reply_channel_range_tlvs_new(ctx);

	get_chainhash(test_vector, obj, &chain_hash);
	assert(json_to_number(test_vector, json_get_member(test_vector, obj, "firstBlockNum"), &firstBlockNum));
	assert(json_to_number(test_vector, json_get_member(test_vector, obj, "numberOfBlocks"), &numberOfBlocks));
	assert(json_to_number(test_vector, json_get_member(test_vector, obj, "complete"), &complete));

	encoded_scids = get_scid_array(ctx, test_vector, obj);

	opt = json_get_member(test_vector, obj, "timestamps");
	if (opt) {
		const jsmntok_t *encodingtok, *tstok;
		tlvs->timestamps_tlv
			= tal(tlvs, struct tlv_reply_channel_range_tlvs_timestamps_tlv);

		tlvs->timestamps_tlv->encoded_timestamps
			= encoding_start(tlvs->timestamps_tlv, false);
		encodingtok = json_get_member(test_vector, opt, "encoding");
		tstok = json_get_member(test_vector, opt, "timestamps");
		json_for_each_arr(i, t, tstok) {
			struct channel_update_timestamps ts;
			assert(json_to_number(test_vector,
					      json_get_member(test_vector, t,
							      "timestamp1"),
					      &ts.timestamp_node_id_1));
			assert(json_to_number(test_vector,
					      json_get_member(test_vector, t,
							      "timestamp2"),
					      &ts.timestamp_node_id_2));
			towire_channel_update_timestamps(&tlvs->timestamps_tlv->encoded_timestamps, &ts);
		}
		assert(json_tok_streq(test_vector, encodingtok, "UNCOMPRESSED"));
		tlvs->timestamps_tlv->encoding_type = ARR_UNCOMPRESSED;
	}

	opt = json_get_member(test_vector, obj, "checksums");
	if (opt) {
		const jsmntok_t *cstok;
		tlvs->checksums_tlv
			= tal_arr(tlvs, struct channel_update_checksums, 0);

		cstok = json_get_member(test_vector, opt, "checksums");
		json_for_each_arr(i, t, cstok) {
			struct channel_update_checksums cs;
			assert(json_to_number(test_vector,
				      json_get_member(test_vector, t,
							      "checksum1"),
					      &cs.checksum_node_id_1));
			assert(json_to_number(test_vector,
					      json_get_member(test_vector, t,
							      "checksum2"),
					      &cs.checksum_node_id_2));
			tal_arr_expand(&tlvs->checksums_tlv, cs);
		}
	}

	msg = towire_reply_channel_range(
		NULL, &chain_hash, firstBlockNum, numberOfBlocks,
		complete, encoded_scids, tlvs);
	tal_free(ctx);
	return msg;
}

static struct tlv_query_short_channel_ids_tlvs_query_flags *
get_query_flags_array(const tal_t *ctx,
		      const char *test_vector,
		      const jsmntok_t *opt)
{
	const jsmntok_t *encoding, *arr, *t;
	struct tlv_query_short_channel_ids_tlvs_query_flags *tlv
		= tal(ctx, struct tlv_query_short_channel_ids_tlvs_query_flags);
	size_t i;

	arr = json_get_member(test_vector, opt, "array");

	tlv->encoded_query_flags = encoding_start(tlv, false);
	encoding = json_get_member(test_vector, opt, "encoding");
	json_for_each_arr(i, t, arr) {
		bigsize_t f;
		assert(json_to_u64(test_vector, t, &f));
		encoding_add_query_flag(&tlv->encoded_query_flags, f);
	}
	assert(json_tok_streq(test_vector, encoding, "UNCOMPRESSED"));
	tlv->encoding_type = ARR_UNCOMPRESSED;

	return tlv;
}

static u8 *test_query_short_channel_ids(const char *test_vector,
					const jsmntok_t *obj)
{
	struct bitcoin_blkid chain_hash;
	const jsmntok_t *opt;
	u8 *encoded, *msg;
	struct tlv_query_short_channel_ids_tlvs *tlvs;

	get_chainhash(test_vector, obj, &chain_hash);
	encoded = get_scid_array(NULL, test_vector, obj);

	opt = json_get_member(test_vector, obj, "extensions");
	if (opt && opt->size != 0) {
		/* Only handle a single */
		assert(opt->size == 1);
		tlvs = tlv_query_short_channel_ids_tlvs_new(encoded);
		tlvs->query_flags
			= get_query_flags_array(tlvs, test_vector, opt + 1);
	} else
		tlvs = NULL;
	msg = towire_query_short_channel_ids(NULL,
					     &chain_hash, encoded, tlvs);

	tal_free(encoded);
	return msg;
}

int main(int argc, char *argv[])
{
	jsmntok_t *toks = tal_arr(NULL, jsmntok_t, 1000);

	common_setup(argv[0]);

	for (size_t i = 0; i < ARRAY_SIZE(test_vectors_nozlib); i++) {
		jsmn_parser parser;
		u8 *m;
		const char *hex_m;
		const jsmntok_t *msg, *type, *hex;

		jsmn_init(&parser);
		assert(jsmn_parse(&parser,
				  test_vectors_nozlib[i], strlen(test_vectors_nozlib[i]),
				  toks, tal_count(toks)) > 0);

		msg = json_get_member(test_vectors_nozlib[i], toks, "msg");
		hex = json_get_member(test_vectors_nozlib[i], toks, "hex");
		type = json_get_member(test_vectors_nozlib[i], msg, "type");
		if (json_tok_streq(test_vectors_nozlib[i], type, "QueryChannelRange"))
			m = test_query_channel_range(test_vectors_nozlib[i], msg);
		else if (json_tok_streq(test_vectors_nozlib[i], type, "ReplyChannelRange"))
			m = test_reply_channel_range(test_vectors_nozlib[i], msg);
		else if (json_tok_streq(test_vectors_nozlib[i], type, "QueryShortChannelIds"))
			m = test_query_short_channel_ids(test_vectors_nozlib[i], msg);
		else
			abort();
		hex_m = tal_hex(m, m);
		assert(json_tok_streq(test_vectors_nozlib[i], hex, hex_m));
		tal_free(m);
	}
	tal_free(toks);
	common_shutdown();
	return 0;
}
