#pragma once

#include "data_source.h"

#include <library/cpp/balloc/optional/operators.h>
#include <library/cpp/logger/global/global.h>

#include <saas/rtyserver/common/common_messages.h>
#include <saas/rtyserver/docfetcher/library/types.h>
#include <saas/util/json/json.h>

#include <util/system/event.h>
#include <util/system/thread.h>
#include <util/system/mutex.h>


namespace NFusion {
    namespace NDatacenterChecker {
        struct TDatacenterStatus {
            bool IsFailed = false;
            TInstant BlockedUntil;
        };
    }

    class TDatacenterChecker : public IMessageProcessor {
    public:
        class ISubscriber {
        public:
            virtual void DatacenterChangedStatus(const TString& datacenter, bool isFailed) = 0;
        };

    private:
        const TDatacenterCheckerConfig& Config;

        THashMap<TString, NDatacenterChecker::TDatacenterStatus> Statuses;

        THolder<TThread> Checker;
        THolder<NDatacenterChecker::IDataSource> DataSource;
        TVector<ISubscriber*> Subscribers;
        TManualEvent NeedStop;
        TMutex Lock;

    public:
        TDatacenterChecker(const TDatacenterCheckerConfig& config);
        ~TDatacenterChecker();

        void Start();
        void Stop();

        void Subscribe(ISubscriber* subscriber);

        TString GetCorrectDatacenter(const TVector<TString> datacenters) const;
        NJson::TJsonValue GetStatus() const;

    protected:
        virtual TString Name() const override;
        virtual bool Process(IMessage* message_) override;

    private:
        void SendNotify(const TString& dc, bool isFailed) const;

        void SetStatus(
            const TString& dc,
            NDatacenterChecker::TDatacenterStatus& status,
            bool isFailed,
            const TDuration& blockDuration
        );
        void UpdateStatus(const TString& dc, NDatacenterChecker::TDatacenterStatus& status, bool isFailed);

        void TryClearStatuses();
        void ApplyCheckResult(const NDatacenterChecker::TSuccessCheck& successCheck);

        static void* CheckerProc(void* object);
    };
}
