#include "stream.h"
#include "docfetcher.h"
#include "messages.h"

#include <saas/rtyserver/common/common_messages.h>
#include <saas/rtyserver/common/stream_messages.h>
#include <saas/rtyserver/indexer_core/reopen.h>
#include <saas/rtyserver/docfetcher/library/stream_basics.h>
#include <saas/rtyserver/docfetcher/library/types.h>

#include <library/cpp/logger/global/global.h>

#include <util/generic/cast.h>

NFusion::TBaseStream::TBaseStream(const TDocFetcherModule& owner, const TBaseStreamConfig& config, TLog& log, const TString& threadName)
    : IThreadLoop(threadName)
    , BaseConfig(config)
    , Log(log)
    , SearchDisabled(0)
    , AsyncStart(config.AsyncStart)
    , Owner(owner)
{
}

void NFusion::TBaseStream::SubscribeToWatchdog(IWatchdogOptions&) {
}

void NFusion::TBaseStream::OnBeforeStart() {
    if (AsyncStart) {
        NOTICE_LOG << "fetcher stream " << BaseConfig.Name << "will start async" << Endl;
    } else {
        NOTICE_LOG << "Starting fetcher stream " << BaseConfig.Name << Endl;
        OnStreamStart();
    }
}

void NFusion::TBaseStream::OnAfterStart() {
    RegisterGlobalMessageProcessor(this);
    if (!AsyncStart) {
        NOTICE_LOG << "Started fetcher stream " << BaseConfig.Name << Endl;
    }
}

NFusion::IThreadLoop::TTimeToSleep NFusion::TBaseStream::DoIteration() {
    if (AsyncStart) {
        NOTICE_LOG << "Starting fetcher stream " << BaseConfig.Name << Endl;
        OnStreamStart();
        NOTICE_LOG << "Started fetcher stream " << BaseConfig.Name << Endl;
        AsyncStart = false;
        return TTimeToSleep::Zero();
    }
    if (BaseConfig.PauseOnMerge) {
        ui32 mergerTasks = Owner.GetMergerTaskCounter();
        if (mergerTasks != 0) {
            INFO_LOG << "Stream " << BaseConfig.Name << " is paused until merge (" << mergerTasks << " tasks now) is done" << Endl;
            return BaseConfig.PauseOnMergeDuration;
        }
    }
    return DoStreamIteration();
}

void NFusion::TBaseStream::OnBeforeStop() {
    UnregisterGlobalMessageProcessor(this);
    NOTICE_LOG << "Stopping fetcher stream " << BaseConfig.Name << Endl;
    OnStreamBeforeStop();
}

void NFusion::TBaseStream::OnAfterStop() {
    OnStreamStop();
    NOTICE_LOG << "Stopped fetcher stream " << BaseConfig.Name << Endl;
}

void NFusion::TBaseStream::OnPause() {
    NOTICE_LOG << "Paused stream " << BaseConfig.Name << Endl;
}

void NFusion::TBaseStream::OnResume() {
    NOTICE_LOG << "Resumed stream " << BaseConfig.Name << Endl;
}

const TString& NFusion::TBaseStream::GetStreamName() const {
    return BaseConfig.Name;
}

TString NFusion::TBaseStream::Name() const {
    return JoinMetricName(BaseConfig.Name, "FetcherStream");
}

bool NFusion::TBaseStream::Process(IMessage* message_) {
    if (message_->As<TMessagePauseDocfetcher>()) {
        Pause();
        return true;
    }
    if (message_->As<TMessageContinueDocfetcher>()) {
        Resume();
        return true;
    }
    if (auto message = message_->As<TMessageGetServerHealth>()) {
        message->ShardInfo.insert(std::make_pair(BaseConfig.Shard, BaseConfig.NumShards));
        return true;
    }
    return false;
}

NRTYServer::TTimestampSnapshot NFusion::TBaseStream::GetTimestampSnapshot() const {
    if (BaseConfig.UseIndexTimestamp) {
        TMessageGetIndexTimestamp message;
        SendGlobalMessage(message);
        return message.GetTimestamp().GetCurrentSnapshot();
    } else {
        return NRTYServer::TTimestampSnapshot();
    }
}

NRTYServer::TPositionsSnapshot NFusion::TBaseStream::GetPositionsSnapshot() const {
    TMessageGetIndexPositions message;
    SendGlobalMessage(message);
    return message.GetPositions().GetCurrentSnapshot();
}

TDuration NFusion::TBaseStream::GetDelayFromSubStream(const NRTYServer::TTimestampSnapshot& snapshot, ui32 substream) const {
    const auto p = snapshot.find(substream);
    const TInstant timestamp = p != snapshot.end() ? TInstant::Seconds(SafeIntegerCast<ui64>(p->second.MaxValue)) : TInstant::Zero();
    const TInstant now = Now();

    Y_ASSERT(now >= timestamp);
    return now >= timestamp ? now - timestamp : TDuration::Zero();
}

void NFusion::TBaseStream::DisableSearch() {
    if (SearchDisabled != 0) {
        DEBUG_LOG << "Stream " << BaseConfig.Name << " increments SearchDisabled" << Endl;
        SearchDisabled++;
        return;
    }

    NOTICE_LOG << "Stream " << BaseConfig.Name << " requests to disable search" << Endl;
    SendGlobalMessage<TMessageDocfetcherSearchBan>(true);
    SearchDisabled = 1;
}

void NFusion::TBaseStream::EnableSearch() {
    if (SearchDisabled != 1) {
        DEBUG_LOG << "Stream " << BaseConfig.Name << " decrements SearchDisabled" << Endl;
        SearchDisabled = SearchDisabled ? SearchDisabled - 1 : 0;
        return;
    }

    NOTICE_LOG << "Stream " << BaseConfig.Name << " requests to enable search" << Endl;
    SendGlobalMessage<TMessageDocfetcherSearchBan>(false);
    SearchDisabled = 0;
}

bool NFusion::TBaseStream::SearchDisabledByStream() {
    return SearchDisabled;
}

void NFusion::TBaseStream::ReopenIndexers() {
    NRTYServer::ReopenIndexers();
}

void NFusion::TBaseStream::OnWatchdogOption(const TString&, const TString&) {
}
