#include "json_ref_adapter.h"

#include <library/cpp/json/json_value.h>
#include <library/cpp/json/json_reader.h>

#include <dict/web/multipart.h>
#include <dict/web/utils.h>

#include <util/string/vector.h>
#include <util/string/split.h>

using namespace NJson;

const TString NIndexerProxy::TJsonWithRefsAdapter::AdapterAlias("json_ref");

namespace NIndexerProxy {

    class TJsonWithRefsCallbacks: public TParserCallbacks {
    public:
        TJsonWithRefsCallbacks(TJsonValue &value, THashMap<TString, TString> &parts)
            : TParserCallbacks(value)
            , Parts(parts)
        {
        }

        bool OnString(const TStringBuf &val) override {
            THashMap<TString, TString>::iterator value;
            if ((value = Parts.find(TString(val))) == Parts.end())
                return SetValue(val);
            else
                return SetValue(value->second);
        }

        void OnError(size_t off, TStringBuf reason) override {
            Error = "JSON error on offset " + ToString(off) + " (" + reason + ")";
        }

        const TString& GetError() const noexcept {
            return Error;
        }

    protected:
        THashMap<TString, TString> &Parts;
        TString Error;
    };

    bool TJsonWithRefsAdapter::ParseMessage(ISenderTask& context) const {
        try {
            TContentTypeHeader parsedContentType;
            if (const TString* contentTypeStringPtr = context.GetContext().GetRD().HeaderIn("Content-Type")) {
                ParseContentType(*contentTypeStringPtr, &parsedContentType);
            }

            THashMap<TString, TString> parts;
            TString json;
            TString ss(context.GetContext().GetPost().AsCharPtr(), context.GetContext().GetPost().Length());
            if (parsedContentType.Boundary != "") {
                TMultipartReader mpr(ss, parsedContentType.Boundary);
                while (mpr.ReadNext()) {
                    if (mpr.GetName() == "json_message")
                        json = mpr.GetValue();
                    else
                        parts[mpr.GetName()] = mpr.GetValue();
                }
            }
            TMemoryInput inStream(json.data(), json.size());
            IncomingMessage = JSON_MAP;
            TJsonWithRefsCallbacks cb(IncomingMessage, parts);
            if (!ReadJson(&inStream, false, &cb))
                RETURN_ERROR("Errors in Json: " + cb.GetError());
            return true;
        } catch (...) {
            RETURN_ERROR("Error while parse Json: " + CurrentExceptionMessage());
        };
        return false;
    }
}

TAdaptersFactory::TRegistrator<NIndexerProxy::TJsonWithRefsAdapter> NIndexerProxy::TJsonWithRefsAdapter::Registrator(NIndexerProxy::TJsonWithRefsAdapter::AdapterAlias);
