#include <filesystem>
#include <fstream>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>
#include <vector>

namespace fs = std::filesystem;

std::string to_full(const std::string& path)
{
    fs::path base;
    if (fs::path(path).is_relative()) {
        base = fs::current_path();
        base.append(path);
    } else {
        base = fs::path(path);
    }
    fs::path ret = fs::absolute(base);
    return ret.string();
}

struct LuaFunction {
    std::string name;
    std::string brief;
    std::vector<std::pair<std::string, std::string>> params;
    std::string returnType;
};

// 解析 C++ 代码并提取函数信息
std::vector<LuaFunction> parse_cpp(const std::string& cpp_code)
{
    std::vector<LuaFunction> functions;
    std::regex func_regex(R"(///\s*@brief\s*(.*))");
    std::regex param_regex(R"(///\s*@param\s*(\w+)\s*(.*))");
    std::regex return_regex(R"(///\s*@return\s*(.*))");
    std::regex name_regex(R"(\bint\s+(\w+)\s*\()");

    LuaFunction current;
    bool inFunction = false;

    std::istringstream stream(cpp_code);
    std::string line;
    while (std::getline(stream, line)) {
        std::smatch match;

        if (std::regex_search(line, match, func_regex)) {
            current.brief = match[1].str();
            inFunction = true;
        } else if (std::regex_search(line, match, param_regex)) {
            current.params.emplace_back(match[1].str(), match[2].str());
        } else if (std::regex_search(line, match, return_regex)) {
            current.returnType = match[1].str().empty() ? "boolean" : match[1].str();
        } else if (std::regex_search(line, match, name_regex)) {
            current.name = match[1].str();
        } else if (inFunction && line.find("}") != std::string::npos) {
            functions.push_back(current);
            current = LuaFunction();
            inFunction = false;
        }
    }
    return functions;
}

// 生成 Lua 绑定文件
void generate_lua_file(const std::vector<LuaFunction>& functions, const std::string& filename, const std::string& mod_name)
{
    std::ofstream out(filename);
    if (!out) {
        std::cerr << "can't create file: " << filename << std::endl;
        return;
    }

    std::string mod_call = mod_name + "_call";
    std::string mod_call_p = mod_name + "_call.";

    out << "---@meta\n";
    out << "local " << mod_call << " = require(\"" << mod_name << "\")\n\n";
    out << "local M = {}\n\n";

    for (const auto& func : functions) {
        out << "--- " << func.brief << "\n";
        for (const auto& param : func.params) {
            out << "--- @param " << param.first << " " << param.second << "\n";
        }
        out << "--- @return " << func.returnType << "\n";
        out << "function M." << func.name << "(";
        for (size_t i = 0; i < func.params.size(); ++i) {
            out << func.params[i].first;
            if (i < func.params.size() - 1)
                out << ", ";
        }
        out << ")\n    return " << mod_call_p << func.name << "(";
        for (size_t i = 0; i < func.params.size(); ++i) {
            out << func.params[i].first;
            if (i < func.params.size() - 1)
                out << ", ";
        }
        out << ")\nend\n\n";
    }

    out << "return M\n";
    out.close();
    std::cout << "Lua file gen success: " << filename << std::endl;
}

// 主函数
int main(int argc, char* argv[])
{
    if (argc < 4) {
        std::cout << "arg1: cxx file.\n";
        std::cout << "arg2: module name.\n";
        std::cout << "arg3: out dir.\n";
        return -1;
    }

    std::string arg_file(argv[1]);
    std::string arg_mod(argv[2]);
    std::string arg_out(argv[3]);

    arg_file = to_full(arg_file);
    arg_out = to_full(arg_out);
    std::string out_file = fs::path(arg_out).append(arg_mod + ".lua").string();

    std::cout << "file: " << arg_file << std::endl;
    std::cout << "mod: " << arg_mod << std::endl;
    std::cout << "out: " << arg_out << std::endl;

    if (!fs::exists(arg_file)) {
        std::cerr << "file not found: " << arg_file << std::endl;
        return -2;
    }
    try {
        if (!fs::exists(arg_out)) {
            fs::create_directories(arg_out);
        }
    } catch (const std::exception& e) {
        std::cerr << "create dir auto failed: " << e.what() << '\n';
        return -3;
    }

    std::string s;
    std::ifstream in(arg_file);
    if (!in.is_open()) {
        std::cerr << "open file failed: " << arg_file << std::endl;
        return -1;
    }
    std::istreambuf_iterator<char> iterf(in);
    std::istreambuf_iterator<char> iter;
    std::string content(iterf, iter);
    std::vector<LuaFunction> functions = parse_cpp(content);
    generate_lua_file(functions, out_file, arg_mod);
    return 0;
}