#pragma once

#include "metrics.h"
#include "merger_analyzer.h"
#include "merger_task.h"
#include "merger_thread.h"

#include <saas/library/daemon_base/config/watchdog_opts.h>
#include <saas/util/queue.h>
#include <util/generic/deque.h>
#include <util/thread/factory.h>

class TIndexMerger : public IMerger, public IMessageProcessor {
private:
    const TRTYServerConfig& Config;
    TIndexStorage& IndexStorage;

    TMap<TString, TAnalyzerAgent::TPtr> Analyzers;
    TRTYMtpQueue MergerAnalyzers;

    TVector<TMergerAgent::TPtr> MergeAgents;
    TRTYMtpQueue MergingThreads;
    TAutoGlobal<TMergerMetrics> Metrics;

    std::atomic<TThreadStatus> Status;
    TMutex MutexStop;
    TDeque<IMergerTask::TPtr> Tasks;
    TMutex MutexQueue;
    TCondVar CondVarQueue;
    TMutex ResourcesMutex;

    TWatchdogOptionsHandlePtr WatchdogOptions;

public:
    TIndexMerger(TIndexStorage& indexStorage, const TRTYServerConfig& config);
    ~TIndexMerger();

    void SetWatchdog(TWatchdogOptionsHandlePtr w);

    // IMessageProcessor
    bool Process(IMessage* message) override;
    TString Name() const override;

    // IMerger
    void Start() override;
    void Stop(bool rigidStop) override;

    TThreadStatus GetStatus() const override{
        return Status;
    }

    TMergerMetrics& GetMetrics() override {
        return Metrics;
    }

    void DoTask(IMergerTask::TPtr task, const std::atomic<bool>* rigidStop) override;

    IMergerTask::TPtr NextTask(unsigned timeWaitSeconds) override {
        TGuard<TMutex> g(MutexQueue);

        if (Status != tsActive)
            return nullptr;
        for (unsigned i = 0; i < timeWaitSeconds && Tasks.empty(); ++i) {
            CondVarQueue.WaitT(MutexQueue, TDuration::Seconds(1));
            if (Status != tsActive)
                return nullptr;
        }
        if (Tasks.empty())
            return nullptr;
        IMergerTask::TPtr result = Tasks.front();
        Tasks.pop_front();
        return result;
    }

    bool Y_WARN_UNUSED_RESULT AddTask(unsigned shard, TVector<TString> segments, const TString& path, IMergerTaskNotifier& notifier, IMergerLockPolicy::TPtr lockPolicy, bool isPortions) override {
        TGuard<TMutex> g(MutexQueue);
        if (Status != tsActive && Status != tsStarting)
            return false;

        Tasks.push_back(new TMergeTask(shard, segments, Config, path, notifier, Metrics, lockPolicy, isPortions));
        CondVarQueue.Signal();
        return true;
    }

    void WaitMergingFinished(const TString& path) const override;
    TIndexStorage& GetIndexStorage() override {
        return IndexStorage;
    }
    TMutex& GetResourcesMutex() override {
        return ResourcesMutex;
    }
private:
    void SetStatus(TThreadStatus status) {
        TGuard<TMutex> g(MutexQueue);
        Status = status;
    }
};
