/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2025 Brett Sheffield <bacs@librecast.net> */

#ifndef _STATE_H
#define _STATE_H 1

#include <config.h>
#include <errno.h>
#include <stddef.h>
#include <librecast/types.h>

#define LCAGENTRC ".lcagentrc"

#define STATE(a, b) (((a)->flag & b) == b)
#define STATE_SET(a, b) ((a)->flag |= b)

#define STATE_FLAGS \
	X(0x01, STATE_VERBOSE) \
	X(0x02, STATE_LOOPBACK) \
	X(0x04, STATE_EXPIRES) \
	X(0x08, STATE_BWLIMIT) \
	X(0x10, STATE_OVERHEAD) \
	X(0x20, STATE_DEBUG) \
	X(0x40, STATE_STDIN) \
	X(0x80, STATE_FOLLOW)

#define STATE_VERBS \
	X(0, "server",	VERB_SERVER,	agent_server) \
	X(1, "help",	VERB_HELP,	agent_print_version) \
	X(2, "version",	VERB_VERSION,	agent_print_version) \
	X(3, "start",	VERB_START,	agent_start) \
	X(4, "stop",	VERB_STOP,	agent_stop) \
	X(5, "reload",	VERB_RELOAD,	agent_reload) \
	X(6, "whoami",	VERB_WHOAMI,	agent_whoami) \
	X(7, "recv",	VERB_RECV,	agent_recv) \
	X(8, "send",	VERB_SEND,	agent_send) \
/*	X(9, "talk",	VERB_TALK,	agent_talk) */ \
	X(10, "sign",	VERB_SIGN,	agent_sign) \
/*	X(11, "issue",	VERB_ISSUE,	agent_issue) */ \
	X(12, "key",	VERB_KEY,	agent_key) \
	X(13, "exec",	VERB_EXEC,	agent_exec)

#define STATE_OPTIONS_SHORT \
	X( 0 , OPT_TYPE_FLAG, STATE_STDIN) \
	X('d', OPT_TYPE_FLAG, STATE_DEBUG) \
	X('f', OPT_TYPE_FLAG, STATE_FOLLOW) \
	X('l', OPT_TYPE_FLAG, STATE_LOOPBACK) \
	X('v', OPT_TYPE_FLAG, STATE_VERBOSE)

#define STATE_OPTIONS_U64 \
	X("bpslimit",	OPT_TYPE_U64, STATE_BWLIMIT, bpslimit) \
	X("expires",	OPT_TYPE_U64, STATE_EXPIRES, expires) \
	X("overhead",	OPT_TYPE_U64, STATE_OVERHEAD, overhead)

#define STATE_OPTIONS_LONG \
	X("debug",      OPT_TYPE_FLAG, STATE_DEBUG) \
	X("follow",     OPT_TYPE_FLAG, STATE_FOLLOW) \
	X("loopback",	OPT_TYPE_FLAG, STATE_LOOPBACK) \
	X("verbose",	OPT_TYPE_FLAG, STATE_VERBOSE)

#define STATE_DIRECTIVES \
	X("directory", dir)

enum {
	OPT_TYPE_FLAG,
	OPT_TYPE_U64,
};

enum {
#define X(a, b) b = a,
	STATE_FLAGS
#undef X
};

enum {
#define X(a, b, c, d) c = a,
	STATE_VERBS
#undef X
};

enum {
	CMD_TRY = 1,	/* continue if command fails */
};

enum {
	CHAN_RELOAD	= 0x01,
	CHAN_RESTART	= 0x02,
	CHAN_CHROOT	= 0x04,
	CHAN_KEY	= 0x08, /* public key channel */
	CHAN_LOOP	= 0x10,
	CHAN_JOIN	= 0x20,
	CHAN_ENABLE	= 0x40,
	CHAN_ONSTART	= 0x80,
};

