// server_service.hpp
//
// manage a service on a server
//

#ifndef SERVICE_RUNNER_HPP
#define SERVICE_RUNNER_HPP  

#include <string>
#include <vector>
#include <memory>
#include "server_env_manager.hpp"
#include "services.hpp"
#include "utils/utils.hpp"
#include "utils/assert.hpp"
#include "utils/hash.hpp"

namespace dropshell {

typedef enum HealthStatus {
    HEALTHY,
    UNHEALTHY,
    NOTINSTALLED,
    ERROR,
    UNKNOWN
} HealthStatus;

typedef struct ServiceStatus {
    HealthStatus health;
    std::vector<int> ports;
} ServiceStatus;

class service_runner {
    public:
        service_runner(const std::string& server_name, const std::string& service_name);
        bool isValid() const { return mValid; }

        // run a command over ssh, using the credentials from server.env (via server_env.hpp)
        // first check that the command corresponds to a valid .sh file in the service directory
        // then run the command, passing the {service_name}.env file as an argument
        // do a lot of checks, such as:
        // checking that we can ssh to the server.
        // checking whether the service directory exists on the server.
        // checking that the command exists in the service directory.
        // checking that the command is a valid .sh file.
        // checking that the {service_name}.env file exists in the service directory.
        bool run_command(const std::string& command);

        // check health of service. Silent.
        // 1. run status.sh on the server
        // 2. return the output of the status.sh script

        HealthStatus is_healthy();

        std::string healthtick();
        std::string healthmark();

        // get the status of all services on the server
        static std::map<std::string, ServiceStatus> get_all_services_status(std::string server_name);
        static std::string HealthStatus2String(HealthStatus status);

        // ensure the service related dropshell files (template and config) are up to date on the remote server.
        bool ensure_service_dropshell_files_up_to_date();

    private:    
        // install the service over ssh, using the credentials from server.env (via server_env.hpp), by:
        // 1. check if the server_name exists, and the service_name refers to a valid template
        // 2. check if service_name is valid for the server_name
        // 3. create the service directory on the server at {DROPSHELL_DIR}/{service_name}
        // 3. copy the template files into {DROPSHELL_DIR}/{service_name}/template (from the templates directory for the specified server, using templates.hpp to identify the path)
        // 4. copying the local service directory into {DROPSHELL_DIR}/{service_name}/config (from the server directory for the specified server)
        // 5. running the install.sh script on the server, passing the {service_name}.env file as an argument
        bool install(); 
        
        // uninstall the service over ssh, using the credentials from server.env (via server_env.hpp)
        // 1. check if the server_name exists, and the service_name refers to a valid template
        // 2. check if service_name is valid for the server_name
        // 3. run the uninstall.sh script on the server, passing the {service_name}.env file as an argument
        // 4. remove the service directory from the server
        bool uninstall();

        // restore the service over ssh, using the credentials from server.env (via server_env.hpp)
        // 1. copy the backup file to the server's DROPSHELL_DIR/backups folder
        // 2. run the restore.sh script on the server, passing the {service_name}.env file as an argument
        bool restore(std::string backup_file);

        // launch an interactive ssh session on a server or service
        // replaces the current dropshell process with the ssh process
        void interactive_ssh_service();

        // edit the service configuration file
        void edit_service_config();

    private:
        std::string mServer;
        server_env_manager mServerEnv;
        ServiceInfo mServiceInfo;
        std::string mService;
        bool mValid;

        // Helper methods
    public:
};


// other utility routines (not specific to service_runner)
void interactive_ssh(const std::string & server_name, const std::string & command);
void edit_server(const std::string & server_name);
void edit_file(const std::string & file_path, const std::string & aftertext);

// check if the service template and config are up to date on the remote server.
class service_versions {
    public:
        service_versions(const std::string & server_name, const std::string & service_name);

        bool remote_up_to_date() { return remote_template_is_up_to_date() && remote_config_is_up_to_date(); }

        bool remote_template_is_up_to_date() { return get_version_remote_service_template() == calculate_version_local_service_template(); }
        bool remote_config_is_up_to_date() { return get_version_remote_config() == calculate_version_local_config(); }    

        uint64_t calculate_version_local_service_template();
        uint64_t calculate_version_local_config();

        void update_stored_remote_versions();
        uint64_t get_version_remote_config();
        uint64_t get_version_remote_service_template();

    private:
        uint64_t calculate_version_remote_service_template();
        uint64_t calculate_version_remote_config();

    private:
        std::string m_server_name;
        std::string m_service_name;
};


} // namespace dropshell

#endif // SERVICE_RUNNER_HPP
