#pragma once

#include <saas/deploy_manager/scripts/common/script.h>
#include <saas/deploy_manager/scripts/cluster/cluster_task.h>
#include <saas/deploy_manager/server/client/client.h>
#include <saas/util/types/parameter.h>

#include <util/string/builder.h>

template <
    class Product,
    class Action = NRTYDeploy::TClusterAsyncAction,
    class Script = NRTYDeploy::IScript
         >
class TGenericScriptOnlyCommand
    : public Action
    , public Script
    , public NUtil::TJsonParser<TGenericScriptOnlyCommand<Product, Action, Script>>
{
public:
    struct TRegistrator {
        TRegistrator()
            : Name(Product::GetNameStatic())
            , Script_(Name)
            , Action_(Name)
        {}

        const TString Name;
        NRTYDeploy::IScript::TFactory::TRegistrator<Product> Script_;
        NDaemonController::TAction::TFactory::TRegistrator<Product> Action_;
    };

    typedef TGenericScriptOnlyCommand<Product, Action, Script> TBase;

protected:
    TGenericScriptOnlyCommand() {}

    TGenericScriptOnlyCommand(NDaemonController::TAsyncPolicy policy)
        : Action(policy)
    {
    }

public:
    // Action
    virtual NJson::TJsonValue DoSerializeToJson() const {
        return NUtil::TJsonParser<TGenericScriptOnlyCommand<Product, Action, Script>>::SerializeArguments();
    }
    virtual void DoDeserializeFromJson(const NJson::TJsonValue& json) {
        NUtil::TJsonParser<TGenericScriptOnlyCommand<Product, Action, Script>>::DeserializeArguments(json);
    }
    virtual TString GetCustomUriStart() const {
        return ActionName();
    }
    virtual TString DoBuildCommandStart() const {
        TStringBuilder builder;
        for (auto&& p : NUtil::TJsonParser<TGenericScriptOnlyCommand<Product, Action, Script>>::Parameters) {
            builder << "&" << p.first << "=" << p.second->ToString();
        }
        return builder;
    }
    virtual TString Help() {
        TVector<TString> result;
        for (auto&& p : NUtil::TJsonParser<TGenericScriptOnlyCommand<Product, Action, Script>>::Parameters) {
            result.push_back(p.first);
        }
        return JoinStrings(result, ", ");
    }
    virtual TString ActionName() const {
        return Product::GetNameStatic();
    }

protected:
    void ParseCgi(const TCgiParameters &cgi) {
        for (auto&& p : NUtil::TJsonParser<TGenericScriptOnlyCommand<Product, Action, Script>>::Parameters) {
            if (!cgi.Has(p.first))
                continue;

            p.second->Parse(cgi.Get(p.first));
        }
    }
};

template <
    class Product,
    class Action = NRTYDeploy::TClusterAsyncAction,
    class Script = NRTYDeploy::IScript,
    class Task = NRTYDeploy::TClusterTask
         >
class TGenericCommand: public TGenericScriptOnlyCommand<Product, Action, Script> {
protected:
    typedef TGenericCommand<Product, Action, Script, Task> TBase;

    class TTask: public Task {
    public:
        TTask(const TBase& owner,
              const NRTYDeploy::TClusterTask::TCgiContext& context,
              NRTYDeploy::ICommonData* commonData,
              const TString& taskType)
            : Task(context, commonData, taskType)
            , Owner(owner)
        {}

        virtual void DoBuildTask() {
            Owner.BuildScript(Task::GetScript(), Task::CommonData);
        }

    private:
        const TBase& Owner;
    };
protected:
    TGenericCommand() {}

    TGenericCommand(NDaemonController::TAsyncPolicy policy)
        : TBase(policy)
    {
    }

public:
    virtual bool Process(IDeployInfoRequest& request) {
        const auto& cgi = request.GetRD().CgiParam;
        TBase::ParseCgi(cgi);

        try {
            NRTYDeploy::TClusterTask::TCgiContext context = NRTYDeploy::TClusterTask::TCgiContext::Parse(cgi);
            TTask task(*this, context, &request, TBase::ActionName());
            task.BuildTask();
            if (task.GetCurrentStatus() == NRTYDeploy::TClusterTask::ctsEnqueued) {
                request.Output() << "HTTP/1.1 200 \r\n\r\n";
                request.Output() << task.GetId() << Endl;
            } else if (task.GetResultStatus() == NRTYDeploy::TClusterTask::rsDuplication) {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "duplication" << Endl;
            } else {
                request.Output() << "HTTP/1.1 500 \r\n\r\n";
                request.Output() << "Fail code: " << RSToString(task.GetResultStatus()) << ". Message: " << task.GetStatusInfo();
            }
            return true;
        } catch (...) {
            request.Output() << "HTTP/1.1 500 \r\n\r\n";
            request.Output() << "Build and enqueue failed: " << CurrentExceptionMessage();
            return false;
        }
    }
protected:
    virtual void BuildScript(NRTYScript::TRTYClusterScript::TPtr root, NRTYDeploy::ICommonData* commonData) const = 0;
};
