#pragma once

#ifndef WMCUTIL_CONFIG_BASE_H
#define WMCUTIL_CONFIG_BASE_H

#include <stddef.h>

#include <library/cpp/config/config.h>
#include <util/string/subst.h>
#include <util/string/printf.h>

#include "log.h"

namespace NWebmaster {

struct TConfigBase {
    TConfigBase();
    virtual ~TConfigBase() {
    }

    virtual void Load();

    static TString GetProgramRevision();
    static TString GetRunTimestamp();

private:
    template<class... Args>
    static void LogInfo(const char* fmt, Args... args) {
        if (!LogDisabled) {
            LOG_INFO(fmt, args...);
        }
    }

    template <typename T>
    void Prepare(T&) const {
    }

    void Prepare(TString& value) const {
        SubstGlobal(value, "_TIMESTAMP_", GetRunTimestamp());
    }

    template <typename T>
    bool GetInternal(const TString &param, T &value) const {
        TStringStream valueStr;
        if (LocalOkFlag && !localConfig[param].IsNull()) {
            value = localConfig[param].As<T>();
            Prepare(value);
            valueStr << value;
            LogInfo("Loaded local config value <%s=%s>", param.data(), valueStr.Str().data());
            return true;
        }

        if (GlobalOkFlag && !globalConfig[param].IsNull()) {
            value = globalConfig[param].As<T>();
            Prepare(value);
            valueStr << value;
            LogInfo("Loaded global config value <%s=%s>", param.data(), valueStr.Str().data());
            return true;
        }

        return false;
    }

public:
    template <typename T>
    T Get(const TString &param) const {
        TStringStream valueStr;

        try  {
            T value;
            if (GetInternal(param, value)) {
                return value;
            }
        } catch (const std::exception &e) {
            throw yexception() << "Failed to load required config value <" << param << ">, stopping. Error: " << e.what();
        }

        throw yexception() << "Unable to find required config value <" << param << ">, stopping";
    }

    template <typename T>
    T Get(const TString &param, const T &default_value) const {
        TStringStream valueStr;

        try  {
            T value;
            if (GetInternal(param, value)) {
                return value;
            }
        } catch (const std::exception &e) {
            valueStr << default_value;
            LOG_ERROR("Failed to load config value, used default <%s=%s>. Error: %s", param.data(), valueStr.Str().data(), e.what());
            return default_value;
        }

        valueStr << default_value;
        LogInfo("Unable to find config value, used default <%s=%s>", param.data(), valueStr.Str().data());
        return default_value;
    }

    template<typename T, typename D>
    void Assign(T &val, const TString &name, const D &defaultValue) {
        val = Get<T>(name, defaultValue);
    }

    template<typename T>
    void Assign(T &val, const TString &name) {
        val = Get<T>(name);
    }

    bool IsGlobalOk() const;
    bool IsOk() const;

    static const TString GetYTToken(const char *key = "YT_TOKEN");

private:
    static TString GetFormatDate(const TString &format);

private:
    NConfig::TConfig globalConfig;
    NConfig::TConfig localConfig;

    bool GlobalOkFlag;
    bool LocalOkFlag;
    static bool LogDisabled;
};

} //NWebmaster

#endif //WMCUTIL_CONFIG_BASE_H
