/*
 * assoc_ctrl.h - controller-side association control persistence
 *
 * Copyright (C) 2026 Genexis AB. All rights reserved.
 *
 */
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#include <easy/easy.h>
#include <easymesh.h>
#include <json-c/json.h>

#include "assoc_ctrl.h"
#include "cntlr.h"
#include "config.h"
#include "utils/debug.h"
#include "utils/utils.h"

static void assoc_ctrl_remove_client(struct json_object *arr, const uint8_t *macaddr)
{
	int len = json_object_array_length(arr);
	int i;

	for (i = 0; i < len; i++) {
		struct json_object *t = json_object_array_get_idx(arr, i);
		const char *p;
		uint8_t mac[6];

		if (!t || !json_object_is_type(t, json_type_object))
			continue;

		p = json_get_string(t, "macaddr");
		if (!p || !hwaddr_aton(p, mac))
			continue;

		if (!memcmp(mac, macaddr, 6)) {
			json_object_array_del_idx(arr, i, 1);
			return;
		}
	}
}

static void assoc_ctrl_write_client(struct json_object *arr, const uint8_t *macaddr,
		uint16_t validity, uint8_t mode)
{
	struct json_object *new;
	char macstr[18] = {0};
	char time_str[8] = {0};
	time_t now = time(NULL);
	struct tm res;

	if (!arr || !json_object_is_type(arr, json_type_array))
		return;

	assoc_ctrl_remove_client(arr, macaddr);

	if (mode == ASSOC_CTRL_UNBLOCK)
		return;

	new = json_object_new_object();
	if (!new)
		return;

	localtime_r(&now, &res);
	snprintf(time_str, sizeof(time_str), "%02d:%02d", res.tm_hour, res.tm_min);

	hwaddr_ntoa(macaddr, macstr);
	json_object_object_add(new, "macaddr", json_object_new_string(macstr));
	json_object_object_add(new, "start_time", json_object_new_string(time_str));
	json_object_object_add(new, "duration", json_object_new_int(validity));
	json_object_object_add(new, "mode", json_object_new_int(mode));

	json_object_array_add(arr, new);
}

static struct json_object *assoc_ctrl_get_or_add_bssid(struct json_object *bssids,
		const uint8_t *bssid)
{
	int len = json_object_array_length(bssids);
	struct json_object *bssid_obj;
	char bssidstr[18] = {0};
	int i;

	for (i = 0; i < len; i++) {
		struct json_object *t = json_object_array_get_idx(bssids, i);
		uint8_t cur[6];
		const char *p;

		if (!t || !json_object_is_type(t, json_type_object))
			continue;

		p = json_get_string(t, "bssid");
		if (!p || !hwaddr_aton(p, cur))
			continue;

		if (!memcmp(cur, bssid, 6))
			return t;
	}

	bssid_obj = json_object_new_object();
	if (!bssid_obj)
		return NULL;

	hwaddr_ntoa(bssid, bssidstr);
	json_object_object_add(bssid_obj, "bssid", json_object_new_string(bssidstr));
	json_object_array_add(bssids, bssid_obj);

	return bssid_obj;
}

int cntlr_update_assoc_ctrl_file(struct controller *c, uint8_t *bssid,
		uint8_t mode, uint16_t validity_period,
		uint8_t sta_count, uint8_t *stalist)
{
	const char *file;
	struct json_object *root, *bssids, *bssid_obj, *clients;
	int i, rc = -1;

	if (!c || !bssid || (sta_count > 0 && !stalist))
		return -1;

	file = c->cfg.assoc_ctrl_file;
	if (!file[0])
		return -1;

	root = json_object_from_file(file);
	if (!root) {
		root = json_object_new_object();
		if (!root)
			return -1;
	}

	if (!json_object_object_get_ex(root, "bssids", &bssids) ||
			!bssids || !json_object_is_type(bssids, json_type_array)) {
		bssids = json_object_new_array();
		if (!bssids)
			goto out;
		json_object_object_add(root, "bssids", bssids);
	}

	bssid_obj = assoc_ctrl_get_or_add_bssid(bssids, bssid);
	if (!bssid_obj)
		goto out;

	if (!json_object_object_get_ex(bssid_obj, "clients", &clients) ||
			!clients || !json_object_is_type(clients, json_type_array)) {
		clients = json_object_new_array();
		if (!clients)
			goto out;
		json_object_object_add(bssid_obj, "clients", clients);
	}

	for (i = 0; i < sta_count; i++)
		assoc_ctrl_write_client(clients, &stalist[i * 6], validity_period, mode);

	if (json_object_to_file_ext(file, root, JSON_C_TO_STRING_PRETTY) != 0)
		cntlr_dbg(LOG_STA, "%s: failed to write %s\n", __func__, file);
	else
		rc = 0;

out:
	json_object_put(root);
	return rc;
}
