/**
 * BLE Management Module (ble_mgmt.c)
 * * Structural Fix: Offloading heavy NVS and Thread operations to a workqueue
 * to prevent stack overflows in the BT RX thread.
 */

#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/hci.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/settings/settings.h>
#include <lasertag_utils.h>
#include <thread_mgmt.h>
#include <ble_mgmt.h>
#include <game_mgmt.h>
#include <string.h>

LOG_MODULE_REGISTER(ble_mgmt, CONFIG_BLE_MGMT_LOG_LEVEL);

/* UUID Definitions */
#define BT_UUID_LT_PROV_SERVICE      BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1000))
#define BT_UUID_LT_PROV_NAME_CHAR    BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1001))
#define BT_UUID_LT_PROV_TYPE_CHAR    BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1008))
#define BT_UUID_LT_PROV_CONFIG_CHAR  BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c100c))

/* Global state and Workqueue structures */
static uint8_t device_role = 0;
static uint8_t adv_enabled = 0;
static struct k_work_delayable adv_restart_work;

/* Buffers for asynchronous config application */
static device_config_payload_t pending_config;
static struct k_work config_apply_work;

/* ============================================================================
   Workqueue Handlers
   ============================================================================ */
static void config_apply_work_handler(struct k_work *work)
{
	ARG_UNUSED(work);

	LOG_DBG("conf rcv, name: " BOLD("%s") ", state: " BOLD("%d") ", game-id: " BOLD("0x%llx") ", net name: " BOLD("%s") ", channel: " BOLD("%u") ", pan: " BOLD("0x%04X"),
			pending_config.node_name,
			pending_config.system_state,
			pending_config.game_id,
			pending_config.network_name,
			pending_config.channel,
			pending_config.pan_id);
	LOG_HEXDUMP_DBG(pending_config.ext_pan_id, 8, "ext pan id");
	LOG_HEXDUMP_DBG(pending_config.network_key, 16, "network key");

	if (pending_config.system_state != SYS_STATE_NO_CHANGE) {
        game_mgmt_set_state((sys_state_t)pending_config.system_state);
    }
    if (pending_config.game_id != 0) {
        game_mgmt_set_game_id(pending_config.game_id);
    }

    if (pending_config.node_name[0] != '\0') {
        lasertag_set_device_name(pending_config.node_name, strlen(pending_config.node_name));
        bt_set_name(lasertag_get_device_name());
    }

    if (pending_config.channel != 0) {
        thread_mgmt_restart_thread_stack(&pending_config, false);
    }
}

static void adv_restart_work_handler(struct k_work *work)
{
	ARG_UNUSED(work);
	LOG_DBG("Restarting BLE advertising via System Workqueue...");    
    if (adv_enabled == 0) {
        int err = ble_mgmt_adv_start();
        if (err) {
            LOG_ERR("Fehler beim Neustart des Advertisings (err %d)", err);
        }
    }
}
/* ============================================================================
   GATT Handlers
   ============================================================================ */

static ssize_t read_leader_config(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                 void *buf, uint16_t len, uint16_t offset)
{
    device_config_payload_t payload;
    memset(&payload, 0, sizeof(payload));

    payload.system_state = (uint8_t)game_mgmt_get_state();
    payload.game_id = game_mgmt_get_game_id();
    payload.pan_id = thread_mgmt_get_pan_id();
    payload.channel = thread_mgmt_get_channel();
    thread_mgmt_get_ext_pan_id(payload.ext_pan_id);
    thread_mgmt_get_network_key(payload.network_key);
    thread_mgmt_get_network_name(payload.network_name, sizeof(payload.network_name));
    strncpy(payload.node_name, lasertag_get_device_name(), 32);

	LOG_DBG("conf snd, name: " BOLD("%s") ", state: " BOLD("%d") ", game-id: " BOLD("0x%llx") ", net name: " BOLD("%s") ", channel: " BOLD("%u") ", pan: " BOLD("0x%04X"),
			payload.node_name,
			payload.system_state,
			payload.game_id,
			payload.network_name,
			payload.channel,
			payload.pan_id);
	LOG_HEXDUMP_DBG(payload.ext_pan_id, 8, "ext pan id");
	LOG_HEXDUMP_DBG(payload.network_key, 16, "network key");

    return bt_gatt_attr_read(conn, attr, buf, len, offset, &payload, sizeof(payload));
}

