#include "socket_adapter_proto2json.h"
#include <saas/library/behaviour/behaviour.h>
#include <saas/api/clientapi.h>

#include <library/cpp/http/io/stream.h>

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

#include <library/cpp/logger/global/global.h>

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

TSocketAdapterProto2Json::TSocketAdapterProto2Json(const TString& service, IInputStream& input, IOutputStream& output, bool realtime)
: ISocketAdapter(input, output)
, Service(service)
, UsedToWrite(false)
, UsedToRead(false)
, Realtime(realtime)
{}

TString TSocketAdapterProto2Json::BuildJson(const NRTYServer::TMessage& message) const {
    NSaas::TAction action;
    action.ParseFromProtobuf(message);
    return action.BuildJsonMessage();
}

void TSocketAdapterProto2Json::WriteMessage(const ::google::protobuf::Message& message, const TString& request) const {
    Y_VERIFY(!UsedToWrite, "TSocketAdapterProto2Json: only one message to socket");
    UsedToWrite = true;
    const NRTYServer::TMessage& messageRTY = dynamic_cast<const NRTYServer::TMessage&>(message);
    MessageId = messageRTY.GetMessageId();
    TStringStream ss;
    ss << "POST /service/" << Service << "?ftests=yes" << (Realtime ? "" : "&realtime=no") << request << " HTTP/1.1\r\n";
    ss << "Content-Type: text/json\r\n";
    TString body = BuildJson(messageRTY);
    ss << "Content-Length: " << (body.size()) << "\r\n";
    ss << "\r\n" << body;
    Output << ss.Str();
    Output.Flush();
}

void TSocketAdapterProto2Json::WriteMessage(const TString& message, const TString& request) const {
    Y_VERIFY(!UsedToWrite, "TSocketAdapterProto2Json: only one message to socket");
    UsedToWrite = true;
    MessageId = 1;
    TStringStream ss;
    ss << "POST /service/" << Service << request << " HTTP/1.1\r\n";
    ss << "Content-Type: text/plain\r\n";
    ss << "Content-Length: " << (message.size()) << "\r\n";
    ss << "\r\n" << message;
    Output << ss.Str();
    Output.Flush();
}

bool TSocketAdapterProto2Json::ReadMessage(::google::protobuf::Message& message) const {
    Y_VERIFY(UsedToWrite && !UsedToRead, "TSocketAdapterProto2Json: only one message from socket and write before read");
    UsedToRead = true;
    NRTYServer::TReply& replyRTY = dynamic_cast<NRTYServer::TReply&>(message);
    THttpInput input(&Input);
    replyRTY.SetMessageId(MessageId);
    int httpCode = ParseHttpRetCode(input.FirstLine());
    bool wasRtyReply = false;
    TString msg;
    try {
        msg = input.ReadAll();
        NJson::TJsonValue reply;
        TStringInput si(msg);
        NJson::ReadJsonTree(&si, &reply);
        const NJson::TJsonValue& docReply = reply["dispatch"][0];
        bool wasDataAccepted = false;
        int rtyStatus = -1;
        NJson::TJsonValue rtyMsg(NJson::JSON_MAP);
        NJson::TJsonValue oneRtyMsg;
        bool needInResend = false;
        rtyMsg.InsertValue("http_code", httpCode);
        for (NJson::TJsonValue::TMapType::const_iterator i = docReply.GetMap().begin(); i != docReply.GetMap().end(); ++i) {
            if (i->second.IsMap()) {
                int rtyStatusLoc = FromString<int>(i->second["RTYStatus"].GetString());
                auto bh = GetBehaviour((NRTYServer::TReply::TRTYStatus)rtyStatusLoc);
                if (rtyStatusLoc == NRTYServer::TReply::DATA_ACCEPTED)
                    wasDataAccepted = true;
                TStringStream ss;
                ss << i->second["RTYMessage"].GetString();
                NJson::ReadJsonTree(&ss, &oneRtyMsg);
                rtyMsg.InsertValue(i->first, oneRtyMsg);
                if (!needInResend && bh.NeedInResend || (needInResend == bh.NeedInResend && rtyStatus < rtyStatusLoc)) {
                    rtyStatus = rtyStatusLoc;
                    needInResend = bh.NeedInResend;
                }
            }
        }
        if (rtyStatus != -1) {
            wasRtyReply = true;
            replyRTY.SetStatus(wasDataAccepted ? NRTYServer::TReply::DATA_ACCEPTED : rtyStatus);
            TStringStream ss;
            NJson::WriteJson(&ss, &rtyMsg);
            if (wasDataAccepted && reply.Has("async_code"))
                replyRTY.SetStatusMessage(reply["async_code"].GetString());
            else
                replyRTY.SetStatusMessage(ss.Str());
            DEBUG_LOG << "rtyStatus: " << rtyStatus << " rtyMessage: " << ss.Str() << "\n";
        }
    } catch (...) {
        wasRtyReply = false;
    }
    if (!wasRtyReply){
        replyRTY.SetStatus(DecodeHttpToDisp(httpCode));
        NJson::TJsonValue stat;
        stat["http_code"] = httpCode;
        stat["reply"] = msg;
        TStringStream ss;
        NJson::WriteJson(&ss, &stat);
        replyRTY.SetStatusMessage(ss.Str());
        DEBUG_LOG << "rtyStatus: " << replyRTY.GetStatus() << " rtyMessage: " << msg << "\n";
    }

    return true;
}