#define KEY_PUBLIC_BYTES crypto_box_PUBLICKEYBYTES + crypto_sign_PUBLICKEYBYTES
#define KEY_SECRET_BYTES crypto_box_SECRETKEYBYTES + crypto_sign_SECRETKEYBYTES
#define HEXKEY_PUBLIC_CRYPTBYTES crypto_box_PUBLICKEYBYTES * 2
#define HEXKEY_SECRET_CRYPTBYTES crypto_box_SECRETKEYBYTES * 2
#define HEXKEY_PUBLIC_SIGNBYTES crypto_sign_PUBLICKEYBYTES * 2
#define HEXKEY_SECRET_SIGNBYTES crypto_sign_SECRETKEYBYTES * 2
#define KEY_PUBLIC_HEXLEN HEXKEY_PUBLIC_CRYPTBYTES + HEXKEY_PUBLIC_SIGNBYTES
#define KEY_SECRET_HEXLEN HEXKEY_SECRET_CRYPTBYTES + HEXKEY_SECRET_SIGNBYTES
#define KEY_PATH ".config/lcagent"
#define KEY_DEFAULT "default"

#define E(FLAG, ...) if (state->flag & FLAG) fprintf(stderr, __VA_ARGS__)

typedef struct key_combo_s {
	char phex[KEY_PUBLIC_HEXLEN + 1];
	char shex[KEY_SECRET_HEXLEN + 1];
	lc_keypair_t s; /* signing */
	lc_keypair_t e; /* encryption */
} key_combo_t;

typedef struct state_cmd_s state_cmd_t;
struct state_cmd_s {
	state_cmd_t *next;
	char *cmd;
	int flags;
};

typedef struct state_chan_s state_chan_t;
struct state_chan_s {
	state_chan_t *next;
	lc_channel_t *chan;
	state_cmd_t *cmd;
	key_combo_t keyring;
	lc_token_t *token;
	char *auth_key;
	char *chan_name;
	char *chan_status;
	char *chan_status_cmd;
	char *chan_stdout;
	char *chan_stdin;
	char *chan_stderr;
	char *dir;
	char *seed;
	unsigned char chan_key[crypto_secretbox_KEYBYTES];
	unsigned char auth_pek[crypto_box_PUBLICKEYBYTES];
	unsigned char auth_psk[crypto_sign_PUBLICKEYBYTES];
	unsigned char hash[HASHSIZE]; /* hash of chan_name */
	char hex[HEXLEN]; /* hex of hash */
	int flags;
	unsigned int auth_keys;
};

typedef struct state_s {
	lc_socket_t *sock;
	char **argv;
	char *dir_home;
	char *dir_cache;
	char *dir_config;
	char *dir_data;
	char *dir_state;
	char *rcfile;
	char *logfile;
	FILE *log;
	state_chan_t *chan_head;
	state_chan_t defaults;
	uint64_t bpslimit;
	uint64_t expires;
	uint64_t overhead;
	unsigned int flag;
	unsigned int ifx;
	unsigned char verb;
	int argc;
	char optmask[128];
	int lockfd; /* file descriptor of lockfile */
	pid_t pid;  /* pid of running server */
} state_t;

int state_defaults_set(state_t *state);
int state_dirs(state_t *state, char *home);
int state_parse_args(state_t *state, int argc, char *argv[]);
int state_parse_configfile(state_t *state, char *configfile);
int state_parse_config(state_t *state, char *config, size_t len);
int state_parse_config_default(state_t *state);
int state_authkey_set(state_t *state, char *authkey);
int state_push_channel(state_t *state, char *channel_name);
int state_push_command(state_t *state, char *command, int flags);
int state_dir_set(state_t *state, char *arg);
int state_logfile_set(state_t *state, char *arg);
int state_seed_set(state_t *state, char *arg);
int state_status_set(state_t *state, char *arg);
int state_statuscmd_set(state_t *state, char *arg);
int state_stderr_set(state_t *state, char *arg);
int state_stdin_set(state_t *state, char *arg);
int state_stdout_set(state_t *state, char *arg);
char *state_pidfile(state_t *state);
char *arg_pop(state_t *state);
state_chan_t *state_chan_by_addr(state_t *state, struct in6_addr *addr);
state_chan_t *state_chan_by_name(state_t *state, char *name);
state_chan_t *state_channel_free(state_chan_t *schan);
void free_state(state_t *state);

#endif /* _STATE_H */
