#include "settings.h"

#include <library/cpp/logger/global/global.h>
#include <library/cpp/yconf/patcher/unstrict_config.h>

#include <util/string/join.h>

namespace NSaas {

TPersQueueWriterSettings& TPersQueueWriterSettings::SetDryRun(bool value) {
    DryRun = value;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetLogger(TIntrusivePtr<NPersQueue::ILogger>& logger) {
    Logger = logger;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetPersQueueSettings(const TString& server, const TString& directoryWithTopics, NPersQueue::TPQLibSettings pqLibSettings) {
    Server = server;
    DirectoryWithTopics = directoryWithTopics;
    PQLibSettings = pqLibSettings;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetPQLib(TPQLibPtr pqLib) {
    PQLib = pqLib;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetSourceIdPrefix(const TString& sourceIdPrefix) {
    SourceIdPrefix = sourceIdPrefix;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetCodec(NPersQueueCommon::ECodec codec) {
    Codec = codec;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetServiceInfo(const TSearchMapInputSettings& searchMapSettings, const TString& service) {
    ServiceName = service;

    SearchMapSettings = searchMapSettings;
    ServiceShards.reset();
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetServiceInfo(const NSearchMapParser::TSearchMap& searchMap, const TString& service) {
    ServiceName = service;

    ServiceShards = TServiceShards();
    ServiceShards->Init(searchMap, service);
    SearchMapSettings.reset();
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetServiceInfo(const TVector<NSearchMapParser::TShardsInterval>& shards, const TShardsDispatcher::TContext& dispatching) {
    ServiceShards = TServiceShards();
    ServiceShards->Init(shards, dispatching);
    SearchMapSettings.reset();
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetTvm(std::shared_ptr<NPersQueue::ICredentialsProvider> credentialsProvider) {
    CredentialsProvider = credentialsProvider;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetTvm(std::shared_ptr<NTvmAuth::TTvmClient> tvmClient, const TString tvmAlias) {
    CredentialsProvider = NPersQueue::CreateTVMCredentialsProvider(tvmClient, Logger, tvmAlias);
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetTvm(
    NTvmAuth::TTvmId srcClientId,
    NTvmAuth::TTvmId dstClientId,
    const TString& secret
) {
    CredentialsProvider = NPersQueue::CreateTVMCredentialsProvider(secret, srcClientId, dstClientId, Logger);
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetQloudTvm(
    NTvmAuth::TTvmId srcClientId,
    NTvmAuth::TTvmId dstClientId,
    ui32 port /*= 1*/
) {
    CredentialsProvider = NPersQueue::CreateTVMQloudCredentialsProvider(srcClientId, dstClientId, Logger, TDuration::Minutes(10), port);
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetMaxAttempts(ui32 maxAttempts) {
    MaxAttempts = maxAttempts;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetConnectionTimeout(TDuration connectionTimeout) {
    ConnectionTimeout = connectionTimeout;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetSendTimeout(TDuration sendTimeout) {
    SendTimeout = sendTimeout;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetPrepareMessages(bool allow) {
    PrepareMessages = allow;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetCheckMessagesSettings(const TCheckMessageSettings& settings) {
    CheckMessageSettings = settings;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetThreadsBlockingMode(bool blocking) {
    ThreadsBlockingMode = blocking;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetWriteThreadsCount(ui64 threadsCount) {
    ThreadsCount = threadsCount;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetMaxInFlightCount(ui64 maxInFlightCount) {
    MaxInFlightCount = maxInFlightCount;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetNoWriteQueue(bool value) {
    NoWriteQueue = value;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetThreadsName(const TString& name) {
    ThreadsName = name;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetTelemetryConfig(const TTelemetryConfig& config) {
    TelemetryConfig = config;
    TelemetryInterval.reset();
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetEnableTelemetry(const TDuration& interval /*= RECOMMENDED_TELEMETRY_INTERVAL*/) {
    TelemetryConfig.reset();
    TelemetryInterval = interval;
    return *this;
}

TPersQueueWriterSettings& TPersQueueWriterSettings::SetUseLogbrokerCDS(const NPersQueue::EClusterDiscoveryUsageMode useLogbrokerCDS) {
    UseLogbrokerCDS = useLogbrokerCDS;
    return *this;
}

bool TPersQueueWriterSettings::IsCorrectlyFilled(TString* error = nullptr) const {
    try {
        if (!DryRun) {
            Y_ENSURE(Server, "empty server field");
            Y_ENSURE(DirectoryWithTopics, "empty directory topics path");
            Y_ENSURE(CredentialsProvider, "not set credentials settings (tvm)");
        }

        Y_ENSURE(SearchMapSettings || ServiceShards, "not set shards information");
        if (ServiceShards) {
            Y_ENSURE(ServiceShards->Initilized(), "service shards initilized incorrectly");
        }
    } catch(...) {
        if (error) {
            *error = CurrentExceptionMessage();
        }
        ERROR_LOG << "TPersQueueWriterSettings are specified incorrectly: " << CurrentExceptionMessage() << Endl;
        return false;
    }
    return true;
}

class TPrintSection {
public:
    TPrintSection(IOutputStream& so, const TString& name)
        : So(so)
        , Name(name)
    {
        So << "<" << Name << ">" << Endl;
    }
    ~TPrintSection() {
        So << "</" << Name << ">" << Endl;
    }

private:
    IOutputStream& So;
    const TString Name;
};

TString TPersQueueWriterSettings::GetStringConfig() const {
    TStringStream so;
    {
        TPrintSection writerSettings(so, "WriterSettings");
        so << "DryRun: " << DryRun << Endl;

        so << "Logger: " << bool(Logger) << Endl;
        so << "Server: " << Server << Endl;
        so << "DirectoryWithTopics: " << DirectoryWithTopics << Endl;
        so << "SourceIdPrefix: " << SourceIdPrefix << Endl;
        so << "PQLib: " << bool(PQLib) << Endl;
        so << "ServiceName: " << ServiceName << Endl;
        so << "MaxAttempts: " << MaxAttempts << Endl;
        so << "ConnectionTimeout: " << ConnectionTimeout << Endl;
        so << "SendTimeout: " << SendTimeout << Endl;
        so << "PrepareMessages: " << PrepareMessages << Endl;
        so << "ThreadsBlockingMode: " << ThreadsBlockingMode << Endl;
        so << "ThreadsCount: " << ThreadsCount << Endl;
        so << "MaxInFlightCount: " << MaxInFlightCount << Endl;
        so << "NoWriteQueue: " << NoWriteQueue << Endl;
        so << "ThreadsName: " << ThreadsName << Endl;
        so << "CredentialsProvider: " << bool(CredentialsProvider) << Endl;
        if (Codec) {
            so << "Codec: " << Codec.value() << Endl;
        }
        if (SearchMapSettings) {
            TPrintSection searchMapSettingsSection(so, "SearchMap");
            SearchMapSettings.value().Print(so);
        }
        if (ServiceShards) {
            TPrintSection serviceShardsSection(so, "ServiceShards");
            auto shards = ServiceShards->GetShards();
            so << "Shards: " << JoinRange(",", shards.begin(), shards.end()) << Endl;
            so << "ShardsDispatcherDescr: " << ServiceShards->GetShardsDispatcher()->GetContext().ToString() << Endl;
        }
        {
            TPrintSection checkMessagesSection(so, "CheckMessage");
            so << "Disabled: " << CheckMessageSettings.GetDisabled() << Endl;
            so << "MaxSizeBytes : " << CheckMessageSettings.GetCheckMaxSizeBytes() << Endl;
            so << "Attributes : " << CheckMessageSettings.GetCheckAttributes() << Endl;
            so << "KeyPrefixType : " << CheckMessageSettings.GetKeyPrefixCheckType() << Endl;
        }
        {
            TPrintSection pqLibSettingsSection(so, "PQLibSettings");
            so << "ThreadsCount : " << PQLibSettings.ThreadsCount << Endl;
            so << "CompressionPoolThreads : " << PQLibSettings.CompressionPoolThreads << Endl;
            so << "GRpcThreads : " << PQLibSettings.GRpcThreads << Endl;
            so << "ChannelCreationTimeout : " << PQLibSettings.ChannelCreationTimeout << Endl;
        }
        if (TelemetryConfig) {
            TPrintSection telemetrySection(so, "Telemetry");
            TelemetryConfig->Print(so);
        }
        if (TelemetryInterval) {
            so << "TelemetryInterval: " << TelemetryInterval.value() << Endl;
        }
    }
    return so.Str();
}


NJson::TJsonValue TPersQueueWriterSettings::GetJsonConfig() const {
    const TString configText = GetStringConfig();
    NJson::TJsonValue result;
    TUnstrictConfig::ToJson(configText, result);
    return result;
}

}
