#include <string>
#include <vector>
#include <filesystem>
#include <memory>
#include <set>

#include "utils/json.hpp"

namespace dropshell {

typedef enum template_source_type {
    TEMPLATE_SOURCE_TYPE_LOCAL,
    TEMPLATE_SOURCE_TYPE_REGISTRY,
    TEMPLATE_SOURCE_NOT_SET
} template_source_type;

class template_info {
    public:
        template_info() : mIsSet(false) {}
        template_info(const std::string& template_name, const std::string& location_id, const std::filesystem::path& local_template_path) : mTemplateName(template_name), mLocationID(location_id), mTemplateLocalPath(local_template_path), mIsSet(true) {}
        virtual ~template_info() {}
        bool is_set() { return mIsSet; }
        std::string name() { return mTemplateName; }
        std::string locationID() { return mLocationID; }
        std::filesystem::path local_template_path() { return mTemplateLocalPath; }
    private:
        std::string mTemplateName;
        std::string mLocationID;
        std::filesystem::path mTemplateLocalPath; // source or cache.
        bool mIsSet;
};

class template_source_interface {
    public:
        virtual ~template_source_interface() {}
        virtual std::set<std::string> get_template_list() = 0;
        virtual bool has_template(const std::string& template_name) = 0;
        virtual template_info get_template_info(const std::string& template_name) = 0;
        virtual bool template_command_exists(const std::string& template_name,const std::string& command) = 0;
};

class template_source_registry : public template_source_interface {
    public:
        template_source_registry(std::string URL) : mURL(URL) {}

        ~template_source_registry() {}

        std::set<std::string> get_template_list();
        bool has_template(const std::string& template_name);
        template_info get_template_info(const std::string& template_name);
        bool template_command_exists(const std::string& template_name,const std::string& command);
    private:
        std::filesystem::path get_cache_dir();
    private:
        std::string mURL;
        std::vector<nlohmann::json> mTemplates; // cached list.
};

class template_source_local : public template_source_interface {
    public:
        template_source_local(std::string local_path) : mLocalPath(local_path) {}
        ~template_source_local() {}
        std::set<std::string> get_template_list();
        bool has_template(const std::string& template_name);
        template_info get_template_info(const std::string& template_name);
        bool template_command_exists(const std::string& template_name,const std::string& command);
    private:
        std::filesystem::path mLocalPath;
};

class template_manager {
    public:
        template_manager() : mLoaded(false) {}
        ~template_manager() {}

        std::set<std::string> get_template_list();
        bool has_template(const std::string& template_name);
        template_info get_template_info(const std::string& template_name);

        bool template_command_exists(const std::string& template_name,const std::string& command);
        void create_template(const std::string& template_name);
        bool test_template(const std::string& template_path);

        void list_templates();

    private:
        void load_sources();
        bool required_file(std::string path, std::string template_name);
        template_source_interface* get_source(const std::string& template_name);

    private:
        bool mLoaded;
        std::vector<std::unique_ptr<template_source_interface>> mSources;
};

template_manager & gTemplateManager();


} // namespace dropshell
