#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h> 

#include "esp_vfs.h"
#include "esp_vfs_fat.h"
#include "esp_spiffs.h"
#include "esp_littlefs.h"
#include "mbedtls/sha256.h"

#include "drivers/fs/fs.hpp"
#include "boot/typedef.hpp"
#include "boot/answer_code.hpp"
#include "boot/entry.hpp"
#include "drivers/fs/handlers.hpp"

void input_string(char *buffer, size_t max_len, bool hide_echo) {
    int i = 0;
    while (i < max_len - 1) {
        int c = getchar();
        if (c == EOF || c == 0xFF) {
            vTaskDelay(pdMS_TO_TICKS(10));
            continue;
        }
        if (c == '\n' || c == '\r') {
            buffer[i] = '\0';
            printf("\n");
            break;
        }
        if (c == '\b' || c == 127) {
            if (i > 0) {
                i--;
                printf("\b \b"); 
                fflush(stdout);
            }
            continue;
        }
        if (c >= 32 && c <= 126) {
            buffer[i++] = (char)c;
            if (!hide_echo) putchar(c);
            else putchar('*');
            fflush(stdout);
        }
    }
}

void create_fs() {

    char device_name[32];
    char user_name[32];
    char user_password[32];
    char user_password_sha256[32];

    printf("\033[0;31mNot find file os\n\033[0m");
    printf("Please entry device name: ");
    input_string(device_name, 32, false);
    printf("Please entry username: ");
    input_string(user_name, 32, false);
    printf("Please entry user password: ");
    input_string(user_password, 32, true);

    mbedtls_sha256((const unsigned char *)user_password, strlen(user_password), (unsigned char *)user_password_sha256, 0);

    char hex_hash[65]; 
    for (int j = 0; j < 32; j++) {
        sprintf(&hex_hash[j * 2], "%02x", (unsigned char)user_password_sha256[j]);
    }

    printf("device_name: %s\n", device_name);
    printf("user_name: %s\n", user_name);
    printf("user_password: %s\n", user_password);
    printf("user_password_sha256: %s\n", user_password_sha256);

    int total_prograss = 0;
    int max_prograss = 15;
    
    mkdir("/bin", 0777);                                                                                            total_prograss++;
    mkdir("/boot", 0777);                                                                                           total_prograss++;
    mkdir("/dev", 0777);                                                                                            total_prograss++;
    mkdir("/etc", 0777);                                                                                            total_prograss++;
    mkdir("/home", 0777);                                                                                           total_prograss++;
    mkdir("/lib", 0777);                                                                                            total_prograss++;
    mkdir("/mnt", 0777);                                                                                            total_prograss++;
    mkdir("/opt", 0777);                                                                                            total_prograss++;
    mkdir("/root", 0777);                                                                                           total_prograss++;
    mkdir("tmp", 0777);                                                                                             total_prograss++;
    mkdir("/usr", 0777);                                                                                            total_prograss++;

    FILE *file = fopen("/etc/hostname", "w");                                                                       total_prograss++;
    fprintf(file, "%s\n", device_name);
    fclose(file);

    file = fopen("/etc/passwd", "w");                                                                               total_prograss++;
    fprintf(file, "root:0:0:/root:/bin/shell\n%s:1000:1000:/home/%s:/bin/shell\n", user_name, user_name);
    fclose(file);

    file = fopen("/etc/shadow", "w");                                                                               total_prograss++;
    fprintf(file, "root:*\n%s:%s\n", user_name, hex_hash);
    fclose(file);

    file = fopen("/etc/group", "w");                                                                                total_prograss++;
    fprintf(file, "root:0:root\nusr:1000:%s", user_name);
    fclose(file);

}

void list_files(const char *path) {
    printf("Чтение директории: %s\n", path);

    DIR *dir = opendir(path);
    if (dir == NULL) {
        printf("Не удалось открыть директорию\n");
        return;
    }

    struct dirent *entry;
    int found = 0;
    while ((entry = readdir(dir)) != NULL) {
        found++;
        const char *type = (entry->d_type == DT_DIR) ? "DIR" : "FILE";
        printf("[%s] %s\n", type, entry->d_name); 
    }

    if (found == 0) {
        printf("Директория пуста\n");
    }

    closedir(dir);
}

