#include "KMPluginSynchronizer.h"
#include "KMLogger.h"
#include <fstream>
#include <iostream>
#include <set>

bool KMPluginSynchronizer::syncPlugins(const fs::path& versionDataPath,
                                   const std::vector<std::string>& exportPlugins,
                                   const fs::path& targetBaseDir) {

    fs::path pluginsListFile = targetBaseDir / "plugins.list";
    std::set<std::string> allPlugins;

    try {
        if (!fs::exists(targetBaseDir)) {
            fs::create_directories(targetBaseDir);
        }

        if (fs::exists(pluginsListFile)) {
            if (!readExistingPluginsList(pluginsListFile, allPlugins)) {
                return false;
            }
        }

        for (const auto& plugin : exportPlugins) {
            if (!plugin.empty()) {
                allPlugins.insert(plugin);
            }
        }

        for (const auto& plugin : exportPlugins) {
            if (plugin.empty()) continue;
            
            std::string pluginRel = plugin;
            if(!pluginRel.empty() && pluginRel[0] == '/') {
                pluginRel.erase(0, 1);

            }
            fs::path sourcePluginDir = versionDataPath / pluginRel;
            fs::path targetPluginDir = targetBaseDir / pluginRel;

            kmlogger.info("Plugin syncing from: %s to: %s", sourcePluginDir.string().c_str(), targetPluginDir.string().c_str());

            if (!syncSinglePlugin(sourcePluginDir, targetPluginDir)) {
                kmlogger.error("Failed to sync plugin: %s", plugin.c_str() );
                return false;
            }
        }

        if (!writePluginsList(pluginsListFile, allPlugins)) {
            return false;
        }
        
        return true;
        
    } catch (const fs::filesystem_error& e) {
        kmlogger.error("Filesystem error: %s",  std::string(e.what()).c_str());
        return false;
    } catch (const std::exception& e) {
        kmlogger.error("FError: %s",  std::string(e.what()).c_str());
        return false;
    }
}

bool KMPluginSynchronizer::readExistingPluginsList(const fs::path& pluginsListFile, std::set<std::string>& allPlugins) {
    try {
        std::ifstream inFile(pluginsListFile);
        if (!inFile.is_open()) {
            kmlogger.error("Failed to open plugins list file for reading: %s", pluginsListFile.string().c_str());
            return false;
        }
        
        std::string line;
        while (std::getline(inFile, line)) {
            if (!line.empty()) {
                allPlugins.insert(line);
            }
        }
        inFile.close();
        return true;
        
    } catch (const std::exception& e) {
        kmlogger.error("Error reading plugins list: %s", std::string(e.what()).c_str());
        return false;
    }
}

bool KMPluginSynchronizer::writePluginsList(const fs::path& pluginsListFile, const std::set<std::string>& allPlugins) {
    try {
        std::ofstream outFile(pluginsListFile);
        if (!outFile.is_open()) {
            kmlogger.error("Failed to open plugins list file for writing: %s", pluginsListFile.string().c_str());
            return false;
        }
        
        for (const auto& plugin : allPlugins) {
            outFile << plugin << std::endl;
        }
        outFile.close();
        return true;
        
    } catch (const std::exception& e) {
        kmlogger.error("Error writing plugins list: %s", std::string(e.what()).c_str());
        return false;
    }
}

bool KMPluginSynchronizer::syncSinglePlugin(const fs::path& sourcePluginDir, const fs::path& targetPluginDir) {
    try {
        if (!fs::exists(sourcePluginDir)) {
            kmlogger.error("Source path does not exist: %s", sourcePluginDir.string().c_str());
            return false;
        }

        fs::create_directories(targetPluginDir.parent_path());

        std::string src = sourcePluginDir.string();
        std::string dst = targetPluginDir.string();

        if (fs::is_directory(sourcePluginDir)) {
            if (src.back() != '/') src += '/';
        }

        // -a: Archive mode; enables recursive copying while preserving symbolic links, permissions, timestamps, etc.
        // --links: Preserve symbolic links
        std::string cmd = "rsync -a --links " + src + " " + dst;

        kmlogger.info("Executing rsync: %s", cmd.c_str());
        int ret = std::system(cmd.c_str());
        if (ret != 0) {
            kmlogger.error("rsync failed with code %d", ret);
            return false;
        }

        kmlogger.info("rsync completed: %s -> %s", sourcePluginDir.string().c_str(), targetPluginDir.string().c_str());
        return true;
    }
    catch (const std::exception& e) {
        kmlogger.error("Exception during syncSinglePlugin: %s", e.what());
        return false;
    }
}