#include <filesystem>
#include <fstream>
#include <httplib.h>
#include <iostream>
#include <string>
#include <vector>

#ifdef _WIN32
#include <windows.h>
#endif

namespace fs = std::filesystem;

#ifdef _WIN32
std::string u8_to_ansi(const std::string& str)
{
    int wideCharLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, nullptr, 0);
    if (wideCharLen <= 0) {
        return "";
    }
    std::wstring wideStr(wideCharLen, L'\0');
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wideStr[0], wideCharLen);
    int gbkLen = WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    if (gbkLen <= 0) {
        return "";
    }
    std::string gbkStr(gbkLen, '\0');
    WideCharToMultiByte(CP_ACP, 0, wideStr.c_str(), -1, &gbkStr[0], gbkLen, nullptr, nullptr);

    gbkStr.resize(gbkLen - 1);
    return gbkStr;
}
std::string ansi_to_u8(const std::string& str)
{
    int wideCharLen = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, nullptr, 0);
    if (wideCharLen <= 0) {
        return "";
    }
    std::wstring wideStr(wideCharLen, L'\0');
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &wideStr[0], wideCharLen);

    int utf8Len = WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, nullptr, 0, nullptr, nullptr);
    if (utf8Len <= 0) {
        return "";
    }
    std::string utf8Str(utf8Len, '\0');
    WideCharToMultiByte(CP_UTF8, 0, wideStr.c_str(), -1, &utf8Str[0], utf8Len, nullptr, nullptr);

    utf8Str.resize(utf8Len - 1);
    return utf8Str;
}
#endif

