#pragma once

#include <saas/util/ptr.h>
#include <util/thread/pool.h>
#include <util/datetime/base.h>
#include <util/generic/ptr.h>
#include <util/generic/singleton.h>
#include <util/generic/maybe.h>

namespace NUtil {
    /**
     * @brief An implementation of IThreadPool based on an adaptive (dynamically sized) thread pool
     * @note https://wiki.yandex-team.ru/users/yrum/SmartQueue/
     */
    class TSmartMtpQueueNg: public IThreadPool, public TThreadFactoryHolder {
        class TImpl;

    public:
        /**
         * @brief Minimal number of threads and High-Low balancing options
         */
        struct TOptions {
            size_t MinThreads = 1;
            size_t StepThreads = 1;
            int    MaxThreadsAdd = 0;
            float  MaxThreadsMult = 1.0f;
            TMaybe<size_t> MaxThreadsLimit;

            float  JobsPerWorker = 1.0f;
            float  BurstJobsPerWorker = 3.0f;

            TString ThreadName;

        public:
            TOptions() {
            }

            TOptions(int threadCount)
                : MinThreads(threadCount)
            {
            }

            inline bool IsFixed() const {
                // checks MaxThreadsAdd == 0 && MaxThreadsMult == 1.0f
                return MaxThreadsAdd == 0 && 256 == size_t(MaxThreadsMult * 256);
            }

            inline size_t MaxThreads() const {
                return CalcMaxThreads(MinThreads);
            }

            inline constexpr size_t CalcMaxThreads(size_t minThreads) const {
                int maxThreads = Max(MaxThreadsAdd + int(MaxThreadsMult * minThreads), (int)minThreads);
                return (size_t)maxThreads;
            }
        };

    public:
        /**
         * @brief Optional callback for profiling
         */
        class IStatHandler : public TAtomicRefCount<IStatHandler> {
        public:
            struct TStats {
                size_t InQueue;
                size_t InFly;
                size_t TargetInFly;
                size_t NumThreads;
                size_t NumAdviced;
                size_t NumBusyThreads;
                size_t AdvicePeriod;
                ui32 nOperations;
            };
        public:
            virtual ~IStatHandler() {}
            virtual void OnStats(const TStats& data) = 0;
        };
        using TStatHandlerRef = TIntrusivePtr<IStatHandler>;

        /**
         * @brief Optional callback for live manual tuning (mostly for SRE / debug)
         */
        class IUpdatable {
        public:
            virtual ~IUpdatable() {};
            virtual bool ResetOptions(const TOptions& options) = 0;
        };

        using TUpdatableRef = THandle<IUpdatable>;

    private:
        class TUpdateHandle;

    public:
        /**
         * @brief Create Smart MTP Queue with the default thread Pool
         *
         * @param options Minimal number of threads and High-Low balancing options
         */
        explicit TSmartMtpQueueNg(const TOptions& options = Default<TOptions>());

        /**
         * @brief Create Smart MTP Queue with specified thread pool
         *
         * @param options Minimal number of threads and High-Low balancing options
         * @param pool thread pool object (not owned)
         */
        explicit TSmartMtpQueueNg(IThreadFactory* pool, const TOptions& options = Default<TOptions>());

        /**
         * @brief Destroy queue
         *
         * @details Destructor synchronously waits for all the objects being processed
         */
        virtual ~TSmartMtpQueueNg() override;

        /**
         * @brief Add new object to queue
         *
         * @note  Obj is not deleted after execution
         * @param obj object to be processed
         * @return bool @b true success @b false object rejected will not be processed
         */
        virtual bool Add(IObjectInQueue* obj) override;

        /**
         * @brief Queue size
         * @return size_t
         */
        virtual size_t Size() const noexcept override;

        /**
         * @brief Start queue processing
         *
         * @param maxThreadCount Maximum thread count
         * @param queueSizeLimit Queue size limit
         * @return void
         */
        virtual void Start(size_t maxThreadCount, size_t queueSizeLimit) override;
        /**
         * @brief Stop queue processing
         *
         * @return void
         */
        virtual void Stop() noexcept override;

        size_t ThreadCount();

        void SetStatHandler(TStatHandlerRef& collector);

        /**
         *  @brief Interface to change options on a live queue
         *
         *  @note  Not all options can be changed this way (see the code for details)
         *  @return true on success
         */
        TUpdatableRef GetUpdatable();

    private:
        THolder<TImpl> Impl;
        TOptions Options;
        THandleOwner<IUpdatable> Updatable;
    };
}
