#include "process_storage.h"
#include <saas/deploy_manager/server/client/client.h>
#include <saas/util/external/mail_service.h>
#include <saas/util/external/diff.h>
#include <library/cpp/json/json_writer.h>
#include <util/folder/pathsplit.h>
#include <util/folder/path.h>
#include <util/string/hex.h>
#include <util/string/type.h>
#include <util/generic/algorithm.h>

namespace NRTYDeploy {
    void TScriptProcessStorage::FillUsedResources(IUserAccessControl::TUsedResources& result, IDeployInfoRequest& request) {
        TString path = request.GetRD().CgiParam.Get("path");
        const TPathSplit& split = TFsPath(path).Fix().PathSplit();
        if (split.size() >=2 && split[0] == "configs")
            result.Services.insert(split[1].data());
        else
            result.Services.insert("all_services");
    }

    bool TScriptProcessStorage::Process(IDeployInfoRequest& request) {
        TString action = request.GetRD().CgiParam.Get("action");
        TString path = TFsPath(request.GetRD().CgiParam.Get("path")).Fix().GetPath();
        if (action == "make") {
            if (!request.GetStorage().SetValue(path, "new")) {
                request.Output() << "HTTP/1.1 500 \r\n\r\n";
                request.Output() << "Can't make path";
            } else {
                request.Output() << "HTTP/1.1 200 \r\n\r\n";
                request.Output() << path;
                SetAdditionalMessage("ACTION:\nMAKE\nPATH:\n" + path);
            }
            return true;
        }

        if (action == "version") {
            i64 version = -1;
            if (!request.GetStorage().GetVersion(path, version)) {
                request.Output() << "HTTP/1.1 500 \r\n\r\n";
                request.Output() << "Can't find path";
            } else {
                request.Output() << "HTTP/1.1 200 \r\n\r\n";
                request.Output() << version;
            }
            return true;
        }

        if (action == "rm") {
            if (!request.GetStorage().RemoveNode(path)) {
                request.Output() << "HTTP/1.1 500 \r\n\r\n";
                request.Output() << "Can't remove";
            } else {
                request.Output() << "HTTP/1.1 200 \r\n\r\n";
                request.Output() << "Removed node: " << path;
                SetAdditionalMessage("ACTION:\nRM\nPATH:\n" + path);
            }
            return true;
        }

        if (action == "get") {
            TVector<TString> nodes;
            i64 version = -1;
            try {
                if (!!request.GetRD().CgiParam.Get("version"))
                    version = FromString(request.GetRD().CgiParam.Get("version"));
            }
            catch (...) {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "incorrect version" << Endl;
                return true;
            }
            if (path == "/" || request.GetStorage().ExistsNode(path) || version != -1) {
                TString data;
                NJson::TJsonValue json(NJson::JSON_MAP);
                bool nodesFlag = request.GetStorage().GetNodes(path, nodes, true);
                bool valueFlag = request.GetStorage().GetValue(path, data, version);
                if (valueFlag) {
                    if (request.GetRD().CgiParam.Get("hex") == "yes")
                        json.InsertValue("data", HexEncode(data));
                    else
                        json.InsertValue("data", data);
                }
                if (nodesFlag && nodes.size()) {
                    NJson::TJsonValue jsonNodes(NJson::JSON_ARRAY);
                    NJson::TJsonValue jsonData(NJson::JSON_ARRAY);

                    if (request.GetStorage().GetNodes(path, nodes, true)) {
                        Sort(nodes);
                        for (auto&& node : nodes) {
                            TVector<TString> nodesInternal;
                            TString data;
                            request.GetStorage().GetNodes(path + "/" + node, nodesInternal, true);
                            if (!nodesInternal.size()) {
                                jsonData.AppendValue(node);
                            } else {
                                jsonNodes.AppendValue(node);
                            }
                        }
                    }
                    json.InsertValue("dirs", jsonNodes);
                    json.InsertValue("files", jsonData);
                }
                if (!nodesFlag && !valueFlag) {
                    request.Output() << "HTTP/1.1 500 \r\n\r\n";
                    request.Output() << "Incorrect storage structure";
                    return false;
                } else {
                    if (IsTrue(request.GetRD().CgiParam.Get("download"))) {
                        request.Output() << "HTTP/1.1 200 \r\n";
                        request.Output() << "Content-Type: application/octet-stream \r\n\r\n";
                        request.Output() << data;
                    }
                    else {
                        TStringStream ss;
                        NJson::WriteJson(&ss, &json, true, true, false);
                        request.Output() << "HTTP/1.1 200 \r\n\r\n";
                        request.Output() << ss.Str();
                    }
                }
            } else {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "can't find node" << Endl;
            }
            return true;
        }
        if (action == "restore") {
            TVector<TString> nodes;
            i64 version = -1;
            try {
                version = FromString(request.GetRD().CgiParam.Get("version"));
                if (version < 0)
                    ythrow yexception() << "Incorrect version";
            }
            catch (...) {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "incorrect version" << Endl;
                return true;
            }
            TString data;
            if (request.GetStorage().GetValue(path, data, version)) {
                if (request.GetStorage().SetValue(path, data, true, false)) {
                    request.Output() << "HTTP/1.1 200 \r\n\r\n";
                    request.Output() << "restored node: " << path;
                    SetAdditionalMessage("PATH:\n" + path + "\n\nNEW_DATA:\n" + data);
                }
                else {
                    request.Output() << "HTTP/1.1 500 \r\n\r\n";
                    request.Output() << "Can't restore node: " << path;
                }
            }
            else {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "can't find this version" << Endl;
                return true;
            }
            return true;
        }
        if (action == "cmp") {
            TVector<TString> nodes;
            i64 version1 = -1;
            i64 version2 = -1;
            try {
                version1 = FromString(request.GetRD().CgiParam.Get("version1"));
            }
            catch (...) {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "incorrect version1" << Endl;
                return true;
            }
            try {
                version2 = FromString(request.GetRD().CgiParam.Get("version2"));
            }
            catch (...) {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "incorrect version2" << Endl;
                return true;
            }
            TString data1;
            TString data2;
            if (!request.GetStorage().GetValue(path, data1, version1)) {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "can't find this version1" << Endl;
                return true;
            }
            if (!request.GetStorage().GetValue(path, data2, version2)) {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "can't find this version2" << Endl;
                return true;
            }

            TString cmpResult = NRTY::TDiffBuilder::Calc(data1, data2);

            request.Output() << "HTTP/1.1 200 \r\n\r\n";
            request.Output() << cmpResult;

            return true;
        }
        request.Output() << "HTTP/1.1 400 \r\n\r\n";
        request.Output() << "incorrect action";
        return true;
    }

    IScript::TFactory::TRegistrator<TScriptProcessStorage> TScriptProcessStorage::Registrator("process_storage");
}
