#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/openthread.h>
#include <openthread/thread.h>
#include <openthread/dataset.h>
#include <openthread/instance.h>
#include <openthread/udp.h>
#include <openthread/coap.h>
#include <openthread/ip6.h>
#include <lasertag_utils.h>
#include <thread_mgmt.h>
#include <string.h>
#include <stdio.h>

LOG_MODULE_REGISTER(thread_mgmt, CONFIG_THREAD_MGMT_LOG_LEVEL);

#define UDP_PORT 1234
#define MAX_DISCOVERED_NODES 10

static otUdpSocket s_udp_socket;
static char discovered_nodes_list[256] = "";
static uint8_t node_count = 0;

/* --- CoAP Server Logik --- */

static void coap_id_handler(void *context, otMessage *message, const otMessageInfo *message_info)
{
    otError error = OT_ERROR_NONE;
    otMessage *response;
    otInstance *instance = (otInstance *)context;

    if (otCoapMessageGetCode(message) != OT_COAP_CODE_GET) {
        return;
    }

    LOG_INF("CoAP GET /id received");

    response = otCoapNewMessage(instance, NULL);
    if (response == NULL) return;

    otCoapMessageInitResponse(response, message, OT_COAP_TYPE_ACKNOWLEDGMENT, OT_COAP_CODE_CONTENT);
    otCoapMessageSetPayloadMarker(response);

    const char *name = lasertag_get_device_name();
    error = otMessageAppend(response, name, strlen(name));
    if (error != OT_ERROR_NONE) {
        otMessageFree(response);
        return;
    }

    error = otCoapSendResponse(instance, response, message_info);
    if (error != OT_ERROR_NONE) {
        otMessageFree(response);
    }
}

static otCoapResource s_id_resource = {
    .mUriPath = "id",
    .mHandler = coap_id_handler,
    .mContext = NULL,
    .mNext = NULL
};

/* --- CoAP Discovery Client Logik --- */

static void coap_discover_res_handler(void *context, otMessage *message, const otMessageInfo *message_info, otError result)
{
    if (result != OT_ERROR_NONE || otCoapMessageGetCode(message) != OT_COAP_CODE_CONTENT) {
        return;
    }

    char name_buf[32];
    uint16_t length = otMessageGetLength(message) - otMessageGetOffset(message);
    if (length > sizeof(name_buf) - 1) length = sizeof(name_buf) - 1;

    otMessageRead(message, otMessageGetOffset(message), name_buf, length);
    name_buf[length] = '\0';

    char addr_str[OT_IP6_ADDRESS_STRING_SIZE];
    otIp6AddressToString(&message_info->mPeerAddr, addr_str, sizeof(addr_str));

    LOG_INF("Node discovered: %s (%s)", name_buf, addr_str);

    /* Add to list (simple CSV format for BLE) */
    if (node_count < MAX_DISCOVERED_NODES && !strstr(discovered_nodes_list, name_buf)) {
        if (node_count > 0) strcat(discovered_nodes_list, ",");
        strcat(discovered_nodes_list, name_buf);
        node_count++;
    }
}

int thread_mgmt_discover_nodes(void)
{
    otInstance *instance = openthread_get_default_instance();
    otMessage *message;
    otMessageInfo message_info;
    otError error = OT_ERROR_NONE;

    /* Reset list */
    discovered_nodes_list[0] = '\0';
    node_count = 0;

    message = otCoapNewMessage(instance, NULL);
    if (message == NULL) return -ENOMEM;

    otCoapMessageInit(message, OT_COAP_TYPE_NON_CONFIRMABLE, OT_COAP_CODE_GET);
    otCoapMessageAppendUriPathOptions(message, "id");

    memset(&message_info, 0, sizeof(message_info));
    otIp6AddressFromString("ff03::1", &message_info.mPeerAddr);
    message_info.mPeerPort = OT_DEFAULT_COAP_PORT;

    error = otCoapSendRequest(instance, message, &message_info, coap_discover_res_handler, NULL);
    if (error != OT_ERROR_NONE) {
        otMessageFree(message);
        return -EIO;
    }

    LOG_INF("Discovery started...");
    return 0;
}

