#pragma once
#include <string>
#include <vector>
#include <map>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <filesystem>

/*
This Api is made for Python <-> C++ communication via pybind11.
This Api Provides several API calls that can be extenden in other c++ modules or directly be imported as function in python.
For Adding External C++ Modules (simply plugins). Add a Announcer file '<modulename>.cp' in the folder 'src/plugins/'.
This file connects the external C++ module with the main application via this API. Allowing to also use the external C++ module functions in python via pybind11.
This API Manager will automatically load all plugins in the folder 'src/plugins/' on startup.
*/

/*  Basic ShowOff of .cp

SOURCE(<here_your_path_to_module.cpp>) && HEADER(<here_your_path_to_module.h>) <name_your_module>

~oder nur cpp ohne header~
SOURCE(<here_your_path_to_module.cpp>) <name_your_module>


PUBLIC(
    <name_your_module> FUNC(<funcname>)
    <name_your_module> FUNC2(<funcname>>)
    <name_your_module> CLASS(<ClassName>)
    <name_your_module> VAR(<VariableName>)
)
~ wenn alle dann~
PUBLIC(
    ALL
)
*/

/* The Best is, you also can give and Take params! Here another Example based on real usecase:
fast_list.cp:

SOURCE(/src/include/fast_list.cpp) && HEADER(/src/include/fast_list.h) fast_list
PUBLIC(
    fast_list CLASS(FastList) && PY_PARAM(list, "List to convert") && PY_PARAM(int, "Pointer count")
    fast_list FUNC(fast_sort)
)
PY_PARAM ist rein optional und sollte nicht genutzt werden da Parameter auch von Pybind11 automatisch erkannt werden.
*/

struct FunctionBinding {
    std::string module_name;
    std::string function_name;
    std::vector<std::pair<std::string, std::string>> params;
};

struct ClassBinding {
    std::string module_name;
    std::string class_name;
    std::vector<std::pair<std::string, std::string>> params;
    std::vector<std::string> methods;    // Method names to bind
    std::vector<std::string> fields;     // Field names to bind
    bool auto_bind_all;                  // Bind all methods automatically
};

struct VariableBinding {
    std::string module_name;
    std::string variable_name;
};

struct ModuleDescriptor {
    std::string module_name;
    std::string source_path;
    std::string header_path;
    bool has_header;
    bool expose_all;
    std::vector<FunctionBinding> functions;
    std::vector<ClassBinding> classes;
    std::vector<VariableBinding> variables;
};

class API
{
public:
    static int main(int argc, char* argv[]);
    static std::vector<ModuleDescriptor> parse_all_cp_files(const std::string& plugins_dir);
    static ModuleDescriptor parse_cp_file(const std::string& filepath);
    static std::string generate_pybind11_code(const std::vector<ModuleDescriptor>& modules);
    static std::string generate_class_bindings(const ClassBinding& cls, const ModuleDescriptor& mod);
    static bool validate_bindings_code(const std::string& code);
    static void validate_module_name(const std::string& name, int line_num);
    static bool write_files(const std::vector<ModuleDescriptor>& modules,
                           const std::string& bindings_path,
                           const std::string& sources_path);
    static std::string compute_file_hash(const std::string& filepath);
    static std::string generate_registry_json(const std::vector<ModuleDescriptor>& modules, const std::string& plugins_dir);

private:
    static std::string trim(const std::string& str);
    static std::vector<std::string> split(const std::string& str, char delimiter);
    static std::string extract_between(const std::string& str, char open, char close);
    static std::string safe_extract_between(const std::string& str,
                                           size_t start_pos,
                                           size_t end_pos,
                                           const std::string& context);
    static bool starts_with(const std::string& str, const std::string& prefix);
    static std::string normalize_path(const std::string& path);
    static std::string replace_all(std::string str, const std::string& from, const std::string& to);
};