/**
 * BLE Management Module (ble_mgmt.c)
 * 
 * Handles Bluetooth Low Energy (BLE) setup, advertising, GATT services,
 * and connection management for the Lasertag device.
 * 
 * Services provided:
 * - Provisioning Service (0x10xx): Device configuration and Thread mesh setup
 * - Game Service (0x20xx): Game-related commands and logging
 */

#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
   ============================================================================
   Base UUID: 03afe2cf-6c64-4a22-9289-c3ae820cXXXX
   Service and characteristic IDs use the last two bytes (XXXX)
   ========================================================================== */

#define LT_UUID_BASE_VAL \
	BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c0000)


/* PROVISIONING SERVICE (0x10xx): Device configuration & Thread mesh setup */
#define BT_UUID_LT_PROV_SERVICE BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x03afe2cf, 0x6c64, 0x4a22, 0x9289, 0xc3ae820c1000))

/* Provisioning characteristics */
#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))	


/* ============================================================================
   Data Structures
   ============================================================================ */

/**
 * Device configuration payload.
 * Packed structure for transmitting full device configuration via BLE.
 * Total size: 86 bytes
 */
struct device_config_payload {
    uint8_t system_state;     /* Offset 0 */
    uint64_t game_id;         /* Offset 1 */
    uint16_t pan_id;          /* Offset 9 */
    uint8_t channel;          /* Offset 11 */
    uint8_t ext_pan_id[8];    /* Offset 12 */
    uint8_t network_key[16];  /* Offset 20 */
    char network_name[17];    /* Offset 36 (16 chars + \0) */
    char node_name[33];       /* Offset 53 (32 chars + \0) */
} __packed;


/* ============================================================================
   Global State Variables
   ============================================================================ */
static uint8_t device_role = 0;              /* Store device type for provisioning */
static uint8_t adv_enabled = 0;              /* Track advertising state */
static struct k_work_delayable adv_restart_work; /* Delayed advertising restart */

/* ============================================================================
   Advertising Data
   ============================================================================ */
/* Manufacturer data: last byte contains device role type */
static uint8_t mfg_data[] = { 0xff, 0xff, 0x00 };

/* Advertising data array with UUID and flags */
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)), 
};


/* ============================================================================
   GATT Callback Handlers
   
   These functions handle read/write operations on GATT characteristics.
   They validate requests and interface with device configuration APIs.
   ============================================================================ */

/**
 * Read handler for provisioning characteristics.
 * Supports reading: device type, name, PAN ID, channel, extended PAN ID,
 * network key, and network name.
 */
static ssize_t read_lasertag_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);
}

/**
 * Write handler for provisioning characteristics.
 * Validates write length and updates device configuration via lasertag_utils APIs.
 * Also updates Bluetooth advertised name when device name is changed.
 */
static ssize_t write_lasertag_val(struct bt_conn *conn, const struct bt_gatt_attr *attr,
								  const void *buf, uint16_t len, uint16_t offset, uint8_t flags)
{
	int rc = 0;
	if (bt_uuid_cmp(attr->uuid, BT_UUID_LT_PROV_NAME_CHAR) == 0)
	{
		rc = lasertag_set_device_name(buf, len);
		if (rc == 0 ) {
			bt_set_name(lasertag_get_device_name());
		}
	}

	if (rc)
		return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
	return len;
}

/**
 * Read handler for the full leader configuration.
 * Returns a packed structure containing all device configuration in a single read.
 */
static ssize_t read_leader_config(struct bt_conn *conn, const struct bt_gatt_attr *attr,
                                 void *buf, uint16_t len, uint16_t offset)
{
    struct device_config_payload payload;
    memset(&payload, 0, sizeof(payload));

    payload.system_state = game_mgmt_get_state();
    payload.game_id = game_mgmt_get_game_id();
    payload.pan_id = lasertag_get_thread_pan_id();
    payload.channel = lasertag_get_thread_channel();
    
    memcpy(payload.ext_pan_id, lasertag_get_thread_ext_pan_id(), 8);
    memcpy(payload.network_key, lasertag_get_thread_network_key(), 16);
    
    /* Ensure null termination and copy strings */
    strncpy(payload.network_name, lasertag_get_thread_network_name(), 16);
    strncpy(payload.node_name, lasertag_get_device_name(), 32);

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

/**
 * Write handler for the full leader configuration.
 * Accepts a packed structure and applies all configuration at once.
 */
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(struct device_config_payload))
	{
		return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
	}

	const struct device_config_payload *payload = (const struct device_config_payload *)buf;

	/* Apply RAM values */
	if (payload->system_state != SYS_STATE_NO_CHANGE)
	{
		game_mgmt_set_state(payload->system_state);
	}
	if (payload->game_id != 0)
	{
		game_mgmt_set_game_id(payload->game_id);
	}

	/* Apply NVS values via utils */
	if (payload->node_name[0] != '\0') // Only update if a name is provided
	{
		lasertag_set_device_name((const void *)payload->node_name, strlen(payload->node_name));
		bt_set_name(lasertag_get_device_name());
	}

	if (payload->channel != 0)
	{
		lasertag_set_thread_pan_id(payload->pan_id);
		lasertag_set_thread_channel(payload->channel);
		lasertag_set_thread_ext_pan_id(payload->ext_pan_id);
		LOG_HEXDUMP_INF(payload->network_key, 16, "Setting new Thread network key:");
		lasertag_set_thread_network_key(payload->network_key);
		if (payload->network_name[0] != '\0') // Only update if a name is provided
		{
			lasertag_set_thread_network_name(payload->network_name, strlen(payload->network_name));
		}
		thread_mgmt_restart_thread_stack(false);
	}

	LOG_INF("Device config processed. Only changed values were applied.");
	return len;
}

