#include "alarm_db.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/syslog.h>
#include <sys/time.h>
#include <cJSON.h>
#include <inttypes.h>
#include "heartbeat.h"

static bmser_alarm_control_t* bmser_alarm_ctrl = NULL;

static int bmser_init_db(void);
static int bmser_close_db(void);
static int bmser_insert_alarm(bmser_alarm_t* alarm);
static int bmser_alarm_db_delete(void);

static uint64_t get_time_stamp_ms(void)
{
    struct timeval te; 
    gettimeofday(&te, NULL); // 获取当前时间
    return te.tv_sec*1000LL + te.tv_usec/1000;
}

int get_entity_by_id(int bankid, int rackid, char* entity)
{
    for(int i = 0; i < heartbeat_list.device_count;i++)
    {
        device_t* dev = &heartbeat_list.device[i];
        if(((dev->no >> 16 )& 0xFFFF) == bankid && ((dev->no)& 0xFFFF) == rackid)
        {
            strcpy(entity, dev->dev_no);
            return 0;
        }
    }
    return -1;
}

void on_message(struct mosquitto* mosq, void* obj, const struct mosquitto_message* mqtt_msg)
{
    if (bmser_alarm_ctrl == NULL || bmser_alarm_ctrl->db == NULL)
    {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return;
    }

    cJSON *root = NULL;

    char* topic = mqtt_msg->topic;
    char* payload = mqtt_msg->payload;
    char sn[32], dev_sn[32], entity[32] = {0};
    sscanf(topic, "ipc/%[^/]/bms/%[^/]/data/Set/ImmediateAlarm", sn, dev_sn);

    root = cJSON_Parse(payload);
    if (root == NULL)
    {
        fprintf(stderr, "cJSON_Parse failed");
        goto END;
    }
    cJSON *frames = cJSON_GetObjectItem(root, "frames");

    if (frames == NULL)
    {
        fprintf(stderr, "cJSON_GetObjectItem failed");
        goto END;
    }
    uint64_t raise_time = get_time_stamp_ms();
    bmser_alarm_t alarm;
    int      size       = cJSON_GetArraySize(frames);
    for (int i = 0; i < size; i++)
    {
        cJSON* frame = cJSON_GetArrayItem(frames, i);
        if (frame == NULL) continue;

        cJSON* regs = cJSON_GetObjectItem(frame, "regs");
        if (regs == NULL) continue;

        int size = cJSON_GetArraySize(regs);
        for (int i = 0; i < size; i++)
        {
            cJSON* reg = cJSON_GetArrayItem(regs, i);
            if (reg == NULL) continue;

            cJSON* bankid = cJSON_GetObjectItem(reg, "bankid");
            cJSON* rackid = cJSON_GetObjectItem(reg, "rackid");

            if( bankid == NULL || rackid == NULL || rackid->valueint == 0) continue;

            memset(&alarm, 0, sizeof(alarm));
            alarm.raise_time = raise_time;

            if(get_entity_by_id(bankid->valueint, rackid->valueint, alarm.entity) < 0)
            {
                fprintf(stderr, "Cannot find entity, bankid:%d, rackid:%d", bankid->valueint, rackid->valueint);
            }

            alarm.frame = cJSON_PrintUnformatted(reg);
            if (bmser_insert_alarm(&alarm) < 0)
            {
                fprintf(stderr, "bmser_insert_alarm failed,sn:%s, entity:%s, frame:%s", sn, entity, alarm.frame);
            }
            free(alarm.frame);
        }
    }
END:
    if (root) cJSON_Delete(root);
    bmser_alarm_db_delete();
}

static int bmser_alarm_init_mqtt();


int bmser_alarm_control_start(void)
{
    if (bmser_alarm_ctrl != NULL)
    {
        fprintf(stderr, "has been activated");
        return 0;
    }

    bmser_alarm_ctrl = (bmser_alarm_control_t*)malloc(sizeof(bmser_alarm_control_t));
    if (bmser_alarm_ctrl == NULL)
    {
        fprintf(stderr, "bmser_alarm_ctrl malloc failed");
        goto ERROR;
    }

    if (bmser_alarm_init_mqtt() < 0)
    {
        fprintf(stderr, "bmser_alarm_init_mqtt failed");
        goto ERROR;
    }

    if (bmser_init_db() != 0)
    {
        fprintf(stderr, "bmser_init_db failed");
        goto ERROR;
    }

    return 0;
ERROR:
    if (bmser_alarm_ctrl) free(bmser_alarm_ctrl);
    bmser_close_db();
    bmser_alarm_ctrl = NULL;
    return -1;
}

