#include "shared_commands.hpp"
#include "utils/assert.hpp"
#include "utils/utils.hpp"
#include "server_env_manager.hpp"
#include "directories.hpp"
#include "services.hpp"
#include "servers.hpp"

namespace dropshell
{

    namespace shared_commands
    {

    // ------------------------------------------------------------------------------------------------
    // std_autocomplete : SHARED COMMAND
    // ------------------------------------------------------------------------------------------------
    void std_autocomplete(const CommandContext &ctx)
    {
        if (ctx.args.size() == 0)
        { // just the command, no args yet.
            // list servers
            std::vector<ServerInfo> servers = get_configured_servers();
            for (const auto &server : servers)
            {
                std::cout << server.name << std::endl;
            }
        }
        else if (ctx.args.size() == 1)
        {
            // list services
            std::vector<LocalServiceInfo> services = get_server_services_info(ctx.args[0]);
            for (const auto &service : services)
            {
                std::cout << service.service_name << std::endl;
            }
        }
    }

    // ------------------------------------------------------------------------------------------------
    // std_autocomplete_allowall : SHARED COMMAND
    // ------------------------------------------------------------------------------------------------
    void std_autocomplete_allowall(const CommandContext &ctx)
    {
        std_autocomplete(ctx);
        if (ctx.args.size() == 1)
            std::cout << "all" << std::endl;
    }

    // ------------------------------------------------------------------------------------------------
    // rsync_tree_to_remote : SHARED COMMAND
    // ------------------------------------------------------------------------------------------------
    bool rsync_tree_to_remote(
        const std::string &local_path,
        const std::string &remote_path,
        server_env_manager &server_env,
        bool silent)
    {
        ASSERT(!local_path.empty() && !remote_path.empty(), "Local or remote path not specified. Can't rsync.");

        std::string rsync_cmd = "rsync --delete --mkpath -zrpc -e 'ssh -p " + server_env.get_SSH_PORT() + "' " +
                                quote(local_path + "/") + " " +
                                quote(server_env.get_SSH_USER() + "@" + server_env.get_SSH_HOST() + ":" +
                                      remote_path + "/");
        return execute_local_command(rsync_cmd, nullptr, (silent ? cMode::Silent : cMode::Defaults));
    }

    // ------------------------------------------------------------------------------------------------
    // get_arch : SHARED COMMAND
    // ------------------------------------------------------------------------------------------------
    std::string get_arch()
    {
        // determine the architecture of the system
        std::string arch;
#ifdef __aarch64__
        arch = "arm64";
#elif __x86_64__
        arch = "amd64";
#endif
        return arch;
    }

    // ------------------------------------------------------------------------------------------------
    // cRemoteTempFolder : SHARED CLASS
    // ------------------------------------------------------------------------------------------------
    cRemoteTempFolder::cRemoteTempFolder(const server_env_manager &server_env) : mServerEnv(server_env)
    {
        std::string p = remotepath::temp_files(server_env.get_server_name()) + "/" + random_alphanumeric_string(10);
        std::string mkdir_cmd = "mkdir -p " + quote(p);
        if (!execute_ssh_command(server_env.get_SSH_INFO(), sCommand("", mkdir_cmd, {}), cMode::Silent))
            std::cerr << "Failed to create temp directory on server" << std::endl;
        else
            mPath = p;
    }

    cRemoteTempFolder::~cRemoteTempFolder()
    {
        std::string rm_cmd = "rm -rf " + quote(mPath);
        execute_ssh_command(mServerEnv.get_SSH_INFO(), sCommand("", rm_cmd, {}), cMode::Silent);
    }

    std::string cRemoteTempFolder::path() const
    {
        return mPath;
    }

