/*
 * Created by jiaqiang.ye@lnxall.com
 *
 * Template info collector
 *
 * 2024/07/04
 */

#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <glob.h>

#include "template_info.h"
#include "tag.h"
#include <dy_utils/lnxall_buffer.h>

#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>

#define FETCH_INFO             "fetch_info"

int load_string_from_lua(const char * path, const char * funcname, char * * outp)
{
    char * lbuf;
    int rval, fd, ret;
    lua_State * L;
    struct stat lst;

    fd = -1;
    rval = 0;
    L = NULL;
    lbuf = NULL;
    if (path == NULL || funcname == NULL) {
        rval = -1;
        goto err0;
    }

    L = luaL_newstate();
    if (L == NULL) {
        rval = -1;
        goto err0;
    }
    luaL_openlibs(L);

    fd = open(path, O_RDONLY | O_CLOEXEC);
    if (fd == -1) {
        int error = errno;
        fprintf(stderr, "Error, failed to open %s: %s\n",
            path, strerror(error));
        fflush(stderr);

        rval = -1;
        goto err0;
    }

    ret = fstat(fd, &lst);
    if (ret < 0 || S_ISREG(lst.st_mode) == 0) {
        rval = -1;
        goto err0;
    }

    if (lst.st_size <= 0 || lst.st_size >= 0x100000) {
        fprintf(stderr, "Error, invalid size for %s: %lu\n",
            path, (unsigned long) lst.st_size);
        fflush(stderr);
        rval = -1;
        goto err0;
    }

    lbuf = (char *) malloc((size_t) (lst.st_size + 1));
    if (lbuf == NULL) {
        fprintf(stderr, "Error, system out of memory: %lu\n",
            (unsigned long) lst.st_size);
        fflush(stderr);
        rval = -1;
        goto err0;
    }

    if (read(fd, lbuf, (size_t) lst.st_size) != (ssize_t) lst.st_size) {
        free(lbuf); lbuf = NULL;
        fprintf(stderr, "Error, failed to read '%s'\n", path);
        fflush(stderr);
        rval = -1;
        goto err0;
    }

    close(fd); fd = -1;
    lbuf[lst.st_size] = '\0';
    do {
        char * p = strchr(lbuf, '\n');
        if (p == NULL)
            p = lbuf;
        else
            p++;

        ret = luaL_dostring(L, p);
        if (ret != 0) {
            free(lbuf); lbuf = NULL;
            fprintf(stderr, "Error, failed to load %s: %d\n", path, ret);
            fflush(stderr);
            rval = -1;
            goto err0;
        }

        free(lbuf);
        lbuf = NULL;
    } while (0);

    lua_settop(L, 0);
    lua_getglobal(L, funcname);
    ret = lua_gettop(L);
    if (ret != 1 || lua_type(L, 1) != LUA_TFUNCTION) {
        fprintf(stderr, "Error, failed to find function '%s'\n", funcname);
        fflush(stderr);
        rval = -1;
        goto err0;
    }

    ret = lua_pcall(L, 0, 1, 0);
    if (ret != 0) {
        fprintf(stderr, "Error, failed to run function '%s': %d\n",
            funcname, ret);
        fflush(stderr);
        rval = -1;
        goto err0;
    }

    ret = lua_gettop(L);
    if (ret <= 0 || lua_type(L, ret) != LUA_TSTRING) {
        fprintf(stderr, "Error, function has returned non-string: %s\n",
            funcname);
        fflush(stderr);
        rval = -1;
        goto err0;
    }

    do {
        char * str;
        size_t len = 0;
        const char * p = lua_tolstring(L, -1, &len);
        if (p == NULL) {
            fputs("Error, failed to get string.\n", stderr);
            fflush(stderr);
            rval = -1;
            goto err0;
        }

        str = (char *) malloc(len + 1);
        if (str == NULL) {
            fprintf(stderr, "Error, system out of memory: %u\n",
                (unsigned int) len);
            fflush(stderr);
            rval = -1;
        }

        if (len > 0)
            memcpy(str, p, len);

        rval = 0;
        str[len] = '\0';
        *outp = str;
    } while (0);

err0:
    if (fd != -1)
        close(fd);
    if (L != NULL)
        lua_close(L);
    return rval;
}

int template_info_collect(char* path, struct lnxall_buff* lbuff)
{
    if (path == NULL || lbuff == NULL) return -1;

    size_t len_t = strlen(path) + 1;

    char pattern[128]    = {0};
    char temp_info[2048] = {0};

    glob_t files;
    memset(&files, 0, sizeof(files));
    snprintf(pattern, sizeof(pattern), "%s/*.lua", path);

    if (glob(pattern, 0, NULL, &files) != 0)
    {
        ems_syslog(LOG_ERR, "Error, failed to glob %s: %s\n", pattern, strerror(errno));
        return -1;
    }
    for (int i = 0; i < files.gl_pathc; ++i)
    {
        if (files.gl_pathv[i] == NULL)
            continue;
        char* out = NULL;
        if (load_string_from_lua(files.gl_pathv[i], FETCH_INFO, &out) < 0)
        {
            ems_syslog(LOG_ERR, "Error, failed to get info from '%s'\n", files.gl_pathv[i]);
            continue;
        }
        int len = snprintf(temp_info, sizeof(temp_info), "{\"templete\":\"%s\",\"info\":%s},", files.gl_pathv[i] + len_t, out);
        lbuff_append(lbuff, temp_info, len);
        free(out);
    }
    globfree(&files);
    return 0;
}

static char* get_template_info(char* path)
{
    cJSON* root               = NULL;
    cJSON* temp_info          = NULL;
    char*  str                = NULL;
    char*  pdata              = read_file_data(path);
    if (pdata == NULL)
    {
        proto_syslog(LOG_ERR, "read cfg file %s error", path);
        goto END;
    }
    root = json_parse_string_with_comments(pdata);
    if (!root)
    {
        proto_syslog(LOG_ERR, "parse pdata %s error", pdata);
        goto END;
    }
    temp_info = cJSON_GetObjectItem(root, "info"); // 使用cJSON_GetObjectItemCaseSensitive会内存访问越界不知道为啥
    if (temp_info == NULL)
    {
        proto_syslog(LOG_ERR, "get temp info fail:%s", path);
        goto END;
    }
    str = cJSON_PrintUnformatted(temp_info);
END:
    if (pdata) free(pdata);
    if (root) cJSON_Delete(root);
    return str;
}

int get_all_temp_info(char* path, struct lnxall_buff* str)
{
    if (path == NULL || str == NULL) return -1;

    size_t len_t           = strlen(path) + 1;
    char   pattern[128]    = {0};
    char   temp_info[2048] = {0};

    glob_t files;
    memset(&files, 0, sizeof(files));
    snprintf(pattern, sizeof(pattern), "%s/*.json", path);

    if (glob(pattern, 0, NULL, &files) != 0)
    {
        ems_syslog(LOG_ERR, "Error, failed to glob %s: %s\n", pattern, strerror(errno));
        return -1;
    }
    for (int i = 0; i < files.gl_pathc; ++i)
    {
        if (files.gl_pathv[i] == NULL)
            continue;

        memset(temp_info, 0, sizeof(temp_info));

        char* template_info = get_template_info(files.gl_pathv[i]);

        if (template_info != NULL)
        {
            int len = snprintf(temp_info, sizeof(temp_info), "{\"templete\":\"%s\",\"info\":%s},", files.gl_pathv[i] + len_t, template_info);
            lbuff_append(str, temp_info, len);
            free(template_info);
        }
    }
    globfree(&files);
    return 0;
}
