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

#ifdef _WIN32
#  include <windows.h>
#else
#  include <dlfcn.h>
#endif

int ffi_load(Runtime *rt, const char *path) {
    if (rt->ffi_count >= 16) {
        snprintf(rt->errbuf, sizeof(rt->errbuf), "FFI: 已加载库数量达到上限");
        rt->has_error = 1;
        return -1;
    }
    void *lib = NULL;
#ifdef _WIN32
    lib = (void *)LoadLibraryA(path);
    if (!lib) {
        /* 自动补 .dll 后缀重试 */
        char tmp[300];
        snprintf(tmp, sizeof(tmp), "%s.dll", path);
        lib = (void *)LoadLibraryA(tmp);
    }
#else
    /* Linux: 把 Windows 测试用的 msvcrt 映射到系统 C 库 */
    const char *real_path = path;
    if (strcmp(path, "msvcrt") == 0) real_path = "libc.so.6";
    lib = dlopen(real_path, RTLD_LAZY);
    if (!lib) {
        char tmp[300];
        snprintf(tmp, sizeof(tmp), "%s.so", real_path);
        lib = dlopen(tmp, RTLD_LAZY);
    }
#endif
    if (!lib) {
        snprintf(rt->errbuf, sizeof(rt->errbuf), "FFI: 无法加载动态库 '%s'", path);
        rt->has_error = 1;
        return -1;
    }
    rt->ffi_libs[rt->ffi_count].lib = lib;
    snprintf(rt->ffi_libs[rt->ffi_count].path, sizeof(rt->ffi_libs[0].path), "%s", path);
    rt->ffi_count++;
    return 0;
}

void *ffi_resolve(Runtime *rt, const char *name) {
    void *cached = map_get(&rt->ffi_syms, name);
    if (cached) return cached;
#ifndef _WIN32
    /* Linux: 把 Windows 专用的 _atoi64 映射到标准 C 库 atoll */
    const char *real_name = name;
    if (strcmp(name, "_atoi64") == 0) real_name = "atoll";
#else
    const char *real_name = name;
#endif
    for (int i = 0; i < rt->ffi_count; ++i) {
#ifdef _WIN32
        void *p = (void *)GetProcAddress((HMODULE)rt->ffi_libs[i].lib, name);
#else
        void *p = dlsym(rt->ffi_libs[i].lib, real_name);
#endif
        if (p) {
            map_set(&rt->ffi_syms, name, p);
            return p;
        }
    }
    return NULL;
}

/* ---- v1.1 FFI 真沙箱: operator allowlist ---- */

void *ffi_resolve_allowed(Runtime *rt, const char *name) {
    /* v1.1: 符号必须命中白名单 (operator 通过 cvmruntime.ffi 预先授权) */
    if (rt->ffi_allow_sym.count == 0 || !map_get(&rt->ffi_allow_sym, name)) {
        /* 白名单空 = 无配置文件 = 运维未授权任何 FFI; 或符号不在白名单内 */
        if (rt->ffi_allow_sym.count == 0) {
            snprintf(rt->errbuf, sizeof(rt->errbuf),
                "FFI 未启用 (运维未配置 cvmruntime.ffi)");
        } else {
            snprintf(rt->errbuf, sizeof(rt->errbuf),
                "FFI 符号未授权 (不在 cvmruntime.ffi 白名单): %s", name);
        }
        rt->has_error = 1;
        return NULL;
    }
    return ffi_resolve(rt, name);
}

int rt_load_ffi_allowfile(Runtime *rt, const char *filename) {
    FILE *fp = fopen(filename, "r");
    if (!fp) return -1;  /* 文件不存在 = 无白名单, FFI 维持全拒绝, 非错误 */

    char line[512];

    while (fgets(line, sizeof(line), fp)) {
        /* 去尾 \n/\r */
        size_t len = strlen(line);
        while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r')) line[--len] = '\0';

        /* 跳过空行和注释 */
        char *s = line;
        while (*s == ' ' || *s == '\t') s++;
        if (s[0] == '\0' || s[0] == '#') continue;

        /* 格式: <lib_path> : <sym1,sym2,...> */
        char *colon = strchr(s, ':');
        if (!colon) {
            snprintf(rt->errbuf, sizeof(rt->errbuf),
                "cvmruntime.ffi 格式错误 (缺 ':' 分隔): %s", s);
            rt->has_error = 1; fclose(fp); return -1;
        }

        /* 截取 lib_path (去尾部空格) */
        char *lib = s;
        while (lib < colon && (*lib == ' ' || *lib == '\t')) lib++;  /* 跳行首空格 */
        char *lib_end = colon;
        while (lib_end > lib && (lib_end[-1] == ' ' || lib_end[-1] == '\t')) lib_end--;
        *lib_end = '\0';
        if (lib_end == lib) {
            snprintf(rt->errbuf, sizeof(rt->errbuf),
                "cvmruntime.ffi 库路径为空");
            rt->has_error = 1; fclose(fp); return -1;
        }

        /* 解析符号列表 (逗号分隔) */
        char *symlist = colon + 1;

        /* 创建该库的子 Map (存储授权符号) */
        Map *sub = (Map *)calloc(1, sizeof(Map));
        if (!sub) { fclose(fp); return -1; }
        map_init(sub);

        char *tok = strtok(symlist, ",");
        while (tok) {
            /* trim 空格 */
            while (*tok == ' ' || *tok == '\t') tok++;
            char *end = tok + strlen(tok);
            while (end > tok && (end[-1] == ' ' || end[-1] == '\t')) end--;
            *end = '\0';

            if (tok[0] != '\0') {
                map_set(sub, tok, (void *)1);
                /* 同时写入扁平快速查找表 */
                map_set(&rt->ffi_allow_sym, tok, (void *)1);
            }
            tok = strtok(NULL, ",");
        }

        /* 将子 Map 注册到 ffi_allow[lib] */
        map_set(&rt->ffi_allow, lib, (void *)sub);
    }

    fclose(fp);

    /* 白名单加载成功 → 开启 FFI (受控模式下, 仅白名单内符号可用) */
    rt->ffi_enabled = 1;
    return 0;
}

void ffi_close(void *lib) {
    if (!lib) return;
#ifdef _WIN32
    FreeLibrary((HMODULE)lib);
#else
    dlclose(lib);
#endif
}