#pragma once

#include <kernel/groupattrs/docsattrs.h>

//todo: not this
#include <saas/rtyserver/search/processors/processor.h>

enum class ERtyAggregationType {
    AGGR_MIN /* "min" */ ,
    AGGR_MAX /* "max" */ ,
    AGGR_UNIQUE /* "unique" */,
    AGGR_SUM /* "sum" */
};

class ILowAggregator {
public:
    virtual void AddValue(const TCateg& /*value*/) = 0;
    virtual TString GetResult(const TMap<TCateg,TString>& categ2ValueLow) const = 0;
    virtual ~ILowAggregator() = default;
};

class TMinAggregator : public ILowAggregator {
    public:
        TMinAggregator(const TCateg& firstResult) { Result = firstResult; }
        virtual void AddValue(const TCateg& value) override {
            if (Result > value) { Result = value; }
        }
        virtual TString GetResult(const TMap<TCateg,TString>& /*categ2ValueLow*/) const override {
            return ToString(Result);
        }
    private:
        TCateg Result;
};

class TMaxAggregator : public ILowAggregator {
    public:
        TMaxAggregator(const TCateg& firstResult) { Result = firstResult; }
        virtual void AddValue(const TCateg& value) override {
            if (Result < value) { Result = value; }
        }
        virtual TString GetResult(const TMap<TCateg,TString>& /*categ2ValueLow*/) const override {
            return ToString(Result);
        }
    private:
        TCateg Result;
};

class TUniqueAggregator : public ILowAggregator {
    public:
        TUniqueAggregator(const TCateg& firstResult) { Results.insert(firstResult); }
        virtual void AddValue(const TCateg& value) override {
            Results.insert(value);
        }
        virtual TString GetResult(const TMap<TCateg,TString>& categ2ValueLow) const override {
            TSet<TString> ResultsStr;
            for (const auto& res : Results) {
                ResultsStr.insert(categ2ValueLow.find(res) != categ2ValueLow.end()
                    ? categ2ValueLow.find(res)->second : ToString(res));
            }
            return JoinStrings(ResultsStr.begin(), ResultsStr.end(), ",");
        }
    private:
        TSet<TCateg> Results;
};

class TSumAggregator : public ILowAggregator {
    public:
        TSumAggregator(const TCateg& firstResult) { Result = firstResult; }
        virtual void AddValue(const TCateg& value) override {
            Result += value;
        }
        virtual TString GetResult(const TMap<TCateg,TString>& /*categ2ValueLow*/) const override {
            return ToString(Result);
        }
    private:
        TCateg Result;
};

THolder<ILowAggregator> MakeLowAggregator(const ERtyAggregationType aggrType, const TCateg& firstResult);


class THighAggregator {
public:
    THighAggregator(const TString& highAttrName, const TString& lowAttrName, const ERtyAggregationType aggrType)
        : AttrName(highAttrName)
        , ChildAttrName(lowAttrName)
        {
            AggrType = aggrType;
        };

    void AddValue(TMap<TString,TVector<TCateg>>& attrs) {
        auto highValuesIt = attrs.find(AttrName);
        auto lowValuesIt= attrs.find(ChildAttrName);
        if (highValuesIt == attrs.end() || lowValuesIt == attrs.end()) {
            return;
        }
        for (TCateg& value : highValuesIt->second) {
            for (TCateg& lowValue : lowValuesIt->second) {
                auto it = Aggregators.find(value);
                if (it == Aggregators.end()) {
                    Aggregators.insert(std::make_pair(value, MakeLowAggregator(AggrType, lowValue)));
                } else {
                    it->second->AddValue(lowValue);
                }
            }
        }
    }

    TString GetResult(const TMap<TStringBuf,TMap<TCateg,TString>>& categ2Value) const {
        TString result = "";
        auto it=categ2Value.find(AttrName);
        auto itLow = categ2Value.find(ChildAttrName);
        if (it == categ2Value.end() || itLow == categ2Value.end()) {
            //todo: error?
            return "";
        }
        const TMap<TCateg,TString>& categ2ValueHigh = it->second;
        for (auto& aggr : Aggregators) {
            result += (categ2ValueHigh.find(aggr.first) != categ2ValueHigh.end()
                    ? categ2ValueHigh.find(aggr.first)->second
                    : ToString(aggr.first))
                + ":"
                + aggr.second->GetResult(itLow->second) + ";";
        }
        return  result;
    }

    TString GetAttrName() const {
        return AttrName;
    }

    TString GetChildAttrName() const {
        return ChildAttrName;
    }

    TString GetAggrTypeName() const {
        return ToString(AggrType);
    }

private:
    TString AttrName;
    ERtyAggregationType AggrType;
    TString ChildAttrName;
    TMap<TCateg, THolder<ILowAggregator>> Aggregators;
};
