#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/shell/shell.h>
#include <zephyr/sys/reboot.h>
#include <zephyr/settings/settings.h>
#include <string.h>
#include <stdlib.h>
#include <lasertag_utils.h>
#include <ble_mgmt.h>
#include <thread_mgmt.h>
#include <stdio.h>

LOG_MODULE_REGISTER(lasertag_utils, CONFIG_LASERTAG_UTILS_LOG_LEVEL);

static char device_name[32] = "UnknownDevice";
static uint16_t thread_pan_id = 0xabcd;
static char thread_network_name[17] = "OpenThread-nRF";
static uint8_t thread_channel = 15;
static uint8_t thread_ext_pan_id[8] = {0xde, 0xad, 0xbe, 0xef, 0xca, 0xfe, 0xba, 0xbe};
static uint8_t thread_network_key[16] = {
    0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
    0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
};

/* --- Settings Handler --- */

static int lasertag_settings_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg)
{
    const char *next;
    if (settings_name_steq(name, "name", &next) && !next) {
        if (len > sizeof(device_name) - 1) return -EINVAL;
        ssize_t rc = read_cb(cb_arg, device_name, len);
        if (rc >= 0) { device_name[rc] = '\0'; return 0; }
    }
    if (settings_name_steq(name, "pan_id", &next) && !next) {
        return read_cb(cb_arg, &thread_pan_id, sizeof(thread_pan_id)) >= 0 ? 0 : -EIO;
    }
    if (settings_name_steq(name, "net_name", &next) && !next) {
        if (len > sizeof(thread_network_name) - 1) return -EINVAL;
        ssize_t rc = read_cb(cb_arg, thread_network_name, len);
        if (rc >= 0) { thread_network_name[rc] = '\0'; return 0; }
    }
    if (settings_name_steq(name, "channel", &next) && !next) {
        return read_cb(cb_arg, &thread_channel, sizeof(thread_channel)) >= 0 ? 0 : -EIO;
    }
    if (settings_name_steq(name, "ext_pan_id", &next) && !next) {
        return read_cb(cb_arg, thread_ext_pan_id, sizeof(thread_ext_pan_id)) >= 0 ? 0 : -EIO;
    }
    if (settings_name_steq(name, "net_key", &next) && !next) {
        return read_cb(cb_arg, thread_network_key, sizeof(thread_network_key)) >= 0 ? 0 : -EIO;
    }
    return -ENOENT;
}

struct settings_handler lasertag_conf = { .name = "lasertag", .h_set = lasertag_settings_set };

void lasertag_utils_init(void)
{
    LOG_INF("==========================================");
    LOG_INF("Lasertag System - Common Lib v0.0.1");

    settings_subsys_init();
    settings_register(&lasertag_conf);
    settings_load();

    LOG_INF("Device Name  : %s", device_name);
    LOG_INF("Thread PAN   : 0x%04x", thread_pan_id);
    LOG_INF("Thread Name  : %s", thread_network_name);
    LOG_INF("Thread Chan  : %d", thread_channel);
    LOG_INF("==========================================");
}

/* Getters */
const char* lasertag_get_device_name(void) { return device_name; }
uint16_t lasertag_get_thread_pan_id(void) { return thread_pan_id; }
const char* lasertag_get_thread_network_name(void) { return thread_network_name; }
uint8_t lasertag_get_thread_channel(void) { return thread_channel; }
const uint8_t* lasertag_get_thread_ext_pan_id(void) { return thread_ext_pan_id; }
const uint8_t* lasertag_get_thread_network_key(void) { return thread_network_key; }