bool file_exists(const char *path) {
    struct stat buffer;
    return (stat(path, &buffer) == 0);
}
uint32_t mount(fs_type_t type, const char *path, const char *label, const Handler_vfs *class_fs) {
    switch (type) {
        case MOUNT_LITTLEFS: {
            printf("fs: mount littlefs\n");
            esp_vfs_littlefs_conf_t conf = {
                .base_path = path,
                .partition_label = label,
                .format_if_mount_failed = true
            };
            switch (esp_vfs_littlefs_register(&conf))
            {
            case ESP_OK:                return OS_OK;
            case ESP_ERR_NO_MEM:        return OS_ERR_NO_MEMORY;
            case ESP_ERR_INVALID_ARG:   return OS_ERR_INVALID_ARG;
            case ESP_ERR_INVALID_STATE: return VFS_ERR_TARGET_BUSY;
            case ESP_FAIL:              return OS_ERR;
            case ESP_ERR_NOT_FOUND:     return OS_ERR_NOT_FOUND;
            case ESP_ERR_NOT_SUPPORTED: return VFS_ERR_NOT_SUPPORTED;
            default:                    return OS_ERR;
            };
        }

        case MOUNT_SD_CARD: {
            printf("fs: sd card mount is not configured yet\n");
            return VFS_ERR_NOT_SUPPORTED;
        }

        case MOUNT_SPIFFS: {
            printf("fs: mount spiffs\n");
            esp_vfs_spiffs_conf_t conf = {
                .base_path = path,
                .partition_label = label,
                .max_files = 5,
                .format_if_mount_failed = true
            };
            switch (esp_vfs_spiffs_register(&conf))
            {
            case ESP_OK:                return OS_OK;
            case ESP_ERR_NO_MEM:        return OS_ERR_NO_MEMORY;
            case ESP_ERR_INVALID_ARG:   return OS_ERR_INVALID_ARG;
            case ESP_ERR_INVALID_STATE: return VFS_ERR_TARGET_BUSY;
            case ESP_FAIL:              return OS_ERR;
            case ESP_ERR_NOT_FOUND:     return OS_ERR_NOT_FOUND;
            case ESP_ERR_NOT_SUPPORTED: return VFS_ERR_NOT_SUPPORTED;
            default:                    return OS_ERR;
            };
        }

        case MOUNT_HANDLER: {
            esp_vfs_t conf = {};
            memset(&conf, 0, sizeof(conf));
            conf.flags = ESP_VFS_FLAG_CONTEXT_PTR;
            conf.open_p = &Handler_vfs::open_vfs;
            conf.read_p = &Handler_vfs::read_vfs;
            conf.write_p = &Handler_vfs::write_vfs;
            conf.close_p = &Handler_vfs::close_vfs;
            switch (esp_vfs_register(path, &conf, (void*)class_fs))
            {
            case ESP_OK:                return OS_OK;
            case ESP_ERR_NO_MEM:        return OS_ERR_NO_MEMORY;
            case ESP_ERR_INVALID_ARG:   return OS_ERR_INVALID_ARG;
            case ESP_ERR_INVALID_STATE: return VFS_ERR_TARGET_BUSY;
            case ESP_FAIL:              return OS_ERR;
            case ESP_ERR_NOT_FOUND:     return OS_ERR_NOT_FOUND;
            case ESP_ERR_NOT_SUPPORTED: return VFS_ERR_NOT_SUPPORTED;
            default:                    return OS_ERR;
            };
        }

        default:
            return ESP_ERR_NOT_SUPPORTED;
    }
}
int unmount() {
    printf("fs: unmount");
    return 0;
}
int format() {
    printf("fs: format");
    return 0;
}
int get_info() {
    printf("fs: get_info");
    return 0;
}
int is_present() {
    printf("fs: is_present");
    return 0;
}
int get_vfs_config() {
    printf("fs: get_vfs_config");
    return 0;
}

int init_fs() {
    uint32_t ret = mount(MOUNT_LITTLEFS, "", "storage");
    if (ret != OS_OK) return ret;

    if (!file_exists("/etc/hostname")) create_fs();

    return OS_OK;
}