#include "config.h"
#include <common/daemon.h>
#include <common/node_id.c>
#include <common/setup.h>
#include <common/status_levels.c>
#include <stdio.h>

#define fwrite test_fwrite
static size_t test_fwrite(const void *ptr, size_t size, size_t nmemb,
			  FILE *stream);

#include "../log.c"

void notify_log(struct lightningd *ld UNNEEDED,
		enum log_level level UNNEEDED,
		struct timeabs time UNNEEDED,
		const char *source UNNEEDED,
		const char *logmsg UNNEEDED)
{  }

/* AUTOGENERATED MOCKS START */
/* Generated stub for command_check_done */
struct command_result *command_check_done(struct command *cmd)

{ fprintf(stderr, "command_check_done called!\n"); abort(); }
/* Generated stub for command_check_only */
bool command_check_only(const struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_check_only called!\n"); abort(); }
/* Generated stub for command_deprecated_in_ok */
bool command_deprecated_in_ok(struct command *cmd UNNEEDED,
			      const char *param UNNEEDED,
			      const char *depr_start UNNEEDED,
			      const char *depr_end UNNEEDED)
{ fprintf(stderr, "command_deprecated_in_ok called!\n"); abort(); }
/* Generated stub for command_dev_apis */
bool command_dev_apis(const struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_dev_apis called!\n"); abort(); }
/* 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_filter_ptr */
struct json_filter **command_filter_ptr(struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_filter_ptr called!\n"); abort(); }
/* Generated stub for command_log */
void command_log(struct command *cmd UNNEEDED, enum log_level level UNNEEDED,
		 const char *fmt UNNEEDED, ...)

{ fprintf(stderr, "command_log called!\n"); abort(); }
/* Generated stub for command_param_failed */
struct command_result *command_param_failed(void)

{ fprintf(stderr, "command_param_failed called!\n"); abort(); }
/* Generated stub for command_set_usage */
void command_set_usage(struct command *cmd UNNEEDED, const char *usage UNNEEDED)
{ fprintf(stderr, "command_set_usage called!\n"); abort(); }
/* Generated stub for command_success */
struct command_result *command_success(struct command *cmd UNNEEDED,
				       struct json_stream *response)

{ fprintf(stderr, "command_success called!\n"); abort(); }
/* Generated stub for command_usage_only */
bool command_usage_only(const struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_usage_only called!\n"); abort(); }
/* Generated stub for json_stream_log_suppress_for_cmd */
void json_stream_log_suppress_for_cmd(struct json_stream *js UNNEEDED,
					    const struct command *cmd UNNEEDED)
{ fprintf(stderr, "json_stream_log_suppress_for_cmd called!\n"); abort(); }
/* Generated stub for json_stream_success */
struct json_stream *json_stream_success(struct command *cmd UNNEEDED)
{ fprintf(stderr, "json_stream_success called!\n"); abort(); }
/* Generated stub for notify_warning */
void notify_warning(struct lightningd *ld UNNEEDED,
		    enum log_level level UNNEEDED,
		    struct timeabs time UNNEEDED,
		    const char *source UNNEEDED,
		    const char *logmsg UNNEEDED)
{ fprintf(stderr, "notify_warning called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

static size_t num_written;
static size_t test_fwrite(const void *ptr, size_t size, size_t nmemb,
			  FILE *stream)
{
	num_written++;
	return nmemb;
}

static size_t try_log(struct log_book *lb, const char *prefix,
		      const struct node_id *node_id,
		      enum log_level level)
{
	struct logger *log = new_logger(NULL, lb, node_id, "%s", prefix);

	num_written = 0;
	log_(log, level, NULL, false, "test_log");
	tal_free(log);
	return num_written;
}

int main(int argc, char *argv[])
{
	struct log_book *lb;
	struct node_id node_id;
	struct lightningd *ld;
	char *tmpfile;

	common_setup(argv[0]);

	assert(node_id_from_hexstr("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518",
				   strlen("0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c03518"), &node_id));

	ld = tal(tmpctx, struct lightningd);
	ld->logfiles = NULL;
	lb = ld->log_book = new_log_book(ld);
	ld->log = new_logger(ld, lb, NULL, "dummy");
	assert(arg_log_to_file("-", ld) == NULL);

	assert(tmpdir_mkstemp(tmpctx, "run-log_filter.XXXXXX", &tmpfile) >= 0);
	assert(arg_log_to_file(tmpfile, ld) == NULL);

	/* Log level default. */
	assert(opt_log_level("BROKEN", lb) == NULL);
	assert(try_log(lb, "prefix", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_INFORM) == 0);
	assert(try_log(lb, "prefix", NULL, LOG_DBG) == 0);
	assert(try_log(lb, "prefix", NULL, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", NULL, LOG_IO_IN) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", &node_id, LOG_INFORM) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_DBG) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_IN) == 0);

	/* Now use debug as default */
	assert(opt_log_level("debug", lb) == NULL);
	assert(try_log(lb, "prefix", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_INFORM) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_DBG) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", NULL, LOG_IO_IN) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", &node_id, LOG_INFORM) == 2);
	assert(try_log(lb, "prefix", &node_id, LOG_DBG) == 2);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_IN) == 0);

	/* Log level exception for prefix */
	assert(opt_log_level("broken:prefix2", lb) == NULL);
	assert(try_log(lb, "prefix", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_INFORM) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_DBG) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", NULL, LOG_IO_IN) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", &node_id, LOG_INFORM) == 2);
	assert(try_log(lb, "prefix", &node_id, LOG_DBG) == 2);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_IN) == 0);

	assert(try_log(lb, "prefix2", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix2", NULL, LOG_INFORM) == 0);
	assert(try_log(lb, "prefix2", NULL, LOG_DBG) == 0);
	assert(try_log(lb, "prefix2", NULL, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix2", NULL, LOG_IO_IN) == 0);
	assert(try_log(lb, "prefix2", &node_id, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix2", &node_id, LOG_INFORM) == 0);
	assert(try_log(lb, "prefix2", &node_id, LOG_DBG) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_IN) == 0);

	/* Log level exception for nodeid: that only does broken! */
	assert(opt_log_level("broken:0266e4598", lb) == NULL);
	assert(try_log(lb, "prefix", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_INFORM) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_DBG) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", NULL, LOG_IO_IN) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", &node_id, LOG_INFORM) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_DBG) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", &node_id, LOG_IO_IN) == 0);

	/* File exceptions: log stdout prefix to stdout */
	assert(opt_log_level("io:stdout1:-", lb) == NULL);
	assert(try_log(lb, "stdout1", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "stdout1", NULL, LOG_INFORM) == 2);
	assert(try_log(lb, "stdout1", NULL, LOG_DBG) == 2);
	assert(try_log(lb, "stdout1", NULL, LOG_IO_OUT) == 1);
	assert(try_log(lb, "stdout1", NULL, LOG_IO_IN) == 1);
	assert(try_log(lb, "prefix", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_INFORM) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_DBG) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", NULL, LOG_IO_IN) == 0);

	/* File exceptions: log stdout2 prefix to stdout */
	assert(opt_log_level("broken:stdout2:-", lb) == NULL);
	assert(try_log(lb, "stdout2", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "stdout2", NULL, LOG_INFORM) == 1);
	assert(try_log(lb, "stdout2", NULL, LOG_DBG) == 1);
	assert(try_log(lb, "stdout2", NULL, LOG_IO_OUT) == 0);
	assert(try_log(lb, "stdout2", NULL, LOG_IO_IN) == 0);
	assert(try_log(lb, "prefix", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_INFORM) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_DBG) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_IO_OUT) == 0);
	assert(try_log(lb, "prefix", NULL, LOG_IO_IN) == 0);

	/* File exception: tmpfile logs everything */
	assert(opt_log_level(tal_fmt(tmpctx, "io::%s", tmpfile), lb) == NULL);
	assert(try_log(lb, "stdout1", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "stdout1", NULL, LOG_INFORM) == 2);
	assert(try_log(lb, "stdout1", NULL, LOG_DBG) == 2);
	assert(try_log(lb, "stdout1", NULL, LOG_IO_OUT) == 2);
	assert(try_log(lb, "stdout1", NULL, LOG_IO_IN) == 2);
	assert(try_log(lb, "stdout2", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "stdout2", NULL, LOG_INFORM) == 1);
	assert(try_log(lb, "stdout2", NULL, LOG_DBG) == 1);
	assert(try_log(lb, "stdout2", NULL, LOG_IO_OUT) == 1);
	assert(try_log(lb, "stdout2", NULL, LOG_IO_IN) == 1);
	assert(try_log(lb, "prefix", NULL, LOG_BROKEN) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_INFORM) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_DBG) == 2);
	assert(try_log(lb, "prefix", NULL, LOG_IO_OUT) == 1);
	assert(try_log(lb, "prefix", NULL, LOG_IO_IN) == 1);

	/* Close output file, avoid upsetting valgrind */
	fclose(ld->log_book->log_files[1]->f);

	/* Freeing (last) log frees logbook */
	tal_free(ld->log);
	common_shutdown();
}
