// 在文件开头添加（在所有#include之前）
#define _XOPEN_SOURCE 600 // 启用POSIX 2004标准API
#define _GNU_SOURCE
#include <dirent.h>  // 添加 dirent.h 头文件以支持目录操作
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "emd5.h"
#include <sys/stat.h>
#include <errno.h>
#include <libgen.h>
#include "egenmd5.h"
#include <fcntl.h>      // 添加 open/close/write 头文件
#include <unistd.h>     // 已存在 fsync
#include <sys/syscall.h>

// 修改：使用静态数组存储结果，移除动态内存分配
#define MAX_MD5_RESULTS 256  // 设置最大支持的文件数量

typedef struct {
    char filename[1024];  // 直接使用固定大小缓冲区
    char md5[33];
} Md5Result;

typedef struct {
    Md5Result results[MAX_MD5_RESULTS];
    size_t count;
} Md5ResultList;

// 初始化结果列表（静态实现）
void init_md5_result_list(Md5ResultList *list) {
    list->count = 0;
}

// void force_metadata_sync(const char* path) {
//     int fd = open(path, O_RDONLY);
//     if (fd >= 0) {
//         syscall(SYS_syncfs, fd); // Linux特有系统调用
//         close(fd);
//     }
//     sync(); // 双重保障
// }
// 添加结果（静态检查溢出）
int add_md5_result(Md5ResultList *list, const char *filename, const char *md5) {
    if (list->count >= MAX_MD5_RESULTS) {
        fprintf(stderr, "Too many files, MD5 result limit reached: %d\n", MAX_MD5_RESULTS);
        return -1; // 超出最大文件数限制
    }

    strncpy(list->results[list->count].filename, filename, sizeof(list->results[list->count].filename) - 1);
    list->results[list->count].filename[sizeof(list->results[list->count].filename) - 1] = '\0';
    strncpy(list->results[list->count].md5, md5, sizeof(list->results[list->count].md5) - 1);
    list->results[list->count].md5[sizeof(list->results[list->count].md5) - 1] = '\0';

    list->count++;
    return 0;
}

#define EGENMD5_VERSION "1.0.0"

// 在文件开头添加以下宏定义以简化日志输出
#define LOG_INFO() ;
// #define LOG_INFO() fprintf(stderr, "[%s %d]\r\n", __FUNCTION__, __LINE__)

// 提取路径最后一级目录名
char *get_last_dir_name(const char *path)
{
    static char result[512];   // 使用静态缓冲区避免 malloc
    char *copy = strdup(path); // 可能仍需要临时复制，但不暴露给外部释放
    if (copy == NULL)
    {
        LOG_INFO();
        return NULL;
    }

    size_t len = strlen(copy);
    while (len > 0 && copy[len - 1] == '/')
        copy[--len] = '\0';

    char *base = basename(copy);
    strncpy(result, base, sizeof(result) - 1);
    result[sizeof(result) - 1] = '\0';
    free(copy);
    return result;
}

// 计算文件MD5
static int compute_file_md5(const char *file_path, unsigned char *md5_value)
{
    LOG_INFO();

    if (!file_path || !md5_value)
    {
        LOG_INFO();
        return -1;
    }

    FILE *file = fopen(file_path, "rb");
    if (!file)
    {
        LOG_INFO();
        return -1;
    }

    MD5_CTX ctx;
    if (!e_MD5_Init(&ctx))
    { // 添加初始化状态检查
        LOG_INFO();
        fclose(file);
        return -1;
    }

    unsigned char buffer[1024];
    size_t bytes_read;

    while ((bytes_read = fread(buffer, 1, sizeof(buffer), file)) > 0)
    {
        LOG_INFO();
        if (!e_MD5_Update(&ctx, buffer, bytes_read))
        { // 添加安全检查
            LOG_INFO();
            fclose(file);
            return -1;
        }
        LOG_INFO();
    }
    LOG_INFO();

    if (!e_MD5_Final(md5_value, &ctx))
    { // 添加安全检查
        LOG_INFO();
        fclose(file);
        return -1;
    }
    LOG_INFO();

    fclose(file);
    return 0;
}

// 回调数据结构保持不变
struct callback_data {
    const char *base_dir;       // 基础目录路径
    const char *base_with_slash;// 带斜杠的基础目录
    const char *dir_prefix;     // 新增：要保留的目录前缀（如"files"")
    int output_fd;              // 修改：使用文件描述符替代 FILE*
};

