#include "slots_pool.h"
#include "searchmap.h"
#include "parsers/json/json.h"

#include <saas/util/cluster/cluster.h>
#include <saas/util/json/json.h>
#include <library/cpp/logger/global/global.h>

namespace NSearchMapParser {

    class TSlotFinder {
    public:
        TSlotFinder(const TString& slot) {
            NRTYCluster::TSlotData sd;
            if (!sd.Parse(slot, sd))
                ythrow yexception() << "invalid slot " << slot;
            Slot = sd.ShortSlotName();
        }

        bool operator()(const NSearchMapParser::TSearchMapHost& o) const {
            return o.GetShortSlotName() == Slot;
        }
    protected:
        TString Slot;
    };

    TVector<TSearchMapHost> TSlotsPool::GetSlots() const {
        return Hosts;
    }

    TVector<TSearchMapHost> TSlotsPool::GetEndPointSets() const {
        return EndPointSets;
    }

    TVector<TSearchMapHost>& TSlotsPool::GetMutableSlots() {
        return Hosts;
    }

    TVector<TSearchMapHost>& TSlotsPool::GetMutableEndPointSets() {
        return EndPointSets;
    }

    bool TSlotsPool::EndpointSetExists(const TString& eSetName) const {
        const TVector<TSearchMapHost>& eSets = GetEndPointSets();
        TVector<TSearchMapHost>::const_iterator eSet = FindIf(eSets.begin(), eSets.end(), TSlotFinder(eSetName));
        return eSet != eSets.end();
    }

    ui32 ISlotsPool::Size() const {
        return GetSlots().size();
    }

    bool ISlotsPool::GetHostBySlot(const TString& name, TSearchMapHost& result) const {
        const TVector<TSearchMapHost>& hosts = GetSlots();
        TVector<TSearchMapHost>::const_iterator host = FindIf(hosts.begin(), hosts.end(), TSlotFinder(name));
        if (host == hosts.end())
            return false;
        result = *host;
        return true;
    }

    TSet<TString> ISlotsPool::GetSlotDescriptionSet() const {
        const TVector<TSearchMapHost>& slots = GetSlots();
        TSet<TString> result;
        for (TVector<TSearchMapHost>::const_iterator i = slots.begin(); i != slots.end(); ++i)
            result.insert(i->GetSlotName());
        VERIFY_WITH_LOG(slots.size() == result.size(), "slot dubles detected");
        return result;
    }

    bool ISlotsPool::SlotExists(const TString& slotName) const {
        const TVector<TSearchMapHost>& hosts = GetSlots();
        TVector<TSearchMapHost>::const_iterator host = FindIf(hosts.begin(), hosts.end(), TSlotFinder(slotName));
        return host != hosts.end();
    }

    bool TSlotsPool::Add(const TSearchMapHost& host) {
        if (FindIf(Hosts.begin(), Hosts.end(), TSlotFinder(host.GetSlotName())) != Hosts.end())
            return false;
        if (FindIf(EndPointSets.begin(), EndPointSets.end(), TSlotFinder(host.GetSlotName())) != EndPointSets.end())
            return false;

        if (host.IsSd) {
            EndPointSets.push_back(host);
        } else {
            Hosts.push_back(host);
        }
        return true;
    }

    void TSlotsPool::Parse(const TVector<TString>& slots) {
        for (ui32 i = 0; i < slots.size(); ++i) {
            TSearchMapHost smh;
            NRTYCluster::TSlotData sd;
            if (NRTYCluster::TSlotData::Parse(slots[i], sd)) {
                smh.Name = sd.FullHost();
                smh.SearchPort = sd.Port;
                Add(smh);
            } else {
                ythrow yexception() << "Incorrect slot data: " << slots[i];
            }
        }
    }

    NJson::TJsonValue TSlotsPool::Serialize() const {
        NJson::TJsonValue arr(NJson::JSON_ARRAY);
        for (ui32 i = 0; i < Hosts.size(); ++i) {
            arr.AppendValue(Hosts[i].Serialize());
        }
        for (ui32 i = 0; i < EndPointSets.size(); ++i) {
            arr.AppendValue(EndPointSets[i].Serialize());
        }
        return arr;
    }

    void TSlotsPool::Clear() {
        Hosts.clear();
    }

    bool TSlotsPool::DeserializeFromString(const TString& data) {
        NJson::TJsonValue jsonValue;
        if (!NUtil::JsonFromString(data, jsonValue))
            return false;
        return DeserializeFromJson(jsonValue);
    }

    bool TSlotsPool::DeserializeFromJson(const NJson::TJsonValue& data) {
        try {
            TParsingSettings parsingSettings = { false, Max<TShardIndex>(), {} };
            TVector<TSearchMapHost> hosts = TSearchMapReplicaParser("", data, parsingSettings).Get().Hosts;
            Clear();
            EndPointSets.clear();
            for (ui32 i=0; i<hosts.size(); ++i) {
                if (hosts[i].IsSd) {
                    EndPointSets.push_back(hosts[i]);
                } else {
                    Hosts.push_back(hosts[i]);
                }
            }
            return true;
        } catch (const yexception& e) {
            Cerr << "cannot deserialize slots pool: " << e.what() << Endl;
            return false;
        }
    }

    NSaasProto::TSlotsPool TSlotsPool::SerializeToProto() const {
        NSaasProto::TSlotsPool result;
        for (auto& i : Hosts) {
            *result.AddSlots() = i.SerializeToProto();
        }
        for (auto& i : EndPointSets) {
            *result.AddEndPointSets() = i.SerializeToProto();
        }
        return result;
    }

    bool TSlotsPool::Deserialize(const NSaasProto::TSlotsPool& pool) {
        for (ui32 slot = 0; slot < pool.SlotsSize(); ++slot) {
            TSearchMapHost smh;
            if (!smh.Deserialize(pool.GetSlots(slot)))
                return false;
            Hosts.push_back(smh);
        }
        for (ui32 i = 0; i < pool.EndPointSetsSize(); ++i) {
            TSearchMapHost smh;
            if (!smh.Deserialize(pool.GetEndPointSets(i)))
                return false;
            EndPointSets.push_back(smh);
        }
        return true;
    }

}
