#include "ota_offer_protocol.h"

#include "sdkconfig.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static volatile bool s_query_sent_boot_s;
static volatile bool s_ota_armed_s;
static ota_offer_done_cb_t s_done_cb_s;

void ota_offer_reset_boot(void)
{
    s_query_sent_boot_s = false;
    s_ota_armed_s = false;
}

bool ota_offer_is_armed(void)
{
    return s_ota_armed_s;
}

void ota_offer_disarm(void)
{
    s_ota_armed_s = false;
}

static void ota_offer_format_device_id(char *out, size_t cap, const char *device_id)
{
    const char *dev = (device_id != NULL) ? device_id : "";
    snprintf(out, cap, "%s", dev);
}

static char *ota_offer_build(int message_type, cJSON *body)
{
    if (!body) {
        return NULL;
    }

    cJSON *root = cJSON_CreateObject();
    cJSON *head = cJSON_CreateObject();
    if (!root || !head) {
        cJSON_Delete(root);
        cJSON_Delete(head);
        cJSON_Delete(body);
        return NULL;
    }

    cJSON_AddNumberToObject(head, "message_type", message_type);
    cJSON_AddItemToObject(root, "head", head);
    cJSON_AddItemToObject(root, "body", body);

    char *out = cJSON_PrintUnformatted(root);
    cJSON_Delete(root);
    return out;
}

char *ota_offer_query_json_malloc(const char *device_id)
{
    cJSON *body = cJSON_CreateObject();
    if (!body) {
        return NULL;
    }

    char id_full[64];
    ota_offer_format_device_id(id_full, sizeof(id_full), device_id);

    cJSON_AddStringToObject(body, "phase", "query");
    cJSON_AddStringToObject(body, "device_ID", id_full);
    cJSON_AddStringToObject(body, "version", CONFIG_MY_APP_VERSION);

    return ota_offer_build(OTA_MSG_DEVICE_OFFER, body);
}

char *ota_offer_result_json_malloc(const char *device_id,
                                   bool success,
                                   uint32_t bytes_written,
                                   const char *err_step)
{
    cJSON *body = cJSON_CreateObject();
    if (!body) {
        return NULL;
    }

    char id_full[64];
    ota_offer_format_device_id(id_full, sizeof(id_full), device_id);

    cJSON_AddStringToObject(body, "phase", "done");
    cJSON_AddNumberToObject(body, "ok", success ? 1 : 0);
    cJSON_AddStringToObject(body, "device_ID", id_full);
    cJSON_AddStringToObject(body, "version", CONFIG_MY_APP_VERSION);
    cJSON_AddNumberToObject(body, "written", (double)bytes_written);
    if (!success && err_step != NULL && err_step[0] != '\0') {
        cJSON_AddStringToObject(body, "err", err_step);
    }

    return ota_offer_build(OTA_MSG_DEVICE_OFFER, body);
}

bool ota_offer_parse_phone_response(cJSON *root)
{
    if (!root) {
        return false;
    }

    cJSON *head = cJSON_GetObjectItem(root, "head");
    if (!head || !cJSON_IsObject(head)) {
        return false;
    }

    cJSON *m_type = cJSON_GetObjectItem(head, "message_type");
    if (!m_type || !cJSON_IsNumber(m_type)) {
        return false;
    }

    if ((int)m_type->valuedouble == OTA_MSG_PHONE_OTA_START) {
        s_ota_armed_s = true;
        return true;
    }

    return false;
}

bool ota_offer_mark_query_sent(void)
{
    if (s_query_sent_boot_s) {
        return false;
    }
    s_query_sent_boot_s = true;
    return true;
}

void ota_offer_reset_query_sent(void)
{
    s_query_sent_boot_s = false;
}

void ota_offer_set_done_callback(ota_offer_done_cb_t cb)
{
    s_done_cb_s = cb;
}

void ota_offer_notify_done(bool success, uint32_t bytes_written, const char *err_step)
{
    s_ota_armed_s = false;
    if (s_done_cb_s) {
        s_done_cb_s(success, bytes_written, err_step);
    }
}
