#pragma once

#include <saas/deploy_manager/request_wizard/request_wizard.h>

#include <saas/library/daemon_base/actions_engine/controller_client.h>
#include <saas/library/daemon_base/config/daemon_config.h>
#include <saas/library/daemon_base/module/module_processors.h>
#include <saas/library/storage/abstract.h>

#include <library/cpp/http/server/options.h>

#include <util/generic/noncopyable.h>
#include <util/generic/ptr.h>
#include <util/string/util.h>


namespace NRTYDeploy {
    using NSaas::TStorageOptions;
}

class TDeployManagerConfig: public IServerConfig {
public:
    class TGolemApiConfig : public TDaemonConfig::TConnection {
    public:
        TGolemApiConfig();
        TMap<TString, TString> Aliases;
        TString AliasesString;
        TString Uri;
        void Init(const TYandexConfig::Directives& directives);
        TString ToString() const;
    };

    class TServices20Config : public TDaemonConfig::TConnection {
    public:
        typedef THashMap<TString, TString> TClusterByLocation;
        typedef THashMap<TString, TClusterByLocation> TClustersByCType;
        TServices20Config();
        void Init(const TYandexConfig::Section& section);
        void Init(const TYandexConfig::Directives& directives);
        TString ToString() const;
        TFsPath AuthKeyPath;
        TString Uri;
        TClustersByCType ClustersByCType;
    };

    class TWatchDogConfig {
    public:
        TWatchDogConfig();
        ui64 MaxMemoryCount;
        bool Enabled;
        void Init(const TYandexConfig::Directives& directives);
        TString ToString() const;
    };

    class TCheckerConfig {
    public:
        TCheckerConfig();
        ui32 HoursForCheck;
        ui32 PercentForCheck;
        ui32 AttemptsPing;
        ui32 TimeoutPing;
        ui32 CheckerThreads;
        bool Persist = false;
        ui32 CacheValidityPeriodSeconds = 10;
        bool Enabled = true;
        void Init(const TYandexConfig::Directives& directives);
        TString ToString() const;
    };

    class TWatcherConfig {
    public:
        TWatcherConfig();
        bool Enabled;
        TDuration Period;
        ui32 KeepDays;
        void Init(const TYandexConfig::Directives& directives);
        TString ToString() const;
    };

    class TNannyConfig {
    public:
        TNannyConfig() {

        }
        TString Host = "nanny.yandex-team.ru";
        ui32 Port = 443;
        TString RequestSlotsByServiceTemplate = "/v2/services/%tag_name%/current_state/instances/";
        TString RequestSlotsByTagTemplate = "/v2/services/%tag_name%/current_state/instances/";
        TString RequestRuntimeAttrsByTagTemplate = "/v2/services/%tag_name%/runtime_attrs/";
        TDuration InterSendTimeout = TDuration::Seconds(1);
        TString TokenEnv = "OAUTH_NANNY";
        TString Token = "";
        TString CacheDir = "";
        ui32 CacheFallbackErrorsCnt = 20;
        int CacheFallbackUndoMinutes = 5;
        bool CacheTestMode = false;

        void Init(const TYandexConfig::Directives& directives);
        TString ToString() const;

        bool IsInitialized() const;
    };

    class TJugglerConfig : public TDaemonConfig::THttpOptions {
    private:
        TString CommonTag;
        TString Namespace;
        TString Token;
        TString TokenEnv;
        bool IsTesting;
    public:
        TJugglerConfig();
        const TString& GetCommonTag() const {
            return CommonTag;
        }
        const TString& GetNamespace() const {
            return Namespace;
        }
        const TString& GetToken() const {
            return Token;
        }
        bool GetIsTesting() const {
            return IsTesting;
        }
        void Init(const TYandexConfig::Directives& directives, bool verifyThreads = true) override;
        void Verify() const;
        TString ToString() const;
    };

    class TGolovanConfig : public TDaemonConfig::THttpOptions {
    private:
        TString AlertsPrefix;
    public:
        TGolovanConfig();
        const TString& GetAlertsPrefix() const {
            return AlertsPrefix;
        }
        void Init(const TYandexConfig::Directives& directives, bool verifyThreads = true) override;
        void Verify() const;
        TString ToString() const;
    };

    class TAlertsConfig {
    private:
        TJugglerConfig Juggler;
        TGolovanConfig Golovan;
        bool Enable;
        TString CtypesPrefix;
    public:
        TAlertsConfig();
        const TJugglerConfig& GetJugglerConfig() const {
            return Juggler;
        }
        const TGolovanConfig& GetGolovanConfig() const {
            return Golovan;
        }
        bool GetEnabled() const {
            return Enable;
        }
        const TString& GetCtypesPrefix() const {
            return CtypesPrefix;
        }
        void Verify() const;
        void Init(const TYandexConfig::Section& section);
        void Init(const TYandexConfig::Directives& directives);
        TString ToString() const;
    };

    class TExperimentsConfig {
    public:
        TString ConfigUrl;
        TString ConfigPath;

        TExperimentsConfig();
        void Init(const TYandexConfig::Directives& directives);
        TString ToString() const;
    };

    class TDmBalanser : public TDaemonConfig::THttpOptions {
    public:
        TString UriPrefix;
        virtual void Init(const TYandexConfig::Directives& directives, bool verifyThreads = true) override;
    protected:
        virtual void ToStringImpl(TStringStream& so) const override;
    };