void on_connect(struct mosquitto *mosq, void *obj, int rc) {
    if(rc == 0){
        printf("Connected\n");
        // 自动恢复之前的订阅
        mosquitto_subscribe(mosq, NULL, "ipc/+/bms/+/data/Set/ImmediateAlarm", 2);
    }
}


static int bmser_alarm_init_mqtt()
{
    if (bmser_alarm_ctrl == NULL)
    {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return -1;
    }
    struct mosquitto* mosq = mosquitto_new(BMSER_DB_NAME, false, bmser_alarm_ctrl);
    if (mosq == NULL)
    {
        fprintf(stderr, "creat default!!");
        goto ERROR;
    }
    bmser_alarm_ctrl->mqtt = mosq;
    mosquitto_connect(mosq, BMSER_MQTT_HOST, BMSER_MQTT_PORT, BMSER_MQTT_KEEP_ALIVE_MAX);
    mosquitto_reconnect_delay_set(mosq, 2, 300, true);
    mosquitto_message_callback_set(mosq, on_message);
    mosquitto_connect_callback_set(mosq, on_connect);
    mosquitto_loop_start(mosq);
    return 0;
ERROR:
    return -1;
}


#ifdef EN_TDENGINE
#include "tdengine/client/taos.h"

#define DEFINE_TAOS_HOST  "127.0.0.1"
#define DEFINE_TAOS_USERNAME "root"
#define DEFINE_TAOS_PASSWD "taosdata"
#define DEFINE_TAOS_PORT 6030

static int bmser_init_db(void)
{
    if(bmser_alarm_ctrl == NULL) {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return -1;
    }
    bmser_alarm_ctrl->db = taos_connect(DEFINE_TAOS_HOST, DEFINE_TAOS_USERNAME, DEFINE_TAOS_PASSWD, NULL, DEFINE_TAOS_PORT);
    if (bmser_alarm_ctrl->db == NULL)
    {
        fprintf(stderr, "tdengine connect failed");
        return -1;
    }

    char sql[1024] = {0};
    TAOS_RES* res = taos_query(bmser_alarm_ctrl->db, CREATE_ALARM_HISTORY_DB);
    if (res == NULL)
    {
        fprintf(stderr, "create alarm history db failed");
        return -1;
    }
    if (taos_errno(res) != 0)
    {
        fprintf(stderr, "create alarm history db failed, err=%s", taos_errstr(res));

        taos_free_result(res);
        return -1;
    }
    taos_free_result(res);
    snprintf(sql, sizeof(sql), "use %s", BMSER_DB_NAME);
    res = taos_query(bmser_alarm_ctrl->db, sql);
    if (res == NULL)
    {
        fprintf(stderr, "create alarm history db failed");
        return -1;
    }
    if (taos_errno(res) != 0)
    {
        fprintf(stderr, "create alarm history db failed, err=%s", taos_errstr(res));

        taos_free_result(res);
        return -1;
    }
    taos_free_result(res);
    res = taos_query(bmser_alarm_ctrl->db, TDENGINE_CREATE_BMSER_ALARM_TAB_SQL);
    if (res == NULL)
    {
        fprintf(stderr, "create alarm table failed");
        return -1;
    }
    if (taos_errno(res) != 0)
    {
        fprintf(stderr, "create alarm table failed, err=%s", taos_errstr(res));
        taos_free_result(res);
        return -1;
    }
    taos_free_result(res);
    return 0;
}

static int bmser_close_db(void)
{
    if(bmser_alarm_ctrl == NULL) {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return -1;
    }

    if (bmser_alarm_ctrl->db) {
        taos_close(bmser_alarm_ctrl->db);
        bmser_alarm_ctrl->db = NULL;
    }

    return 0;
}

