#include "detach_action.h"

#include <library/cpp/json/writer/json_value.h>
#include <util/stream/str.h>
#include <library/cpp/json/json_reader.h>
#include <library/cpp/logger/global/global.h>
#include <util/string/vector.h>
#include <saas/library/sharding/sharding.h>

namespace NDaemonController {

    namespace {
        template<class T>
        void SerializeArr(NJson::TJsonValue& json, const TVector<T>& vect) {
            NJson::TJsonValue result(NJson::JSON_ARRAY);
            for (ui32 i = 0; i < vect.size(); ++i) {
                result.AppendValue(vect[i]);
            }
            json = std::move(result);
        }

        bool DeserializeArr(const NJson::TJsonValue& json, TVector<NSearchMapParser::TShardIndex>& vect) {
            vect.clear();
            if (json.IsArray()) {
                NJson::TJsonValue::TArray arr = json.GetArray();
                vect.reserve(arr.size());
                for (ui32 i = 0; i < arr.size(); ++i) {
                    if (arr[i].IsUInteger()) {
                        vect.push_back(arr[i].GetUInteger());
                    }
                    else
                        return false;
                }
                return true;
            }
            return false;
        }
    }

    NJson::TJsonValue TDetachAction::DoSerializeToJson() const {
        NJson::TJsonValue result(NJson::JSON_MAP);
        NJson::TJsonValue jMin;
        NJson::TJsonValue jMax;
        SerializeArr<NSearchMapParser::TShardIndex>(jMin, ShardMin);
        SerializeArr<NSearchMapParser::TShardIndex>(jMax, ShardMax);
        result.InsertValue("shard_min", std::move(jMin));
        result.InsertValue("shard_max", std::move(jMax));
        if (!WaitActionName) {
            result.InsertValue("sharding", Sharding.ToString());
        }
        if (IdRes.size()) {
            result.InsertValue("id_ress", JoinStrings(IdRes, ","));
            CHECK_WITH_LOG(IdRes.size() == ShardMin.size());
            for (ui32 i = 0; i < ShardMin.size(); ++i) {
                result.InsertValue("id_res_" + ToString(ShardMin[i]) + "-" + ToString(ShardMax[i]), IdRes[i]);
            }
        }
        return result;
    }

    void TDetachAction::DoDeserializeFromJson(const NJson::TJsonValue& json) {
        NJson::TJsonValue::TMapType map;
        CHECK_WITH_LOG(json.GetMap(&map));
        ShardMin.clear();
        ShardMax.clear();
        if (!DeserializeArr(map["shard_min"], ShardMin) || !DeserializeArr(map["shard_max"], ShardMax))
            ythrow yexception() << "Incorrect detach action storage format";
        if (!WaitActionName) {
            Sharding = NSaas::TShardsDispatcher::TContext::FromString(map["sharding"].GetStringRobust());
        }
        if (map.find("id_ress") != map.end())
            IdRes = SplitString(map["id_ress"].GetStringRobust(), ",");
    }

    void TDetachAction::InterpretTaskReply(TAsyncTaskExecutor::TTask::TStatus taskStatus, const NJson::TJsonValue& result) {
        if (taskStatus == TAsyncTaskExecutor::TTask::stsFinished) {
            IdRes.clear();
            for (ui32 i = 0; i < ShardMin.size(); ++i) {
                NJson::TJsonValue::TMapType::const_iterator it = result.GetMap().find("id_res_" + ToString(ShardMin[i]) + "-" + ToString(ShardMax[i]));
                if (it != result.GetMap().end()) {
                    IdRes.push_back(it->second.GetStringRobust());
                }
            }
        } else if (taskStatus == TAsyncTaskExecutor::TTask::stsStarted) {
            TString hrInfo = result["stage"].GetStringRobust();
            if (result.Has("info")) {
                hrInfo += ": " + result["info"].GetStringRobust();
            }
            SetHRInfo(hrInfo);
        }
        TControllerAsyncAction::InterpretTaskReply(taskStatus, result);
    }

    TDetachAction::TFactory::TRegistrator<TDetachAction> TDetachAction::Registrator(SYNC_DETACH_ACTION_NAME);
}