// 生成文件列表的HTML页面
std::string generate_file_list(const std::string& base_path, const std::string& current_path)
{
    fs::path full_path = fs::path(base_path) / current_path;

    std::string html = R"(
    <!DOCTYPE html>
    <html>
    <head>
        <title>File Browser</title>
        <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        h1 { color: #333; }
        .path { color: #666; margin-bottom: 20px; }
        ul { list-style-type: none; padding: 0; }
        li { margin: 8px 0; }
        a { text-decoration: none; color: #0066cc; }
        a:hover { text-decoration: underline; } 
        .file::before { content: "\1F4C4 "; }
        .dir::before { content: "\1F4C1 "; }
        .parent::before { content: "\2B06 "; }
        .size { color: #888; font-size: 0.9em; margin-left: 10px; }
        </style>
    </head>
    <body>
    <h1>File Browser</h1>
    <div class="path">Current path: /)" +
                       current_path + R"(</div>
    <ul>
    )";

    // 添加返回上一级链接（如果不是根目录）
    if (current_path != "") {
        fs::path parent_path = fs::path(current_path).parent_path();
        std::string parent_link = parent_path.empty() ? "" : parent_path.string();
        html += R"(<li class="parent"><a href="/browse/)" + parent_link + R"(">..</a></li>)";
    }

    // 遍历目录下的文件和子目录
    for (const auto& entry : fs::directory_iterator(full_path)) {
        std::string filename = entry.path().filename().string();
        std::string new_path = (fs::path(current_path) / filename).string();

        if (entry.is_directory()) {
            html += R"(<li class="dir"><a href="/browse/)" + new_path + R"(">)" + filename + R"(/</a></li>)";
        } else {
            // 获取文件大小并转换为MB
            uintmax_t file_size = entry.file_size();
            double size_mb = file_size / (1024.0 * 1024.0);
            char size_str[20];
            snprintf(size_str, sizeof(size_str), "%.4f MB", size_mb);

            html += R"(<li class="file"><a href="/download/)" + new_path + R"(">)" + filename + R"(</a><span class="size">)" +
                    size_str + R"(</span></li>)";
        }
    }

    html += R"(
    </ul>
    </body>
    </html>
    )";

#ifdef _WIN32
    html = ansi_to_u8(html);
#endif

    return html;
}

int main(int argc, char** argv)
{
    if (argc != 3) {
        std::cout << "Usage: " << argv[0] << " <port> <base_dir>" << std::endl;
        return -2;
    }

    const std::string base_dir(argv[2]);   // 基础文件目录
    if (!fs::exists(base_dir)) {
        std::cout << "Base directory does not exist: " << base_dir << std::endl;
        return -1;
    }

    int port = std::stoi(argv[1]);
    httplib::Server server;
    // 确保基础目录存在
    if (!fs::exists(base_dir)) {
        fs::create_directory(base_dir);
    }

    // 文件浏览路由
    server.Get("/browse(.*)", [&base_dir](const httplib::Request& req, httplib::Response& res) {
        std::string path = req.matches[1].str();
#ifdef _WIN32
        path = u8_to_ansi(path);
#endif
        // 移除开头的斜杠
        if (!path.empty() && path[0] == '/') {
            path = path.substr(1);
        }

        // 安全检查：防止目录遍历攻击
        if (path.find("..") != std::string::npos) {
            res.set_content("Invalid path", "text/plain");
            res.status = 400;
            return;
        }

        fs::path full_path = fs::path(base_dir) / path;

        if (!fs::exists(full_path)) {
            res.set_content("Path not found", "text/plain");
            res.status = 404;
            return;
        }

        if (!fs::is_directory(full_path)) {
            res.set_content("Not a directory", "text/plain");
            res.status = 400;
            return;
        }
        res.set_header("Content-Type", "text/html; charset=utf-8");
        res.set_content(generate_file_list(base_dir, path), "text/html; charset=utf-8");
    });

    // 文件下载路由
    server.Get("/download/(.*)", [&base_dir](const httplib::Request& req, httplib::Response& res) {
        std::string path = req.matches[1];

#ifdef _WIN32
        path = u8_to_ansi(path);
#endif
        // 安全检查：防止目录遍历攻击
        if (path.find("..") != std::string::npos) {
            res.set_content("Invalid path", "text/plain");
            res.status = 400;
            return;
        }

        fs::path file_path = fs::path(base_dir) / path;

        if (!fs::exists(file_path)) {
            res.set_content("File not found", "text/plain");
            res.status = 404;
            return;
        }

        if (fs::is_directory(file_path)) {
            res.set_content("Cannot download directory", "text/plain");
            res.status = 400;
            return;
        }

        // 设置响应头，触发浏览器下载
        res.set_header("Content-Type", "application/octet-stream");
        res.set_header("Content-Disposition", "attachment; filename=" + file_path.filename().string());

        auto file = std::make_shared<std::ifstream>(file_path, std::ios::binary);
        if (!*file) {
            res.status = 500;
            res.set_content("Failed to open file", "text/plain");
            return;
        }

        // 获取文件大小
        const size_t file_size = fs::file_size(file_path);

        // 定义分块回调（严格匹配 ContentProvider 签名）
        auto provider = [file](size_t offset, size_t length, httplib::DataSink& sink) {
            file->seekg(offset);
            const size_t chunk_size = std::min<size_t>(1024 * 1024, length);   // 64KB或剩余长度
            std::vector<char> buffer(chunk_size);

            size_t remaining = length;
            while (remaining > 0 && sink.is_writable() && *file) {
                size_t read_size = std::min(buffer.size(), remaining);
                file->read(buffer.data(), read_size);
                size_t bytes_read = file->gcount();

                if (bytes_read > 0) {
                    sink.write(buffer.data(), bytes_read);
                    remaining -= bytes_read;
                } else {
                    break;
                }
            }
            return true;
        };

        // 定义资源清理回调
        auto releaser = [file](bool /*success*/) { file->close(); };

        // 调用 set_content_provider
        res.set_content_provider(file_size,                    // 文件总大小
                                 "application/octet-stream",   // MIME类型
                                 provider,                     // 数据提供回调
                                 releaser                      // 资源清理回调
        );
    });

    // 根目录重定向到/browse
    server.Get("/", [](const httplib::Request& req, httplib::Response& res) { res.set_redirect("/browse"); });

    std::cout << "Server running at http://localhost:" << port << std::endl;
    std::cout << "Access the root path to browse files.\n";
    if (!server.listen("0.0.0.0", port)) {
        std::cerr << "Failed to start server\n";
        return -1;
    }
    return 0;
}