#pragma once

#include "shared_file_exists_checker.h"
#include "shared_file_rereader.h"
#include "shared_loader.h"

#include <library/cpp/coroutine/engine/impl.h>
#include <library/cpp/json/json_writer.h>
#include <library/cpp/threading/task_scheduler/task_scheduler.h>

#include <util/generic/hash.h>
#include <util/generic/ptr.h>

namespace NSrvKernel {
    class TSharedFiles : public TNonCopyable {
    public:
        void Start() {
            TaskScheduler_.Start();
        }

        auto FileChecker(const TString& path, TDuration interval) noexcept {
            return TSharedFileExistsChecker(GetOrAddTask(CheckerTasks_, path, interval));
        }

        auto FileReReader(const TString& path, TDuration interval) noexcept {
            return TSharedFileReReader(GetOrAddTask(ReaderTasks_, path, interval));
        }

        void DumpFilesInfo(NJson::TJsonWriter& out, const TSharedFiles* masterSharedFiles = nullptr) const {
            TGuard<TMutex> lock(Mutex_);

            out.OpenMap();
            out.OpenMap("file-readers");
            if (masterSharedFiles) {
                masterSharedFiles->DumpFileReReaders(out);
            }
            DumpFileReReaders(out);
            out.CloseMap();
            out.OpenMap("file-checkers");
            if (masterSharedFiles) {
                masterSharedFiles->DumpFileCheckers(out);
            }
            DumpFileCheckers(out);
            out.CloseMap();
            out.CloseMap();
        }

        void Clear() {
            CheckerTasks_.clear();
            ReaderTasks_.clear();
        }

    private:
        void DumpFileReReaders(NJson::TJsonWriter& out) const noexcept {
            for (const auto& task : ReaderTasks_) {
                out.Write(task.first, task.second->GetData()->Id());
            }
        }

        void DumpFileCheckers(NJson::TJsonWriter& out) const noexcept {
            for (const auto& task : CheckerTasks_) {
                out.Write(task.first, task.second->Exists());
            }
        }

        template <typename TTask>
        TIntrusivePtr<TTask> GetOrAddTask(
            THashMap<TString, TIntrusivePtr<TTask>>& tasks, const TString& path, TDuration interval)
        {
            TGuard<TMutex> lock(Mutex_);

            auto it = tasks.find(path);
            if (it != tasks.end()) {
                return it->second;
            } else {
                auto task = MakeIntrusive<TTask>(path);
                task->Process();
                Y_VERIFY(TaskScheduler_.Add(task, interval));
                tasks.emplace(path, task);
                return task;
            }
        }

    private:
        THashMap<TString, TIntrusivePtr<TCheckerTask>> CheckerTasks_;
        THashMap<TString, TIntrusivePtr<TReReadTask>> ReaderTasks_;
        TTaskScheduler TaskScheduler_;

        TMutex Mutex_;
    };
}
