bool deleteRecursive(FS& fs, String path);

FS& getActiveFS() {// Функция для получения активной файловой системы
    if (usbMassStorageActive || !sdMounted || !sdEnable) {
        return LittleFS;
    } else {
        return SD_MMC;
    }
}

//format bytes
String formatBytes(uint64_t bytes) {
    if (bytes < 1024) {
        return String(bytes) + " B";
    } else if (bytes < (1024 * 1024)) {
        return String(bytes / 1024.0, 1) + " KB";
    } else if (bytes < (1024 * 1024 * 1024)) {
        return String(bytes / 1024.0 / 1024.0, 1) + " MB";
    } else {
        return String(bytes / 1024.0 / 1024.0 / 1024.0, 1) + " GB";
    }
}

String getContentType(String filename) {
    if (filename.endsWith(".htm") || filename.endsWith(".html")) {
        return "text/html";
    } else if (filename.endsWith(".css")) {
        return "text/css";
    } else if (filename.endsWith(".js")) {
        return "application/javascript";
    } else if (filename.endsWith(".png")) {
        return "image/png";
    } else if (filename.endsWith(".gif")) {
        return "image/gif";
    } else if (filename.endsWith(".jpg") || filename.endsWith(".jpeg")) {
        return "image/jpeg";
    } else if (filename.endsWith(".ico")) {
        return "image/x-icon";
    } else if (filename.endsWith(".xml")) {
        return "text/xml";
    } else if (filename.endsWith(".pdf")) {
        return "application/x-pdf";
    } else if (filename.endsWith(".zip")) {
        return "application/x-zip";
    } else if (filename.endsWith(".gz")) {
        return "application/x-gzip";
    } else if (filename.endsWith(".json")) {
        return "application/json";
    }
    return "text/plain";
}

bool exists(String path) {
    bool yes = false;
    File file = getActiveFS().open(path, "r");
    if (file) {
        yes = !file.isDirectory();
        file.close();
    }
    return yes;
}

bool handleFileRead(String path) {
    Serial.println("handleFileRead: " + path);
    if (path.endsWith("/")) {
        path += "index.htm";
    }
    
    FS& activeFS = getActiveFS();
    String pathWithGz = path + ".gz";
    
    if (activeFS.exists(pathWithGz) || activeFS.exists(path)) {
        String contentType = getContentType(path);
        if (activeFS.exists(pathWithGz)) {
            path = pathWithGz;
        }
        
        File file = activeFS.open(path, "r");
        if (file) {
            server.streamFile(file, contentType);
            file.close();
            return true;
        }
    }
    return false;
}

// API HANDLERS FOR EDIT.HTM
void handleFileList() {
    if (!server.hasArg("list")) {
        server.send(500, "application/json", "{\"error\":\"BAD ARGS\"}");
        return;
    }

    String path = server.arg("list");
    if (path.length() == 0) path = "/";
    
    if (!path.startsWith("/")) {
        path = "/" + path;
    }
    
    FS& activeFS = getActiveFS();
    Serial.println("handleFileList on " + String(usbMassStorageActive ? "LittleFS" : (sdMounted ? "SD" : "LittleFS")) + ": " + path);

    File dir = activeFS.open(path);
    if (!dir || !dir.isDirectory()) {
        server.send(500, "application/json", "{\"error\":\"Cannot open directory\"}");
        return;
    }

    String output = "[";
    File file = dir.openNextFile();
    bool first = true;
    
    while (file) {
        if (!first) output += ',';
        first = false;
        
        String fileName = String(file.name());
        int lastSlash = fileName.lastIndexOf('/');
        if (lastSlash >= 0) fileName = fileName.substring(lastSlash + 1);
        
        output += "{\"type\":\"";
        output += (file.isDirectory()) ? "dir" : "file";
        output += "\",\"name\":\"";
        output += fileName;
        output += "\",\"size\":";
        output += String(file.size());
        output += "}";
        
        file.close();
        file = dir.openNextFile();
    }
    dir.close();
    output += "]";
    
    server.send(200, "application/json", output);
}

void handleFileReadWithParams() {
    if (server.hasArg("edit") || server.hasArg("download")) {
        String path = server.hasArg("edit") ? server.arg("edit") : server.arg("download");
        
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        
        Serial.println("Reading file: " + path);
        
        if (!exists(path)) {
            server.send(404, "text/plain", "File not found");
            return;
        }
        
        File file = SD_MMC.open(path, "r");
        if (!file) {
            server.send(500, "text/plain", "Cannot open file");
            return;
        }
        
        if (server.hasArg("download")) {
            server.sendHeader("Content-Disposition", "attachment; filename=\"" + path.substring(1) + "\"");
            server.streamFile(file, "application/octet-stream");
        } else {
            String contentType = getContentType(path);
            server.streamFile(file, contentType);
        }
        file.close();
        return;
    }
    
    // Default - serve the editor page
    if (server.hasArg("file") || server.hasArg("lang") || server.hasArg("theme")) {
        handleFileRead("/edit.htm");
    } else {
        server.send(404, "text/plain", "FileNotFound");
    }
}

