/*
// Copyright (c) 2018 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#pragma once

#include "privileges.hpp"
#include "token_authorization_middleware.hpp"
#include "webserver_common.hpp"

#include <error_messages.hpp>
#include <vector>

#include "http_request.h"
#include "http_response.h"

namespace redfish
{

/**
 * AsyncResp
 * Gathers data needed for response processing after async calls are done
 */
class AsyncResp
{
  public:
    AsyncResp(crow::Response& response) : res(response)
    {
    }

    ~AsyncResp()
    {
        res.end();
    }

    crow::Response& res;
};

/**
 * @brief  Abstract class used for implementing Redfish nodes.
 *
 */
class Node
{
  public:
    template <typename... Params>
    Node(CrowApp& app, std::string&& entityUrl, Params... paramsIn)
    {
        crow::DynamicRule& get = app.routeDynamic(entityUrl.c_str());
        getRule = &get;
        get.methods("GET"_method)([this](const crow::Request& req,
                                         crow::Response& res,
                                         Params... params) {
            std::vector<std::string> paramVec = {params...};
            doGet(res, req, paramVec);
        });

        crow::DynamicRule& patch = app.routeDynamic(entityUrl.c_str());
        patchRule = &patch;
        patch.methods("PATCH"_method)([this](const crow::Request& req,
                                             crow::Response& res,
                                             Params... params) {
            std::vector<std::string> paramVec = {params...};
            doPatch(res, req, paramVec);
        });

        crow::DynamicRule& post = app.routeDynamic(entityUrl.c_str());
        postRule = &post;
        post.methods("POST"_method)([this](const crow::Request& req,
                                           crow::Response& res,
                                           Params... params) {
            std::vector<std::string> paramVec = {params...};
            doPost(res, req, paramVec);
        });

        crow::DynamicRule& delete_ = app.routeDynamic(entityUrl.c_str());
        deleteRule = &delete_;
        delete_.methods("DELETE"_method)([this](const crow::Request& req,
                                                crow::Response& res,
                                                Params... params) {
            std::vector<std::string> paramVec = {params...};
            doDelete(res, req, paramVec);
        });
    }

    void initPrivileges()
    {
        auto it = entityPrivileges.find(boost::beast::http::verb::get);
        if (it != entityPrivileges.end())
        {
            if (getRule != nullptr)
            {
                getRule->requires(it->second);
            }
        }
        it = entityPrivileges.find(boost::beast::http::verb::post);
        if (it != entityPrivileges.end())
        {
            if (postRule != nullptr)
            {
                postRule->requires(it->second);
            }
        }
        it = entityPrivileges.find(boost::beast::http::verb::patch);
        if (it != entityPrivileges.end())
        {
            if (patchRule != nullptr)
            {
                patchRule->requires(it->second);
            }
        }
        it = entityPrivileges.find(boost::beast::http::verb::delete_);
        if (it != entityPrivileges.end())
        {
            if (deleteRule != nullptr)
            {
                deleteRule->requires(it->second);
            }
        }
    }

    virtual ~Node() = default;

    OperationMap entityPrivileges;

    crow::DynamicRule* getRule = nullptr;
    crow::DynamicRule* postRule = nullptr;
    crow::DynamicRule* patchRule = nullptr;
    crow::DynamicRule* deleteRule = nullptr;

  protected:
    // Node is designed to be an abstract class, so doGet is pure virtual
    virtual void doGet(crow::Response& res, const crow::Request& req,
                       const std::vector<std::string>& params)
    {
        res.result(boost::beast::http::status::method_not_allowed);
        res.end();
    }

    virtual void doPatch(crow::Response& res, const crow::Request& req,
                         const std::vector<std::string>& params)
    {
        res.result(boost::beast::http::status::method_not_allowed);
        res.end();
    }

    virtual void doPost(crow::Response& res, const crow::Request& req,
                        const std::vector<std::string>& params)
    {
        res.result(boost::beast::http::status::method_not_allowed);
        res.end();
    }

    virtual void doDelete(crow::Response& res, const crow::Request& req,
                          const std::vector<std::string>& params)
    {
        res.result(boost::beast::http::status::method_not_allowed);
        res.end();
    }
};

} // namespace redfish