static int bmser_insert_alarm(bmser_alarm_t* alarm)
{
    if (bmser_alarm_ctrl == NULL || bmser_alarm_ctrl->db == NULL)
    {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return -1;
    }

    char* sql = malloc(10240);
    if (sql == NULL)
    {
        fprintf(stderr, "malloc failed");
        return -1;
    }

    snprintf(sql, 10240, INSERT_BMSER_ALARM_SQL, alarm->raise_time, alarm->entity, alarm->frame);
    TAOS_RES* res = taos_query(bmser_alarm_ctrl->db, sql);
    if (res == NULL)
    {
        fprintf(stderr, "insert alarm failed");
        free(sql);
        return -1;
    }
    if (taos_errno(res) != 0)
    {
        fprintf(stderr, "insert alarm failed, err=%s", taos_errstr(res));
        free(sql);
        taos_free_result(res);
        return -1;
    }
    taos_free_result(res);
    free(sql);
    return 0;
}

int bmser_alarm_db_delete()
{
    if (bmser_alarm_ctrl == NULL || bmser_alarm_ctrl->db == NULL)
    {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return -1;
    }
    int  ret      = -1;
    char sql[512] = {0};
    sprintf(sql, TAOS_GET_DEL_BMSER_HISTORY_TIME, BMSER_MAX_ALARM_CNT);
    TAOS_RES* res = taos_query(bmser_alarm_ctrl->db, sql);
    if (res == NULL || taos_errno(res) != 0)
    {
        if (res != NULL) fprintf(stderr, "Failed to query alarm history: %s", taos_errstr(res));
        goto END;
    }
    TAOS_ROW row = taos_fetch_row(res);
    if (row == NULL)
    {
        goto END;
    }
    int64_t del_time = *(int64_t*)row[0];
    taos_free_result(res);
    sprintf(sql, TAOS_DEL_BMSER_HISTORY_SQL, del_time);
    res = taos_query(bmser_alarm_ctrl->db, sql);
    if (res == NULL || taos_errno(res) != 0)
    {
        if (res != NULL) fprintf(stderr, "Failed to query alarm history: %s", taos_errstr(res));
        goto END;
    }
    ret = 0;

END:
    if (res != NULL) taos_free_result(res);
    return ret;
}

#else
#include "sqlite3.h"

static int bmser_init_db(void)
{
    if(bmser_alarm_ctrl == NULL) {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return -1;
    }

    int ret = sqlite3_open(BMSER_ALARM_DB_PATH, (sqlite3**)(&bmser_alarm_ctrl->db));
    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "sqlite3 open failed, ret=%d", ret);
        return -1;
    }

    ret = sqlite3_exec(bmser_alarm_ctrl->db, SQLITE_CREATE_ALARM_TAB_SQL, NULL, NULL, NULL);
    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "create alarm table failed, ret=%d", ret);
        return -1;
    }
    
    return 0;
}

static int bmser_close_db(void)
{
    if(bmser_alarm_ctrl == NULL) {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return -1;
    }


    return 0;
}

static int bmser_insert_alarm(bmser_alarm_t* alarm)
{
    if (bmser_alarm_ctrl == NULL || bmser_alarm_ctrl->db == NULL)
    {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return -1;
    }
    char* errmsg = NULL;
    char* sql = malloc(10240);
    if (sql == NULL)
    {
        fprintf(stderr, "malloc failed");
        return -1;
    }
    snprintf(sql, 10240, INSERT_BMSER_ALARM_SQL, alarm->raise_time, alarm->entity, alarm->frame);
    int ret = sqlite3_exec(bmser_alarm_ctrl->db, sql, NULL, NULL,  &errmsg);
    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "insert alarm failed, ret=%d, err:%s", ret, errmsg);
        goto END;
    }

    ret = 0;

END:
    if (sql) free(sql);
    return ret;
}

int bmser_alarm_db_delete()
{
    if (bmser_alarm_ctrl == NULL || bmser_alarm_ctrl->db == NULL)
    {
        fprintf(stderr, "bmser_alarm_ctrl is null");
        return -1;
    }
    char sql[512] = {0};
    snprintf(sql, sizeof(sql), DELETE_BMSER_ALARM_HISTORY_SQL, BMSER_MAX_ALARM_CNT);
    char* errmsg = NULL;
    int   ret    = sqlite3_exec(bmser_alarm_ctrl->db, sql, NULL, NULL, &errmsg);
    if (ret != SQLITE_OK)
    {
        fprintf(stderr, "delete staledated alarm failed: %s", errmsg);
        sqlite3_free(errmsg);
    }
    return ret;
}

#endif // BMSER_USE_TAOS
