#pragma once

#include <library/cpp/logger/global/global.h>
#include <library/cpp/json/json_reader.h>
#include <library/cpp/json/json_writer.h>
#include <library/cpp/json/json_value.h>
#include <util/generic/vector.h>
#include <util/system/types.h>
#include <util/system/mutex.h>
#include <util/stream/str.h>
#include <util/generic/map.h>
#include <util/generic/maybe.h>
#include <util/generic/ptr.h>

#include <saas/protos/rtyserver.pb.h>
#include <saas/library/behaviour/behaviour.h>

class TActionReply {
private:
    TMap<TString, TActionReply> Children;
    TMaybeFail<NRTYServer::TReply::TDispStatus> Status;
    TMaybeFail<ui32> HttpCode;
    TVector<TString> Statuses;
    TVector<TString> Messages;
    TAtomicSharedPtr<TMutex> Mutex;

public:

    TActionReply()
    {
        Mutex = new TMutex();
    }

    TActionReply(NRTYServer::TReply::TDispStatus defaultStatus)
    {
        Mutex = new TMutex();
        SetInfo(defaultStatus);
    }

    bool Defined() const {
        return Status.Defined();
    }

    NRTYServer::TReply::TDispStatus GetDispStatus() const {
        TGuard<TMutex> g(*Mutex);
        if (Status.Defined())
            return *Status;
        TMaybeFail<NRTYServer::TReply::TDispStatus> result;
        TMaybeFail<ui32> httpCode;
        for (auto&& i : Children) {
            NRTYServer::TReply::TDispStatus childStatus = i.second.GetDispStatus();
            const TDispStatusBehaviour& dsbC = GetBehaviour(childStatus);
            if (!httpCode.Defined() || *httpCode < dsbC.HttpCode) {
                httpCode = dsbC.HttpCode;
                result = childStatus;
            }
        }
        return *result;
    }

    ui32 GetHttpCode() const {
        TGuard<TMutex> g(*Mutex);
        if (HttpCode.Defined())
            return *HttpCode;
        const TDispStatusBehaviour& dsb = GetBehaviour(GetDispStatus());
        return dsb.HttpCode;
    }

    void SetInfo(NRTYServer::TReply::TDispStatus status, const TString& message) {
        TGuard<TMutex> g(*Mutex);
        SetInfo(status);
        Messages.push_back(message);
    }

    void SetInfo(ui32 httpCode, const TString& message) {
        TGuard<TMutex> g(*Mutex);
        SetInfo(httpCode);
        Messages.push_back(message);
    }

    void SetInfo(NRTYServer::TReply::TDispStatus status) {
        TGuard<TMutex> g(*Mutex);
        if (Status.Defined()) {
            const TDispStatusBehaviour& dsbF = GetBehaviour(*Status);
            const TDispStatusBehaviour& dsbT = GetBehaviour(status);
            CHECK_WITH_LOG(dsbT.HttpCode >= dsbF.HttpCode);
        }
        Statuses.push_back(NRTYServer::TReply::TDispStatus_Name(status));
        Status = status;
    }

    void SetInfo(ui32 httpCode) {
        TGuard<TMutex> g(*Mutex);
        CHECK_WITH_LOG(!HttpCode.Defined());
        HttpCode = httpCode;
    }

    void AddReply(const TString& key, const TActionReply& reply) {
        TGuard<TMutex> g(*Mutex);
        VERIFY_WITH_LOG(!Children.contains(key), "key %s duplication", key.data());
        Children[key] = reply;
    }

    NJson::TJsonValue GetReply() const {
        TGuard<TMutex> g(*Mutex);
        NJson::TJsonValue result(NJson::JSON_MAP);
        const TDispStatusBehaviour& dsb = GetBehaviour(*Status);
        result["code"] = dsb.HttpCode;
        result["d_status"] = NRTYServer::TReply::TDispStatus_Name(*Status);
        for (auto&& i : Messages) {
            result["messages"].AppendValue(i);
        }
        for (auto&& i : Statuses) {
            result["statuses"].AppendValue(i);
        }
        for (auto&& i : Children) {
            result.InsertValue(i.first, i.second.GetReply());
        }
        return result;
    }

};
