#include <dirent.h>
#include <json-c/json.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

#define MAX_FILES 20000
#define MAX_PATH 4096

static const char *extensions[] = {
    ".c", ".cpp", ".h", ".py", ".java", ".js", ".mk", ".html",
    "Makefile", ".css", ".json", ".cs", ".csproj", ".sln", ".toml", ".rs",
    ".go", ".rb", ".swift", ".php", ".pl", ".sh", ".bash", ".sql",
    ".xml", ".yaml", ".yml", ".kt", ".dart", ".scala", ".clj", ".asm",
    ".m", ".r", ".lua", ".groovy", ".v", ".pas", ".d", ".f90", ".f95",
    ".for", ".s", ".tcl", ".vhdl", ".verilog", ".coffee", ".less", ".scss",
    ".ps1", ".psm1", ".cmd", ".bat", ".json5", ".cxx", ".cc", ".hpp",
    ".hxx", ".inc", ".nsi", ".ninja", ".cmake", ".cmake.in", ".mk.in",
    ".make", ".makefile", ".gyp", ".gypi", ".pro", ".qml", ".ui", ".wxs",
    ".wxl", ".wxi", ".wxl", ".wxs", ".wxi", ".wxl", ".wxs", ".wxi"
};
static const size_t ext_count = sizeof(extensions) / sizeof(extensions[0]);

typedef struct {
    char name[MAX_PATH];
    char modification_date[20];
    char creation_date[20];
    char type[10];
    size_t size_bytes;
} FileInfo;

static FileInfo file_list[MAX_FILES];
static size_t file_count = 0;

static int is_valid_extension(const char *filename) {
    const char *dot = strrchr(filename, '.');
    if (!dot) dot = filename;
    for (size_t i = 0; i < ext_count; i++) {
        if (strcmp(dot, extensions[i]) == 0) return 1;
    }
    return 0;
}

static int is_ignored_directory(const char *dir_name) {
    const char *ignored_dirs[] = {"env", ".venv", "node_modules", "venv", "virtualenv"};
    for (size_t i = 0; i < sizeof(ignored_dirs) / sizeof(ignored_dirs[0]); i++) {
        if (strcmp(dir_name, ignored_dirs[i]) == 0) return 1;
    }
    return 0;
}

static void get_file_info(const char *path) {
    struct stat file_stat;
    if (stat(path, &file_stat) == 0) {
        FileInfo info;
        strncpy(info.name, path, MAX_PATH - 1);
        info.name[MAX_PATH - 1] = '\0';
        strftime(info.modification_date, sizeof(info.modification_date), "%Y-%m-%d %H:%M:%S", localtime(&file_stat.st_mtime));
        strftime(info.creation_date, sizeof(info.creation_date), "%Y-%m-%d %H:%M:%S", localtime(&file_stat.st_ctime));
        strncpy(info.type, S_ISDIR(file_stat.st_mode) ? "directory" : "file", sizeof(info.type) - 1);
        info.type[sizeof(info.type) - 1] = '\0';
        info.size_bytes = file_stat.st_size;
        file_list[file_count++] = info;
    }
}

char *index_directory(const char *dir_path) {
    DIR *dir = opendir(dir_path);
    if (!dir) {
        perror("Failed to open directory");
        return NULL;
    }

    struct dirent *entry;
    json_object *jarray = json_object_new_array();

    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue;
        if (entry->d_name[0] == '.' || is_ignored_directory(entry->d_name)) continue;

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

        if (entry->d_type == DT_DIR) {
            char *subdir_json = index_directory(full_path);
            if (subdir_json) {
                json_object *jsubdir = json_object_new_string(subdir_json);
                json_object_array_add(jarray, jsubdir);
                free(subdir_json);
            }
        } else if (is_valid_extension(entry->d_name)) {
            get_file_info(full_path);
            json_object *jfile = json_object_new_object();
            json_object_object_add(jfile, "file_name", json_object_new_string(file_list[file_count - 1].name));
            json_object_object_add(jfile, "modification_date", json_object_new_string(file_list[file_count - 1].modification_date));
            json_object_object_add(jfile, "creation_date", json_object_new_string(file_list[file_count - 1].creation_date));
            json_object_object_add(jfile, "type", json_object_new_string(file_list[file_count - 1].type));
            json_object_object_add(jfile, "size_bytes", json_object_new_int64(file_list[file_count - 1].size_bytes));

            json_object_array_add(jarray, jfile);
        }
    }
    closedir(dir);

    char *result = strdup(json_object_to_json_string(jarray));
    json_object_put(jarray);
    return result;
}