    class TRegularCommandInfo {
    private:
        TDuration RestartInterval;
        TString ActionType;
        NJson::TJsonValue Description;
        TString Name;
    public:
        TDuration GetRestartInterval() const {
            return RestartInterval;
        }

        TString GetName() const {
            return Name;
        }

        NDaemonController::TAction::TPtr GetAction() const {
            NDaemonController::TAction::TPtr result = NDaemonController::TAction::TFactory::Construct(ActionType);
            VERIFY_WITH_LOG(!!result, "Incorret action type %s", ActionType.data());
            try {
                result->Deserialize(Description);
            } catch (...) {
                FAIL_LOG("Can't deserialize action %s: %s", Name.data(), CurrentExceptionMessage().data());
            }
            return result;
        }

        TRegularCommandInfo(const NJson::TJsonValue& value) {
            RestartInterval = TDuration::Parse(value["restart_interval"].GetStringRobust());
            ActionType = value["action_type"].GetStringRobust();
            Name = value["name"].GetStringRobust();
            Description["result"]["status"] = "NOTSTARTED";
            Description["async_policy"] = "SW";
            Description["task"] = value["description"];
            NDaemonController::TAction::TPtr action = GetAction();
            VERIFY_WITH_LOG(action->AsyncPolicy() != NDaemonController::apStart, "Incorrect async policy for %s", Name.data());
        }
    };

public:
    TDeployManagerConfig(const TServerConfigConstructorParams& params);
    void InitFromString(const char* configText);
    void Init(const TYandexConfig::Directives& directives);
    TString ToString() const;

    const TVector<TRegularCommandInfo>& GetRegularCommands() const {
        return RegularCommands;
    }

    TSet<TString> GetModulesSet() const {
        TSet<TString> result;
        result.insert(MESSAGE_PROCESSORS_MODULE_NAME);
        return result;
    }

    const TDaemonConfig& GetDaemonConfig() const {
        return DaemonConfig;
    }

    const TDaemonConfig::THttpOptions& GetHttpOptionsConfig() const {
        return HttpOptionsConfig;
    }

    const TString& GetNotifyAddress() const {
        return NotifyAddress;
    }

    const NRTYDeploy::TStorageOptions& GetStorageOptions() const {
        return StorageOptions;
    }

    const TString& GetSVNPath() const {
        return SVNPath;
    }

    const TString& GetPrivateKey() const {
        return PrivateKey;
    }

    const TDmBalanser& GetDeployManagerBalanser() const {
        return DeployManagerBalanser;
    }

    const TWatchDogConfig& GetWatchDog() const {
        return WatchDog;
    }

    const TWatcherConfig& GetWatcher() const {
        return Watcher;
    }

    const TNannyConfig& GetNanny() const {
        return Nanny;
    }

    const TCheckerConfig& GetChecker() const {
        return Checker;
    }

    const TExperimentsConfig& GetExperimentsConfig() const {
        return ExperimentsConfig;
    }

    const TDuration& GetTimeCacheLive() const {
        return TimeCacheLive;
    }

    ui32 GetExecuteScriptThreads() const {
        return ExecuteScriptThreads;
    }

    ui32 GetTasksExecutorThreads() const {
        return TasksExecutorThreads;
    }

    ui32 GetServiceQueueThreads() const noexcept {
        return ServiceQueueThreads;
    }

    const TRequestWizard::TRequestWizardConfig& GetRequestWizardConfig() const {
        return RequestWizardConfig;
    }

    const TString& GetRequiredUriPrefix() const {
        return RequiredUriPrefix;
    }

    const TString& GetRegularCommandsFile() const {
        return RegularCommandsFile;
    }

    const TString& GetServiceDiscoveryOptions() const {
        return ServiceDiscoveryOptions;
    }

    const TServices20Config& GetServices20Config() const {
        return Services20Config;
    }

    const TAlertsConfig& GetAlertsConfig() const {
        return AlertsConfig;
    }

    bool GetUseExecuteScriptInDeploy() const {
        return UseExecuteScriptInDeploy;
    }
private:
    TDaemonConfig::THttpOptions HttpOptionsConfig;
    TDmBalanser DeployManagerBalanser;
    TDaemonConfig DaemonConfig;
    TGolemApiConfig GolemApi;
    TWatchDogConfig WatchDog;
    TWatcherConfig Watcher;
    TNannyConfig Nanny;
    TCheckerConfig Checker;
    TExperimentsConfig ExperimentsConfig;
    TServices20Config Services20Config;
    TAlertsConfig AlertsConfig;
    NRTYDeploy::TStorageOptions StorageOptions;
    TString ServiceDiscoveryOptions;
    TString NotifyAddress;
    TString SVNPath;
    TString PrivateKey;
    TString RequiredUriPrefix;
    TString RegularCommandsFile;
    TDuration TimeCacheLive;
    ui32 ExecuteScriptThreads;
    ui32 TasksExecutorThreads;
    ui32 ServiceQueueThreads;
    TRequestWizard::TRequestWizardConfig RequestWizardConfig;
    TVector<TRegularCommandInfo> RegularCommands;
    bool UseExecuteScriptInDeploy = true;
};