static ssize_t write_leader_config(struct bt_conn *conn, const struct bt_gatt_attr *attr,
								   const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
	if (len != sizeof(device_config_payload_t)) {
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
	}

    /* Copy data to buffer and delegate to system workqueue */
	memcpy(&pending_config, buf, sizeof(pending_config));
    k_work_submit(&config_apply_work);

    LOG_DBG("Config write received, delegated to workqueue.");
	return len;
}

/* Simple value handlers for name and type */
static ssize_t read_simple_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
							  void *buf, uint16_t len, uint16_t offset)
{
	const char *val_ptr = NULL;
	size_t val_len = 0;

	if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_TYPE_CHAR) == 0) {
		val_ptr = (char *)&device_role;
		val_len = sizeof(device_role);
	} else if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0) {
		val_ptr = lasertag_get_device_name();
		val_len = strlen(val_ptr);
	}
	return bt_gatt_attr_read(conn, attr, buf, len, offset, val_ptr, val_len);
}

static ssize_t write_name(struct bt_conn *conn, const struct bt_gatt_attr *attr,
						 const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
	int rc = lasertag_set_device_name(buf, len);
	if (rc == 0) bt_set_name(lasertag_get_device_name());
	return rc ? BT_GATT_ERR(BT_ATT_ERR_UNLIKELY) : len;
}

/* ============================================================================
   Service Definition
   ============================================================================ */
BT_GATT_SERVICE_DEFINE(provisioning_svc,
	BT_GATT_PRIMARY_SERVICE(BT_UUID_LT_PROV_SERVICE),
	BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NAME_CHAR,
						  BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
						  BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
						  read_simple_val, write_name, NULL),
	BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_TYPE_CHAR,
						  BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
						  read_simple_val, NULL, NULL),
	BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_CONFIG_CHAR,
						  BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
						  BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
						  read_leader_config, write_leader_config, NULL),
);

/* ============================================================================
   Advertising & Management
   ============================================================================ */
static uint8_t mfg_data[] = { 0xff, 0xff, 0x00 };
static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
    BT_DATA_BYTES(BT_DATA_UUID128_ALL, 
        0x00, 0x10, 0x0c, 0x82, 0xae, 0xc3, 0x89, 0x92, 
        0x22, 0x4a, 0x64, 0x6c, 0xcf, 0xe2, 0xaf, 0x03),
    BT_DATA(BT_DATA_MANUFACTURER_DATA, mfg_data, sizeof(mfg_data)), 
};

int ble_mgmt_init(uint8_t device_type)
{
	device_role = device_type;
    /* Initialize work structures */
	k_work_init_delayable(&adv_restart_work, adv_restart_work_handler);
    k_work_init(&config_apply_work, config_apply_work_handler);
	
	int err = bt_enable(NULL);
	if (err) return err;
	LOG_DBG("Bluetooth initialized successfully.");
	return 0;
}

int ble_mgmt_adv_start(void)
{
	mfg_data[2] = device_role;
	const char *name = lasertag_get_device_name();
	bt_set_name(name);
	struct bt_data sd[] = { BT_DATA(BT_DATA_NAME_COMPLETE, name, strlen(name)) };
	struct bt_le_adv_param param = {
		.options = (BT_LE_ADV_OPT_CONN | BT_LE_ADV_OPT_SCANNABLE),
		.interval_min = BT_GAP_ADV_FAST_INT_MIN_2,
		.interval_max = BT_GAP_ADV_FAST_INT_MAX_2,
	};
	int err = bt_le_adv_start(&param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
	if (!err) adv_enabled = 1;
	return err;
}

/**
 * Stop BLE advertising.
 * 
 * @return 0 on success, negative error code on failure
 */
int ble_mgmt_adv_stop(void)
{
	int err = bt_le_adv_stop();
	if (!err)
	{
		LOG_DBG("Advertising stopped");
		adv_enabled = 0;
	}
	return err;
}

/* ============================================================================
   BLE Connection Event Handlers
   ============================================================================ */

/**
 * Callback for when a device connects.
 * Logs the connection and updates advertising state.
 */
static void connected(struct bt_conn *conn, uint8_t err)
{
	if (err) {
		LOG_ERR("Verbindung fehlgeschlagen (err %u)", err);
	} else {
		LOG_DBG("Host verbunden");
		adv_enabled = 0;
	}
}

/**
 * Callback for when a device disconnects.
 * Logs the disconnection and schedules advertising restart.
 */
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
	LOG_DBG("Verbindung getrennt (Grund %u)", reason);
	k_work_reschedule(&adv_restart_work, K_MSEC(100));
}

/* Connection callbacks structure */
BT_CONN_CB_DEFINE(conn_callbacks) = {
	.connected = connected,
	.disconnected = disconnected,
};