#include "lumacs/plugin_manager.hpp"
#include "lumacs/editor_core.hpp"
#include "lumacs/lua_api.hpp"
#include "lumacs/logger.hpp"
#include <spdlog/spdlog.h>
#include <filesystem>
#include <algorithm>

namespace lumacs {

PluginManager::PluginManager(EditorCore& core, LuaApi& lua_api)
    : core_(core), lua_api_(lua_api) {
}

void PluginManager::discover_plugins(const std::vector<std::filesystem::path>& plugin_dirs) {
    discovered_plugins_.clear();
    for (const auto& dir : plugin_dirs) {
        if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
            spdlog::warn("Plugin directory not found or not a directory: {}", dir.string());
            continue;
        }

        for (const auto& entry : std::filesystem::directory_iterator(dir)) {
            if (entry.is_regular_file() && entry.path().extension() == ".lua") {
                std::string plugin_name = entry.path().stem().string();
                discovered_plugins_[plugin_name] = entry.path();
                spdlog::debug("Discovered plugin: {} at {}", plugin_name, entry.path().string());
            }
        }
    }
}

bool PluginManager::load_plugin(const std::string& name) {
    auto it = discovered_plugins_.find(name);
    if (it == discovered_plugins_.end()) {
        spdlog::error("Plugin not found: {}", name);
        return false;
    }

    if (loaded_plugins_.count(name)) {
        spdlog::warn("Plugin already loaded: {}", name);
        return true; // Already loaded, consider it a success
    }

    // Load the Lua file
    if (!lua_api_.load_file(it->second)) {
        spdlog::error("Failed to load Lua script for plugin: {}", name);
        return false;
    }

    Plugin new_plugin;
    new_plugin.name = name;
    new_plugin.path = it->second;
    loaded_plugins_[name] = new_plugin;
    
    execute_on_load(new_plugin); // Execute on_load function if defined in plugin

    spdlog::info("Plugin loaded: {}", name);
    return true;
}

bool PluginManager::unload_plugin(const std::string& name) {
    auto it = loaded_plugins_.find(name);
    if (it == loaded_plugins_.end()) {
        spdlog::warn("Plugin not loaded: {}", name);
        return false;
    }

    execute_on_unload(it->second); // Execute on_unload function if defined

    // TODO: This is the hard part - how to undo effects of a plugin (keybindings, commands, etc.)
    // For now, we just remove it from the loaded list. The Lua state might still retain
    // its functions/variables if not explicitly cleared by the plugin's unload function.
    loaded_plugins_.erase(it);

    spdlog::info("Plugin unloaded: {}", name);
    return true;
}

std::vector<std::string> PluginManager::get_discovered_plugins() const {
    std::vector<std::string> names;
    for (const auto& pair : discovered_plugins_) {
        names.push_back(pair.first);
    }
    return names;
}

std::vector<std::string> PluginManager::get_loaded_plugins() const {
    std::vector<std::string> names;
    for (const auto& pair : loaded_plugins_) {
        names.push_back(pair.first);
    }
    return names;
}

void PluginManager::execute_on_load(const Plugin& plugin) {
    // Attempt to call a Lua function named `on_load` in the plugin's environment
    try {
        sol::state_view lua = lua_api_.get_lua_state();
        if (lua["lumacs"].valid() && lua["lumacs"]["plugins"].valid() && lua["lumacs"]["plugins"][plugin.name].valid()) {
            sol::table plugin_table = lua["lumacs"]["plugins"][plugin.name];
            sol::protected_function on_load = plugin_table["on_load"];
            if (on_load.valid()) {
                on_load();
                spdlog::debug("Executed on_load for plugin: {}", plugin.name);
            }
        }
    } catch (const sol::error& e) {
        spdlog::error("Lua error in plugin on_load({}): {}", plugin.name, e.what());
    }
}

void PluginManager::execute_on_unload(const Plugin& plugin) {
    // Attempt to call a Lua function named `on_unload` in the plugin's environment
    try {
        sol::state_view lua = lua_api_.get_lua_state();
        if (lua["lumacs"].valid() && lua["lumacs"]["plugins"].valid() && lua["lumacs"]["plugins"][plugin.name].valid()) {
            sol::table plugin_table = lua["lumacs"]["plugins"][plugin.name];
            sol::protected_function on_unload = plugin_table["on_unload"];
            if (on_unload.valid()) {
                on_unload();
                spdlog::debug("Executed on_unload for plugin: {}", plugin.name);
            }
        }
    } catch (const sol::error& e) {
        spdlog::error("Lua error in plugin on_unload({}): {}", plugin.name, e.what());
    }
}

} // namespace lumacs