// 自定义递归遍历函数
static int traverse_directory(const char *dir_path, struct callback_data *data, Md5ResultList *result_list) {
    DIR *dir = opendir(dir_path);
    if (!dir) {
        LOG_INFO();
        fprintf(stderr, "Failed to open directory: %s\n", dir_path);
        return -1;
    }

    struct dirent *entry;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
            continue;
        }

        char file_path[1024];
        snprintf(file_path, sizeof(file_path), "%s/%s", dir_path, entry->d_name);

        struct stat sb;
        if (lstat(file_path, &sb) == -1) {
            LOG_INFO();
            fprintf(stderr, "Failed to get file status: %s\n", file_path);
            continue;
        }

        if (S_ISDIR(sb.st_mode)) {
            traverse_directory(file_path, data, result_list);
        } else if (S_ISREG(sb.st_mode)) {
            unsigned char md5_value[MD5_DIGEST_LENGTH];
            if (compute_file_md5(file_path, md5_value) != 0) {
                LOG_INFO();
                fprintf(stderr, "Failed to compute MD5 for file: %s\n", file_path);
                continue;
            }

            char md5_string[33] = {0};
            for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
                sprintf(md5_string + i * 2, "%02x", md5_value[i]);
            }

            // 构建完整路径
            size_t base_len = strlen(data->base_with_slash);
            const char *rel_path = file_path + base_len;
            char full_path[1024] = {0};
            snprintf(full_path, sizeof(full_path), "%s/%s", data->dir_prefix, rel_path);

            // 缓存结果
            if (add_md5_result(result_list, full_path, md5_string) != 0) {
                LOG_INFO();
                fprintf(stderr, "Failed to add MD5 result, too many files\n");
                closedir(dir);
                return -1;
            }
            LOG_INFO();
            fprintf(stderr, "Cached MD5: %s  %s\n", md5_string, full_path);
        }
    }

    closedir(dir);
    return 0;
}

// 主函数：遍历目录并生成MD5记录
void generate_md5_for_directory(const char *dir_path, const char *output_path) {
    LOG_INFO();

    if (!dir_path || !output_path) {
        LOG_INFO();
        return;
    }

    // 使用静态缓冲区替代动态分配
    char base_with_slash[1024] = {0};
    strncpy(base_with_slash, dir_path, sizeof(base_with_slash) - 2);
    size_t len = strlen(base_with_slash);
    if (len > 0 && base_with_slash[len - 1] != '/') {
        strcat(base_with_slash, "/");
    }

    // 提取目录名
    char *dir_prefix = get_last_dir_name(dir_path);
    if (!dir_prefix) {
        LOG_INFO();
        fprintf(stderr, "Failed to extract directory prefix\n");
        return;
    }

    // 修改：使用 open 替代 fopen
    int output_fd = open(output_path, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, 0644);
    if (output_fd == -1) {
        LOG_INFO();
        fprintf(stderr, "Failed to open output file: %s\n", output_path);
        return;
    }

    struct callback_data data = {
        .base_dir = dir_path,
        .base_with_slash = base_with_slash,
        .dir_prefix = dir_prefix,
        .output_fd = output_fd
    };

    // 初始化结果列表（静态内存）
    Md5ResultList result_list;
    init_md5_result_list(&result_list);  // 修改：不再传入初始容量

    LOG_INFO();
    if (traverse_directory(dir_path, &data, &result_list) != 0) {
        LOG_INFO();
        fprintf(stderr, "Directory traversal failed for path: %s\n", dir_path);
    }

    // 统一写入文件
    for (size_t i = 0; i < result_list.count; i++) {
        char buffer[1024];
        int bytes = snprintf(buffer, sizeof(buffer), "%s  %s\n", result_list.results[i].md5, result_list.results[i].filename);
        if (write(output_fd, buffer, bytes) == -1) {
            LOG_INFO();
            fprintf(stderr, "Failed to write to output file\n");
        }
        LOG_INFO();
        fprintf(stderr, "Wrote to output file: %s  %s\n", result_list.results[i].md5, result_list.results[i].filename);
    }
    if (fsync(output_fd) == -1) { // 检查fsync结果
        printf("MD5 file fsync failed: %s", strerror(errno));
    }
    syncfs(output_fd);
    close(output_fd);  // 修改：使用 close 替代 fclose
    // 增加延迟（可选，针对慢速存储）
    usleep(50000); // 50ms延迟
    // 同步文件系统元数据
    // sync(); // 强制全局缓存刷新
    // force_metadata_sync(dir_path);
}
