/* Pipe through jq to create format-string-test.json */
#include "config.h"
#include <assert.h>
#include <ccan/array_size/array_size.h>
#include <ccan/tal/grab_file/grab_file.h>
#include <ccan/tal/path/path.h>
#include <ccan/tal/str/str.h>
#include <common/bolt12.h>
#include <common/bolt12_merkle.h>
#include <common/features.h>
#include <common/setup.h>
#include <common/utils.h>

/* AUTOGENERATED MOCKS START */
/* Generated stub for features_unsupported */
int features_unsupported(const struct feature_set *our_features UNNEEDED,
			 const u8 *their_features UNNEEDED,
			 enum feature_place p UNNEEDED)
{ fprintf(stderr, "features_unsupported called!\n"); abort(); }
/* Generated stub for fromwire */
const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED)
{ fprintf(stderr, "fromwire called!\n"); abort(); }
/* Generated stub for fromwire_blinded_path */
struct blinded_path *fromwire_blinded_path(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *plen UNNEEDED)
{ fprintf(stderr, "fromwire_blinded_path called!\n"); abort(); }
/* Generated stub for fromwire_bool */
bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_bool called!\n"); abort(); }
/* Generated stub for fromwire_fail */
void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_fail called!\n"); abort(); }
/* Generated stub for fromwire_pad */
void fromwire_pad(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED)
{ fprintf(stderr, "fromwire_pad called!\n"); abort(); }
/* Generated stub for fromwire_secp256k1_ecdsa_signature */
void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
					secp256k1_ecdsa_signature *signature UNNEEDED)
{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); }
/* Generated stub for fromwire_sha256 */
void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED)
{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); }
/* Generated stub for fromwire_tal_arrn */
u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED,
		       const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED)
{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); }
/* Generated stub for fromwire_tu32 */
u32 fromwire_tu32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_tu32 called!\n"); abort(); }
/* Generated stub for fromwire_tu64 */
u64 fromwire_tu64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_tu64 called!\n"); abort(); }
/* Generated stub for fromwire_u16 */
u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); }
/* Generated stub for fromwire_u32 */
u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); }
/* Generated stub for fromwire_u64 */
u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); }
/* Generated stub for fromwire_u8 */
u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); }
/* Generated stub for fromwire_u8_array */
void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED)
{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); }
/* Generated stub for fromwire_utf8_array */
void fromwire_utf8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, char *arr UNNEEDED, size_t num UNNEEDED)
{ fprintf(stderr, "fromwire_utf8_array called!\n"); abort(); }
/* Generated stub for merkle_tlv */
void merkle_tlv(const struct tlv_field *fields UNNEEDED, struct sha256 *merkle UNNEEDED)
{ fprintf(stderr, "merkle_tlv called!\n"); abort(); }
/* Generated stub for sighash_from_merkle */
void sighash_from_merkle(const char *messagename UNNEEDED,
			 const char *fieldname UNNEEDED,
			 const struct sha256 *merkle UNNEEDED,
			 struct sha256 *sighash UNNEEDED)
{ fprintf(stderr, "sighash_from_merkle called!\n"); abort(); }
/* Generated stub for siphash_seed */
const struct siphash_seed *siphash_seed(void)
{ fprintf(stderr, "siphash_seed called!\n"); abort(); }
/* Generated stub for towire_blinded_path */
void towire_blinded_path(u8 **p UNNEEDED, const struct blinded_path *blinded_path UNNEEDED)
{ fprintf(stderr, "towire_blinded_path called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

static void example2(const char *comment, bool valid,
		     const char *str, bool final)
{
	printf("{\n");
	printf("\"comment\": \"%s\",\n", comment);
	printf("\"valid\": %s,\n", valid ? "true": "false");
	printf("\"string\": \"%s\"\n", str);
	printf("}%s\n", final ? "" : ",");
}

static void example(const char *comment, bool valid, const char *str)
{
	example2(comment, valid, str, false);
}

static void example_final(const char *comment, bool valid, const char *str)
{
	example2(comment, valid, str, true);
}

static utf8 *tal_utf8(const tal_t *ctx, const char *str)
{
	/* Non-NUL terminated! */
	return tal_dup_arr(ctx, char, str, strlen(str), 0);
}

int main(int argc, char *argv[])
{
	struct tlv_offer *offer;
	struct secret alice;
	char *str;

	common_setup(argv[0]);

	offer = tlv_offer_new(tmpctx);
	/* BOLT #12:
	 * A writer of an offer:
	 *   - MUST NOT set any TLV fields outside the inclusive ranges: 1 to 79 and 1000000000 to 1999999999.
	 *   - if the chain for the invoice is not solely bitcoin:
	 *     - MUST specify `offer_chains` the offer is valid for.
	 *   - otherwise:
	 *     - SHOULD omit `offer_chains`, implying that bitcoin is only chain.
	 *   - if a specific minimum `offer_amount` is required for successful payment:
	 *     - MUST set `offer_amount` to the amount expected (per item).
	 *     - MUST set `offer_amount` greater than zero.
	 *     - if the currency for `offer_amount` is that of all entries in `chains`:
	 *       - MUST specify `offer_amount` in multiples of the minimum lightning-payable unit
	 *         (e.g. milli-satoshis for bitcoin).
	 *     - otherwise:
	 *       - MUST specify `offer_currency` `iso4217` as an ISO 4217 three-letter code.
	 *       - MUST specify `offer_amount` in the currency unit adjusted by the ISO 4217
	 *         exponent (e.g. USD cents).
	 *     - MUST set `offer_description` to a complete description of the purpose
	 *       of the payment.
	 *   - otherwise:
	 *     - MUST NOT set `offer_amount`
	 *     - MUST NOT set `offer_currency`
	 *     - MAY set `offer_description`
	 *   - MAY set `offer_metadata` for its own use.
	 *   - if it supports bolt12 offer features:
	 *     - MUST set `offer_features`.`features` to the bitmap of bolt12 features.
	 *   - if the offer expires:
	 *     - MUST set `offer_absolute_expiry` `seconds_from_epoch` to the number of seconds
	 *       after midnight 1 January 1970, UTC that invoice_request should not be
	 *       attempted.
	 *   - if it is connected only by private channels:
	 *     - MUST include `offer_paths` containing one or more paths to the node from
	 *       publicly reachable nodes.
	 *   - otherwise:
	 *     - MAY include `offer_paths`.
	 *   - if it includes `offer_paths`:
	 *     - MAY set `offer_issuer_id`.
	 *   - otherwise:
	 *     - MUST set `offer_issuer_id` to the node's public key to request the invoice from.
	 *   - if it sets `offer_issuer`:
	 *     - SHOULD set it to identify the issuer of the invoice clearly.
	 *     - if it includes a domain name:
	 *       - SHOULD begin it with either user@domain or domain
	 *       - MAY follow with a space and more text
	 *   - if it can supply more than one item for a single invoice:
	 *     - if the maximum quantity is known:
	 *       - MUST set that maximum in `offer_quantity_max`.
	 *       - MUST NOT set `offer_quantity_max` to 0.
	 *     - otherwise:
	 *       - MUST set `offer_quantity_max` to 0.
	 *   - otherwise:
	 *     - MUST NOT set `offer_quantity_max`.
	 */
	memset(&alice, 'A', sizeof(alice));
	offer->offer_issuer_id = tal(offer, struct pubkey);
	assert(pubkey_from_secret(&alice, offer->offer_issuer_id));
	offer->offer_description = tal_utf8(offer, "An example description");
	offer->offer_issuer = tal_utf8(offer, "BOLT 12 industries");
	offer->offer_amount = tal(offer, u64);
	/* 1M msat */
	*offer->offer_amount = 1000000;

	str = offer_encode(tmpctx, offer);
	printf("[\n");

	example("A complete string is valid", true, str);
	example("+ can join anywhere", true,
		tal_fmt(tmpctx, "%.*s+%s", 1, str, str + 1));
	example("Multiple + can join", true,
		tal_fmt(tmpctx, "%.*s+%.*s+%.*s+%.*s+%s",
			15, str,
			31, str + 15,
			18, str + 15 + 31,
			70, str + 15 + 31 + 18,
			str + 15 + 31 + 18 + 70));
	example("+ can be followed by whitespace", true,
		tal_fmt(tmpctx, "%.*s+ %.*s+  %.*s+\\n%.*s+\\r\\n %s",
			15, str,
			31, str + 15,
			18, str + 15 + 31,
			70, str + 15 + 31 + 18,
			str + 15 + 31 + 18 + 70));

	example("+ must be surrounded by bech32 characters", false,
		tal_fmt(tmpctx, "%s+", str));
	example("+ must be surrounded by bech32 characters", false,
		tal_fmt(tmpctx, "%s+ ", str));
	example("+ must be surrounded by bech32 characters", false,
		tal_fmt(tmpctx, "+%s", str));
	example("+ must be surrounded by bech32 characters", false,
		tal_fmt(tmpctx, "+ %s", str));
	example_final("+ must be surrounded by bech32 characters", false,
		      tal_fmt(tmpctx, "%.*s++%s", 2, str, str+2));
	printf("]\n");
	common_shutdown();
}