/* ============================================================================
   GATT Service Definition
   
   Defines the Provisioning Service with all characteristics and callbacks.
   ============================================================================ */
BT_GATT_SERVICE_DEFINE(provisioning_svc,
					   BT_GATT_PRIMARY_SERVICE(BT_UUID_LT_PROV_SERVICE),

					   /* Device name (readable and writable) */
					   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_lasertag_val, write_lasertag_val, NULL),

					   /* Device type / role (read-only) */
					   BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_TYPE_CHAR,
											  BT_GATT_CHRC_READ,
											  BT_GATT_PERM_READ,
											  read_lasertag_val, NULL, NULL),

					   /* Thread PAN ID (read/write) - DISABLED */
					   /*
					   BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_PANID_CHAR,
											  BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
											  BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
											  read_lasertag_val, write_lasertag_val, NULL),
					   */

					   /* Thread channel (read/write) - DISABLED */
					   /*
					   BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_CHAN_CHAR,
											  BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
											  BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
											  read_lasertag_val, write_lasertag_val, NULL),
					   */

					   /* Extended PAN ID (read/write) - DISABLED */
					   /*
					   BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_EXTPAN_CHAR,
											  BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
											  BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
											  read_lasertag_val, write_lasertag_val, NULL),
					   */

					   /* Network key (read/write) - DISABLED */
					   /*
					   BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NETKEY_CHAR,
											  BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
											  BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
											  read_lasertag_val, write_lasertag_val, NULL),
					   */

					   /* Thread network name (read/write) - DISABLED */
					   /*
					   BT_GATT_CHARACTERISTIC(BT_UUID_LT_PROV_NETNAME_CHAR,
											  BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
											  BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
											  read_lasertag_val, write_lasertag_val, NULL),
					   */

					   /* Full leader configuration (packed struct) */
					   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), );


/* ============================================================================
   Internal Helper Functions
   ============================================================================ */

/**
 * Work handler for delayed advertising restart.
 * Called when a device disconnects to resume advertising after a brief delay.
 */
static void adv_restart_work_handler(struct k_work *work)
{
	if (adv_enabled == 0)
	{
		int err = ble_mgmt_adv_start();
		if (err) {
			LOG_ERR("Fehler beim verzögerten Neustarten des Advertisings (err %d)", err);
		} else {
			LOG_INF("Advertising nach Verzögerung erfolgreich neu gestartet");
		}
	}
}

/* ============================================================================
   Public Initialization & Management Functions
   ============================================================================ */

/**
 * Initialize the BLE module.
 * Enables Bluetooth and sets up the device role for advertising.
 * 
 * @param device_type The device type/role (leader, weapon, vest, beacon)
 * @return 0 on success, negative error code on failure
 */
int ble_mgmt_init(uint8_t device_type)
{
	device_role = device_type;
	k_work_init_delayable(&adv_restart_work, adv_restart_work_handler);
	
	int err = bt_enable(NULL);
	if (err)
		return err;
	LOG_INF("Bluetooth initialized");
	return 0;
}

/**
 * Start BLE advertising.
 * Advertises device name and type, configures fast advertising intervals.
 * 
 * @return 0 on success, negative error code on failure
 */
int ble_mgmt_adv_start(void)
{
	const char set_device_role = device_role;
	mfg_data[2] = set_device_role; // Update device role in advertising data

	const char *name = lasertag_get_device_name();
	bt_set_name(name);

	struct bt_data dynamic_sd[] = {
		BT_DATA(BT_DATA_NAME_COMPLETE, name, strlen(name)),
	};

	struct bt_le_adv_param adv_param = {
		.id = BT_ID_DEFAULT,
		.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(&adv_param, ad, ARRAY_SIZE(ad), dynamic_sd, ARRAY_SIZE(dynamic_sd));
	if (!err)
	{
		LOG_INF("Advertising started as: %s, type: %d", name, device_role);
		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_INF("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_INF("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_INF("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,
};