void handleFileCreate() {
    if (!server.hasArg("path")) {
        server.send(500, "text/plain", "BAD ARGS");
        return;
    }
    
    String path = server.arg("path");
    Serial.println("handleFileCreate: " + path);
    
    if (path == "/") {
        server.send(500, "text/plain", "BAD PATH");
        return;
    }
    
    if (!path.startsWith("/")) {
        path = "/" + path;
    }
    
    if (exists(path)) {
        server.send(500, "text/plain", "FILE EXISTS");
        return;
    }
    
    File file = SD_MMC.open(path, "w");
    if (file) {
        file.close();
        server.send(200, "text/plain", "CREATE: " + path);
    } else {
        server.send(500, "text/plain", "CREATE FAILED");
    }
}

void handleFileDelete() {
    if (!server.hasArg("path")) {
        server.send(500, "text/plain", "BAD ARGS");
        return;
    }
    
    String path = server.arg("path");
    Serial.println("handleFileDelete: " + path);
    
    if (path == "/") {
        server.send(500, "text/plain", "BAD PATH");
        return;
    }
    
    if (!path.startsWith("/")) {
        path = "/" + path;
    }
    
    FS& activeFS = getActiveFS();
    
    if (!activeFS.exists(path)) {
        server.send(404, "text/plain", "FileNotFound");
        return;
    }
    
    // Проверяем, является ли путь директорией
    File file = activeFS.open(path);
    if (!file) {
        server.send(500, "text/plain", "Cannot open path");
        return;
    }
    
    bool isDir = file.isDirectory();
    file.close();
    
    bool success = false;
    
    if (isDir) {
        #ifdef ESP32
        // Для SD карты используем rmdir
        if (&activeFS == &SD_MMC) {
            success = SD_MMC.rmdir(path);
            if (!success) {
                // Если директория не пуста, удаляем рекурсивно
                success = deleteRecursive(activeFS, path);
            }
        } else {
            // Для LittleFS/SPIFFS удаляем все файлы в директории
            success = deleteRecursive(activeFS, path);
        }
        #else
        success = deleteRecursive(activeFS, path);
        #endif
    } else {
        // Это файл - просто удаляем
        success = activeFS.remove(path);
    }
    
    if (success) {
        server.send(200, "text/plain", "DELETE: " + path);
    } else {
        server.send(500, "text/plain", "DELETE FAILED");
    }
}

bool deleteRecursive(FS& fs, String path) {// функция для рекурсивного удаления
    File dir = fs.open(path);
    if (!dir) {
        return false;
    }
    
    if (!dir.isDirectory()) {
        dir.close();
        return fs.remove(path);
    }
    
    bool success = true;
    File file = dir.openNextFile();
    
    while (file) {
        String filePath = String(file.name());
        file.close();
        
        if (fs.open(filePath).isDirectory()) {
            success = deleteRecursive(fs, filePath) && success;
        } else {
            success = fs.remove(filePath) && success;
        }
        
        file = dir.openNextFile();
    }
    
    dir.close();
    
    // Удаляем саму директорию
    #ifdef ESP32
    if (&fs == &SD_MMC) {
        success = SD_MMC.rmdir(path) && success;
    } else {
        success = fs.remove(path) && success;
    }
    #else
    success = fs.remove(path) && success;
    #endif
    
    return success;
}

void handleFileUpload() {
    if (server.uri() != "/edit") {
        return;
    }
    
    HTTPUpload& upload = server.upload();
    
    if (upload.status == UPLOAD_FILE_START) {
        String filename = upload.filename;
        if (!filename.startsWith("/")) filename = "/" + filename;
        
        if (server.hasArg("path")) {
            String basePath = server.arg("path");
            if (basePath.endsWith("/")) {
                filename = basePath + upload.filename;
            } else {
                filename = basePath + "/" + upload.filename;
            }
        }
        
        Serial.print("Upload to " + String(usbMassStorageActive ? "LittleFS" : "SD") + ": ");
        Serial.println(filename);
        
        FS& activeFS = getActiveFS();
        fsUploadFile = activeFS.open(filename, "w");
        
    } else if (upload.status == UPLOAD_FILE_WRITE) {
        if (fsUploadFile) {
            fsUploadFile.write(upload.buf, upload.currentSize);
        }
        
    } else if (upload.status == UPLOAD_FILE_END) {
        if (fsUploadFile) {
            fsUploadFile.close();
            Serial.println("Upload completed: " + String(upload.totalSize) + " bytes");
            // Отправляем простой ответ без автоматических заголовков
            server.setContentLength(CONTENT_LENGTH_UNKNOWN);
            server.send(200, "text/plain", "");
        } else {
            server.send(500, "text/plain", "ERROR");
        }
    }
}