/* Setters */
int lasertag_set_device_name(const char *name, size_t len) {
    if (len >= sizeof(device_name)) len = sizeof(device_name) - 1;
    memcpy(device_name, name, len);
    device_name[len] = '\0';
    return settings_save_one("lasertag/name", device_name, len);
}
int lasertag_set_thread_pan_id(uint16_t pan_id) {
    thread_pan_id = pan_id;
    return settings_save_one("lasertag/pan_id", &thread_pan_id, sizeof(thread_pan_id));
}
int lasertag_set_thread_network_name(const char *name, size_t len) {
    if (len >= sizeof(thread_network_name)) len = sizeof(thread_network_name) - 1;
    memcpy(thread_network_name, name, len);
    thread_network_name[len] = '\0';
    return settings_save_one("lasertag/net_name", thread_network_name, len);
}
int lasertag_set_thread_channel(uint8_t channel) {
    thread_channel = channel;
    return settings_save_one("lasertag/channel", &thread_channel, sizeof(thread_channel));
}
int lasertag_set_thread_ext_pan_id(const uint8_t *ext_id) {
    memcpy(thread_ext_pan_id, ext_id, 8);
    return settings_save_one("lasertag/ext_pan_id", thread_ext_pan_id, 8);
}
int lasertag_set_thread_network_key(const uint8_t *key) {
    memcpy(thread_network_key, key, 16);
    return settings_save_one("lasertag/net_key", thread_network_key, 16);
}

/* --- Shell Commands --- */

#if CONFIG_LASERTAG_SHELL

static int lasertag_hex2bin(const char *hex, uint8_t *bin, size_t bin_len) {
    for (size_t i = 0; i < bin_len; i++) {
        char buf[3] = { hex[i*2], hex[i*2+1], '\0' };
        bin[i] = (uint8_t)strtoul(buf, NULL, 16);
    }
    return 0;
}

static int cmd_reboot(const struct shell *sh, size_t argc, char **argv) {
    shell_print(sh, "Rebooting...");
    sys_reboot(SYS_REBOOT_COLD);
    return 0;
}

static int cmd_name_set(const struct shell *sh, size_t argc, char **argv) {
    lasertag_set_device_name(argv[1], strlen(argv[1]));
    shell_print(sh, "Name gespeichert.");
    return 0;
}

static int cmd_thread_ping(const struct shell *sh, size_t argc, char **argv) {
    char msg[64];
    snprintf(msg, sizeof(msg), "Ping von %s", device_name);
    shell_print(sh, "Sende Multicast-Ping an ff03::1...");
    int rc = thread_mgmt_send_udp("ff03::1", (uint8_t*)msg, strlen(msg));
    if (rc) shell_error(sh, "Ping fehlgeschlagen (%d)", rc);
    return 0;
}

/* Subcommands definitions omitted for brevity, but they should include 'ping' */
static int cmd_thread_set_panid(const struct shell *sh, size_t argc, char **argv) {
    uint16_t pan = (uint16_t)strtoul(argv[1], NULL, 0);
    lasertag_set_thread_pan_id(pan);
    return 0;
}

static int cmd_thread_set_chan(const struct shell *sh, size_t argc, char **argv) {
    uint8_t chan = (uint8_t)strtoul(argv[1], NULL, 10);
    lasertag_set_thread_channel(chan);
    return 0;
}

SHELL_STATIC_SUBCMD_SET_CREATE(sub_thread,
    SHELL_CMD_ARG(panid, NULL, "Set PAN ID", cmd_thread_set_panid, 2, 0),
    SHELL_CMD_ARG(chan,  NULL, "Set channel", cmd_thread_set_chan, 2, 0),
    SHELL_CMD(ping,      NULL, "Send multicast ping", cmd_thread_ping),
    SHELL_SUBCMD_SET_END
);

static int cmd_ble_start(const struct shell *sh, size_t argc, char **argv) {
    return ble_mgmt_adv_start();
}

SHELL_STATIC_SUBCMD_SET_CREATE(sub_ble,
    SHELL_CMD(start, NULL, "Start BLE", cmd_ble_start),
    SHELL_SUBCMD_SET_END
);

SHELL_STATIC_SUBCMD_SET_CREATE(sub_lasertag,
    SHELL_CMD_ARG(name, NULL, "Set name", cmd_name_set, 2, 0),
    SHELL_CMD(thread, &sub_thread, "Thread configuration", NULL),
    SHELL_CMD(ble, &sub_ble, "BLE Management", NULL),
    SHELL_CMD(reboot, NULL, "Reboot", cmd_reboot),
    SHELL_SUBCMD_SET_END
);

SHELL_CMD_REGISTER(lasertag, &sub_lasertag, "Lasertag Befehle", NULL);

#endif