#include "vfs.h"
#include "../sys/kernel_subsystem.h"
#include "memory_manager.h"
#include "core/kutils.h"

typedef struct {
    kernel_subsystem_t *sub;
    subsystem_file_t *file;
    int offset;
} sysfs_handle_t;

static void* sysfs_open(void *fs_private, const char *path, const char *mode) {
    if (path[0] == '/') path++;
    if (path[0] == '\0') return NULL;

    kernel_subsystem_t *sub = NULL;
    int last_slash = -1;
    for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;

    if (last_slash != -1) {
        char prefix[64];
        k_memcpy(prefix, path, last_slash);
        prefix[last_slash] = 0;
        sub = subsystem_get_by_name(prefix);
        
        if (sub) {
            const char *filename = path + last_slash + 1;
            for (int j = 0; j < sub->file_count; j++) {
                if (k_strcmp(sub->files[j].name, filename) == 0) {
                    sysfs_handle_t *h = (sysfs_handle_t*)kmalloc(sizeof(sysfs_handle_t));
                    h->sub = sub;
                    h->file = &sub->files[j];
                    h->offset = 0;
                    return h;
                }
            }
        }
    }

    return NULL;
}

static void sysfs_close(void *fs_private, void *handle) {
    if (handle) kfree(handle);
}

static int sysfs_read(void *fs_private, void *handle, void *buf, int size) {
    sysfs_handle_t *h = (sysfs_handle_t*)handle;
    if (!h || !h->file || !h->file->read) return -1;

    int bytes = h->file->read((char*)buf, size, h->offset);
    if (bytes > 0) h->offset += bytes;
    return bytes;
}

static int sysfs_write(void *fs_private, void *handle, const void *buf, int size) {
    sysfs_handle_t *h = (sysfs_handle_t*)handle;
    if (!h || !h->file || !h->file->write) return -1;

    int bytes = h->file->write((const char*)buf, size, h->offset);
    if (bytes > 0) h->offset += bytes;
    return bytes;
}

static int sysfs_readdir(void *fs_private, const char *path, vfs_dirent_t *entries, int max) {
    if (path[0] == '/') path++;
    
    kernel_subsystem_t *exact_sub = subsystem_get_by_name(path);
    int out = 0;

    if (exact_sub) {
        for (int i = 0; i < exact_sub->file_count && out < max; i++) {
            k_strcpy(entries[out].name, exact_sub->files[i].name);
            entries[out].is_directory = 0;
            entries[out].size = 0;
            out++;
        }
    }

    int count = subsystem_get_count();
    int path_len = k_strlen(path);

    for (int i = 0; i < count && out < max; i++) {
        kernel_subsystem_t *s = subsystem_get_by_index(i);
        if (path_len == 0 || (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/')) {
            const char *sub_path = s->name + (path_len ? path_len + 1 : 0);
            char comp[64];
            int j = 0;
            while (sub_path[j] && sub_path[j] != '/' && j < 63) {
                comp[j] = sub_path[j];
                j++;
            }
            comp[j] = 0;

            if (comp[0] == '\0') continue;

            bool found = false;
            for (int k = 0; k < out; k++) {
                if (k_strcmp(entries[k].name, comp) == 0) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                k_strcpy(entries[out].name, comp);
                entries[out].is_directory = 1;
                entries[out].size = 0;
                out++;
            }
        }
    }
    return out;
}

static bool sysfs_exists(void *fs_private, const char *path) {
    if (path[0] == '/') path++;
    if (path[0] == '\0') return true;

    if (subsystem_get_by_name(path)) return true;

    // File check
    int last_slash = -1;
    for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
    if (last_slash != -1) {
        char prefix[64];
        k_memcpy(prefix, path, last_slash);
        prefix[last_slash] = 0;
        kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
        if (sub) {
            const char *filename = path + last_slash + 1;
            for (int j = 0; j < sub->file_count; j++) {
                if (k_strcmp(sub->files[j].name, filename) == 0) return true;
            }
        }
    }

    int count = subsystem_get_count();
    int path_len = k_strlen(path);
    for (int i = 0; i < count; i++) {
        kernel_subsystem_t *s = subsystem_get_by_index(i);
        if (k_strlen(s->name) > path_len && k_strncmp(s->name, path, path_len) == 0 && s->name[path_len] == '/') return true;
    }

    return false;
}

static bool sysfs_is_dir(void *fs_private, const char *path) {
    if (path[0] == '/') path++;
    if (path[0] == '\0') return true;

    int last_slash = -1;
    for (int j = 0; path[j]; j++) if (path[j] == '/') last_slash = j;
    if (last_slash != -1) {
        char prefix[64];
        k_memcpy(prefix, path, last_slash);
        prefix[last_slash] = 0;
        kernel_subsystem_t *sub = subsystem_get_by_name(prefix);
        if (sub) {
            const char *filename = path + last_slash + 1;
            for (int j = 0; j < sub->file_count; j++) {
                if (k_strcmp(sub->files[j].name, filename) == 0) return false;
            }
        }
    }

    return sysfs_exists(fs_private, path);
}

vfs_fs_ops_t sysfs_ops = {
    .open = sysfs_open,
    .close = sysfs_close,
    .read = sysfs_read,
    .write = sysfs_write,
    .readdir = sysfs_readdir,
    .exists = sysfs_exists,
    .is_dir = sysfs_is_dir
};

vfs_fs_ops_t* sysfs_get_ops(void) {
    return &sysfs_ops;
}