void handleFileUpdate() {
    if (server.method() == HTTP_PUT) {
        // Handle file creation/update via PUT (for saving from editor)
        if (!server.hasArg("path") && !server.hasArg("plain")) {
            server.send(400, "text/plain", "BAD REQUEST");
            return;
        }
        
        String path = server.hasArg("path") ? server.arg("path") : "";
        String content = server.hasArg("plain") ? server.arg("plain") : "";
        
        if (path.length() == 0) {
            // Try to get path from URL parameters
            for (uint8_t i = 0; i < server.args(); i++) {
                if (server.argName(i) == "path") {
                    path = server.arg(i);
                    break;
                }
            }
        }
        
        // Handle POST with FormData
        if (server.hasArg("data")) {
            path = server.arg("data");
            if (!path.startsWith("/")) path = "/" + path;
            
            if (SD_MMC.exists(path)) {
                server.send(200, "text/plain", "UPLOADED: " + path);
            } else {
                server.send(500, "text/plain", "UPLOAD FAILED");
            }
            return;
        }
        
        if (path.length() == 0 || content.length() == 0) {
            server.send(400, "text/plain", "Missing path or content");
            return;
        }
        
        if (!path.startsWith("/")) path = "/" + path;
        
        File file = SD_MMC.open(path, "w");
        if (file) {
            file.print(content);
            file.close();
            server.send(200, "text/plain", "SAVED: " + path);
        } else {
            server.send(500, "text/plain", "SAVE FAILED");
        }
    }
}

void handleStatus() {
    String json = "{";
    json += "\"heap\":" + String(ESP.getFreeHeap());
    json += ", \"sd_size\":" + String((uint64_t)(SD_MMC.totalBytes() / 1024));
    json += ", \"sd_used\":" + String((uint64_t)(SD_MMC.usedBytes() / 1024));
    json += ", \"sdk\":\"" + String(ESP.getSdkVersion()) + "\"";
    json += ", \"cpu\":" + String(ESP.getCpuFreqMHz());
    json += ", \"flash\":" + String(ESP.getFlashChipSize() / 1024 / 1024);
    json += "}";
    server.send(200, "application/json", json);
}

void debugSD_MMC() {
    Serial.println("=== File System Debug ===");
    File root = SD_MMC.open("/");
    if (!root) {
        Serial.println("Failed to open root directory");
        return;
    }
    
    if (!root.isDirectory()) {
        Serial.println("Root is not a directory");
        root.close();
        return;
    }
    
    Serial.println("Files in root:");
    File file = root.openNextFile();
    int fileCount = 0;
    
    while (file) {
        fileCount++;
        String fileName = String(file.name());
        Serial.print("  ");
        Serial.print(fileName);
        Serial.print(" (");
        Serial.print(file.isDirectory() ? "DIR" : "FILE");
        Serial.print(", ");
        Serial.print(file.size());
        Serial.println(" bytes)");
        
        file.close();
        file = root.openNextFile();
    }
    root.close();
    
    Serial.print("Total files: ");
    Serial.println(fileCount);
    Serial.println("========================");
}

