#pragma once

#include "errors.h"

#include <library/cpp/coroutine/engine/impl.h>
#include <util/generic/scope.h>
#include <util/generic/utility.h>
#include <util/stream/buffer.h>
#include <util/stream/file.h>
#include <util/string/builder.h>
#include <util/system/fs.h>
#include <util/system/tempfile.h>


namespace NSrvKernel {
    [[nodiscard]]
    inline TError CheckedSleepT(TCont* cont, TDuration timeout) noexcept {
        Y_REQUIRE(cont->SleepT(timeout) != ECANCELED,
                  TCoroutineCanceledException{});
        return {};
    }

    [[nodiscard]]
    inline TError CheckedSleepD(TCont* cont, TInstant deadline) noexcept {
        Y_REQUIRE(cont->SleepD(deadline) != ECANCELED,
                  TCoroutineCanceledException{});
        return {};
    }

    template <typename TMessage>
    inline void WriteProtobufAtomically(const TString& directory, const TString& filename, const TMessage& message) {
        NFs::MakeDirectoryRecursive(directory);

        TString tmpFile = MakeTempName(directory.Data(), filename.Data());

        Y_DEFER {
            NFs::Remove(tmpFile);
        };

        {
            TBuffer buf;
            buf.Reserve(65535);
            {
                TBufferOutput bufOut{buf};
                message.Save(&bufOut);
            }
            TFileOutput fileOutput{ tmpFile };
            fileOutput.Write(buf.Data(), buf.size());
            fileOutput.Flush();
        }

        NFs::Rename(tmpFile, TStringBuilder{} << directory << "/" << filename);
    }

    template <typename F>
    inline TError MeasureProcessingTime(F&& f, TDuration* out) {
        *out = TDuration::Max();
        TInstant start = TInstant::Now();
        Y_DEFER {
            *out = TInstant::Now() - start;
        };
        return f();
    }
}