const char* thread_mgmt_get_discovered_list(void)
{
    return discovered_nodes_list;
}

/* --- UDP & Init --- */

static void udp_receive_cb(void *context, otMessage *message, const otMessageInfo *message_info)
{
    uint8_t buf[64];
    uint16_t length = otMessageGetLength(message) - otMessageGetOffset(message);
    if (length > sizeof(buf) - 1) length = sizeof(buf) - 1;

    int read = otMessageRead(message, otMessageGetOffset(message), buf, length);
    buf[read] = '\0';

    char addr_str[OT_IP6_ADDRESS_STRING_SIZE];
    otIp6AddressToString(&message_info->mPeerAddr, addr_str, sizeof(addr_str));

    LOG_INF("------------------------------------------");
    LOG_INF("UDP DATA RECEIVED!");
    LOG_INF("From: [%s]", addr_str);
    LOG_INF("Payload: %s", buf);
    LOG_INF("------------------------------------------");
}

int thread_mgmt_send_udp(const char *addr_str, uint8_t *payload, uint16_t len)
{
    struct otInstance *instance = openthread_get_default_instance();
    otMessage *message;
    otMessageInfo message_info;

    if (!instance) return -ENODEV;

    memset(&message_info, 0, sizeof(message_info));
    otIp6AddressFromString(addr_str, &message_info.mPeerAddr);
    message_info.mPeerPort = UDP_PORT;

    message = otUdpNewMessage(instance, NULL);
    if (message == NULL) return -ENOMEM;

    otMessageAppend(message, payload, len);
    otUdpSend(instance, &s_udp_socket, message, &message_info);

    LOG_INF("UDP sent to %s", addr_str);
    return 0;
}

int thread_mgmt_init(void)
{
    struct otInstance *instance = openthread_get_default_instance();
    otOperationalDataset dataset;

    if (!instance) return -ENODEV;

    /* Dataset Setup */
    memset(&dataset, 0, sizeof(otOperationalDataset));
    dataset.mActiveTimestamp.mSeconds = 1;
    dataset.mComponents.mIsActiveTimestampPresent = true;
    
    const char *net_name = lasertag_get_thread_network_name();
    memcpy(dataset.mNetworkName.m8, net_name, strlen(net_name));
    dataset.mComponents.mIsNetworkNamePresent = true;
    dataset.mPanId = lasertag_get_thread_pan_id();
    dataset.mComponents.mIsPanIdPresent = true;
    dataset.mChannel = lasertag_get_thread_channel();
    dataset.mComponents.mIsChannelPresent = true;
    memcpy(dataset.mExtendedPanId.m8, lasertag_get_thread_ext_pan_id(), 8);
    dataset.mComponents.mIsExtendedPanIdPresent = true;
    memcpy(dataset.mNetworkKey.m8, lasertag_get_thread_network_key(), 16);
    dataset.mComponents.mIsNetworkKeyPresent = true;
    uint8_t ml_prefix[] = {0xfd, 0xde, 0xad, 0x00, 0xbe, 0xef, 0x00, 0x00};
    memcpy(dataset.mMeshLocalPrefix.m8, ml_prefix, 8);
    dataset.mComponents.mIsMeshLocalPrefixPresent = true;

    otDatasetSetActive(instance, &dataset);
    otIp6SetEnabled(instance, true);
    otThreadSetEnabled(instance, true);

    /* UDP Initialisierung */
    otSockAddr listen_addr;
    memset(&listen_addr, 0, sizeof(listen_addr));
    listen_addr.mPort = UDP_PORT;
    otUdpOpen(instance, &s_udp_socket, udp_receive_cb, NULL);
    otUdpBind(instance, &s_udp_socket, &listen_addr, OT_NETIF_UNSPECIFIED);

    /* CoAP Initialisierung */
    otCoapStart(instance, OT_DEFAULT_COAP_PORT);
    s_id_resource.mContext = instance;
    otCoapAddResource(instance, &s_id_resource);

    LOG_INF("Thread MGMT: Initialized, UDP %d & CoAP %d open.", UDP_PORT, OT_DEFAULT_COAP_PORT);
    return 0;
}