    // ------------------------------------------------------------------------------------------------
    // get_all_services_status : SHARED COMMAND
    // ------------------------------------------------------------------------------------------------
    std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name)
    {
        std::map<std::string, ServiceStatus> status;

        server_env_manager env(server_name);
        if (!env.is_valid())
        {
            std::cerr << "Error: Invalid server environment" << std::endl;
            return status;
        }

        std::string output;
        if (!execute_ssh_command(env.get_SSH_INFO(), sCommand(remotepath::agent(server_name), "./_allservicesstatus.sh", {{"HOST_NAME", server_name}, {"SERVER", server_name}, {"AGENT_PATH", remotepath::agent(server_name)}}), cMode::CaptureOutput, &output))
            return status;

        std::stringstream ss(output);
        std::string line;
        while (std::getline(ss, line))
        {
            std::string key, value;
            std::size_t pos = line.find("=");
            if (pos != std::string::npos)
            {
                key = dequote(trim(line.substr(0, pos)));
                value = dequote(trim(line.substr(pos + 1)));

                // decode key, it's of format SERVICENAME_[HEALTH|PORTS]
                std::string service_name = key.substr(0, key.find_last_of("_"));
                std::string status_type = key.substr(key.find_last_of("_") + 1);

                if (status_type == "HEALTH")
                { // healthy|unhealthy|unknown
                    if (value == "healthy")
                        status[service_name].health = HealthStatus::HEALTHY;
                    else if (value == "unhealthy")
                        status[service_name].health = HealthStatus::UNHEALTHY;
                    else if (value == "unknown")
                        status[service_name].health = HealthStatus::UNKNOWN;
                    else
                        status[service_name].health = HealthStatus::ERROR;
                }
                else if (status_type == "PORTS")
                { // port1,port2,port3
                    std::vector<std::string> ports = string2multi(value);
                    for (const auto &port : ports)
                    {
                        if (port != "unknown")
                            status[service_name].ports.push_back(str2int(port));
                    }
                }
            }
        }
        return status;
    }



    // ------------------------------------------------------------------------------------------------
    // healthtick : SHARED COMMAND
    // ------------------------------------------------------------------------------------------------
    std::string healthtick(const std::string &server, const std::string &service)
    {
        std::string green_tick = "\033[32m✓\033[0m";
        std::string red_cross = "\033[31m✗\033[0m";
        std::string yellow_exclamation = "\033[33m!\033[0m";
        std::string unknown = "\033[37m✓\033[0m";

        HealthStatus status = is_healthy(server, service);
        if (status == HealthStatus::HEALTHY)
            return green_tick;
        else if (status == HealthStatus::UNHEALTHY)
            return red_cross;
        else if (status == HealthStatus::UNKNOWN)
            return unknown;
        else
            return yellow_exclamation;
    }

    // ------------------------------------------------------------------------------------------------
    // HealthStatus2String : SHARED COMMAND
    // ------------------------------------------------------------------------------------------------
    std::string HealthStatus2String(HealthStatus status)
    {
        if (status == HealthStatus::HEALTHY)
            return ":tick:";
        else if (status == HealthStatus::UNHEALTHY)
            return ":cross:";
        else if (status == HealthStatus::UNKNOWN)
            return ":greytick:";
        else if (status == HealthStatus::NOTINSTALLED)
            return ":warning:";
        else
            return ":error:";
    }


    // ------------------------------------------------------------------------------------------------
    // is_healthy : SHARED COMMAND
    // ------------------------------------------------------------------------------------------------
    HealthStatus is_healthy(const std::string &server, const std::string &service)
    {
        server_env_manager env(server);
        if (!env.is_valid())
        {
            std::cerr << "Error: Server service not initialized" << std::endl;
            return HealthStatus::ERROR;
        }

        if (!env.check_remote_dir_exists(remotepath::service(server, service)))
        {
            return HealthStatus::NOTINSTALLED;
        }

        std::string script_path = remotepath::service_template(server, service) + "/status.sh";
        if (!env.check_remote_file_exists(script_path))
        {
            return HealthStatus::UNKNOWN;
        }

        // Run status script, does not display output.
        if (!env.run_remote_template_command(service, "status", {}, true, {}))
            return HealthStatus::UNHEALTHY;
        return HealthStatus::HEALTHY;
    }


    // ------------------------------------------------------------------------------------------------
    // healthmark : SHARED COMMAND
    // ------------------------------------------------------------------------------------------------
    std::string healthmark(const std::string &server, const std::string &service)
    {
        HealthStatus status = is_healthy(server, service);
        return HealthStatus2String(status);
    }

    } // namespace shared_commands

} // namespace dropshell