void handleMkdir() {
    if (!server.hasArg("path")) {
        server.send(400, "application/json", "{\"success\":false,\"error\":\"No path specified\"}");
        return;
    }
    
    String path = server.arg("path");
    if (!path.startsWith("/")) path = "/" + path;
    
    FS& activeFS = getActiveFS();
    
    if (activeFS.exists(path)) {
        server.send(400, "application/json", "{\"success\":false,\"error\":\"Path already exists\"}");
        return;
    }
    
    // Create directory (implementation depends on FS)
    // For SPIFFS/LittleFS, directories are virtual
    // For SD, we can create real directories
    
    #ifdef ESP32
    if (&activeFS == &SD_MMC) {
        if (SD_MMC.mkdir(path)) {
            server.send(200, "application/json", "{\"success\":true}");
        } else {
            server.send(500, "application/json", "{\"success\":false,\"error\":\"Cannot create directory\"}");
        }
    } else {
        // For LittleFS, just create a marker file
        File dirMarker = activeFS.open(path + "/.dir", "w");
        if (dirMarker) {
            dirMarker.close();
            server.send(200, "application/json", "{\"success\":true}");
        } else {
            server.send(500, "application/json", "{\"success\":false,\"error\":\"Cannot create directory\"}");
        }
    }
    #endif
}
void setup_WS(void) {
    // Start MDNS
    if (!MDNS.begin(HOST)) {
        Serial.println("Error starting MDNS");
    } else {
        MDNS.addService("http", "tcp", 80);
        Serial.print("Open http://");
        Serial.print(HOST);
        Serial.println(".local/edit to see the file browser");
    }

    // API endpoints для edit.htm
    server.on("/list", HTTP_GET, handleFileList);
    
    // Важно: этот обработчик должен проверять параметры внутри себя
    server.on("/edit", HTTP_GET, []() {
        if (server.hasArg("list")) {
            handleFileList();
        } else {
            handleFileReadWithParams();
        }
    });
    server.on("/edit", HTTP_OPTIONS, []() {
        server.sendHeader("Access-Control-Allow-Origin", "*");
        server.sendHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
        server.sendHeader("Access-Control-Allow-Headers", "Content-Type");
        server.send(204);
    });
    server.on("/edit", HTTP_PUT, handleFileCreate);
    server.on("/edit", HTTP_DELETE, handleFileDelete);
    server.on("/edit", HTTP_POST, []() {
        // Этот обработчик вызывается после завершения загрузки
        // Не отправляем здесь ответ, так как он уже отправлен в handleFileUpload
        Serial.println("POST request completed");
    }, handleFileUpload);
    
    // Дополнительный эндпоинт для обновлений файлов
    server.on("/update", HTTP_PUT, handleFileUpdate);
    
    // Статус эндпоинт
    server.on("/status", HTTP_GET, handleStatus);
    
    // Обработка всех остальных запросов
    server.onNotFound([]() {
        if (!handleFileRead(server.uri())) {
            server.send(404, "text/plain", "FileNotFound");
        }
    });
    server.on("/all", HTTP_GET, []() {
        String json = "{";
        json += "\"heap\":" + String(ESP.getFreeHeap());
        json += ", \"analog\":10";
        json += ", \"gpio\":" + String((uint32_t)(0));
        json += "}";
        server.send(200, "text/json", json);
        json = String();
    });
    server.on("/getfile", HTTP_GET, []() {
        if (!server.hasArg("path")) {
            server.send(500, "text/plain", "BAD ARGS");
            return;
        }
        
        String path = server.arg("path");
        
        // Проверка безопасности - только файлы из корня
        if (path.indexOf("..") != -1) {
            server.send(403, "text/plain", "Forbidden");
            return;
        }
        
        if (!SD_MMC.exists(path)) {
            server.send(404, "text/plain", "File not found");
            return;
        }
        
        File file = SD_MMC.open(path);
        if (!file) {
            server.send(500, "text/plain", "Cannot open file");
            return;
        }
        
        // Определяем тип контента
        String contentType = "text/plain";
        if (path.endsWith(".txt")) {
            contentType = "text/plain; charset=utf-8";
        } else if (path.endsWith(".csv")) {
            contentType = "text/csv";
        } else if (path.endsWith(".json")) {
            contentType = "application/json";
        }
        
        server.streamFile(file, contentType);
        file.close();
    });
    server.on("/delete_session", HTTP_DELETE, []() {
        if (!server.hasArg("ip") || !server.hasArg("time")) {
            server.send(400, "application/json", "{\"success\":false,\"error\":\"Missing parameters\"}");
            return;
        }
        
        String ip = server.arg("ip");
        String time = server.arg("time");
        String basePath = "/" + ip + "/" + time;
        
        FS& fs = getActiveFS();
        int deleted = 0;
        
        String files[] = {basePath + "_log.json", basePath + "_msg.json", basePath + "_st.json"};
        
        for (String file : files) {
            if (fs.exists(file) && fs.remove(file)) {
                deleted++;
            }
        }
        
        // Удаляем папку IP, если она пуста
        File dir = fs.open("/" + ip);
        if (dir && dir.isDirectory()) {
            dir.close();
            File check = fs.open("/" + ip);
            File next = check.openNextFile();
            bool empty = !next;
            check.close();
            
            if (empty) {
                #ifdef ESP32
                if (&fs == &SD_MMC) {
                    SD_MMC.rmdir("/" + ip);
                }
                #endif
            }
        }
        
        server.send(200, "application/json", "{\"success\":true,\"deleted\":" + String(deleted) + "}");
    });
    server.on("/fs_status", HTTP_GET, []() {
        String json = "{";
        json += "\"mode\":\"" + String(usbMassStorageActive ? "usb" : (sdMounted ? "sd" : "littlefs")) + "\"";
        json += "\"sd_present\":" + String(sdEnable ? "true" : "false");
        json += "\"usb_active\":" + String(usbMassStorageActive ? "true" : "false");
        json += "}";
        server.send(200, "application/json", json);
    });
    server.on("/mkdir", HTTP_POST, handleMkdir);
    SetupServerInit();
    setupTechnicalWebHandlers();
    server.begin();
    Serial.println("HTTP server started");
}

void loop_WS(void) {
    server.handleClient();
}