#include "common.h"
#include "configuration.h"
#include "response.h"
#include "yacare_params.h"

#include <maps/wikimap/mapspro/services/mrc/drive/firmware_updater/libs/db/include/branch_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/drive/firmware_updater/libs/db/include/hardware_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/drive/firmware_updater/libs/db/include/device_branch_gateway.h>

#include <maps/infra/yacare/include/yacare.h>
#include <maps/infra/yacare/include/params/tvm.h>

namespace maps::fw_updater::storage {

namespace {

using Tbl = db::table::DeviceBranch;

void checkHardware(pqxx::transaction_base& txn,
                   const auth::UserInfo& userInfo,
                   const std::string& hardware)
{
    auto dbHardware = db::HardwareGateway{txn}.tryLoadById(hardware);
    if (!dbHardware) {
        throw yacare::errors::BadRequest() << "Unknown hardware: " << hardware;
    }
    checkAcl(txn, userInfo, *dbHardware);
}

} // namespace


YCR_RESPOND_TO("POST /v2/branch/add_device", userInfo, hardware, deviceid, branch)
{
    auto txn = configuration()->pool().masterWriteableTransaction();
    checkHardware(*txn, userInfo, hardware);

    if (!db::BranchGateway{*txn}.existsById(branch)) {
        throw yacare::errors::BadRequest() << "Unknown branch: " << branch;
    }

    db::DeviceBranch deviceBranch(hardware, deviceid, branch);
    db::DeviceBranchGateway{*txn}.upsert(deviceBranch);
    txn->commit();

    makeDeviceBranchResponse(response, deviceBranch);
}

YCR_RESPOND_TO("GET /v2/branch/list_devices", userInfo,
               hardware, deviceid = "", branch = "", results = 0, skip = 0)
{
    auto txn = configuration()->pool().slaveTransaction();

    checkHardware(*txn, userInfo, hardware);

    sql_chemistry::FiltersCollection filter(sql_chemistry::op::Logical::And);
    filter.add(Tbl::hardwareId == hardware);
    if (has(deviceid)) {
        filter.add(Tbl::deviceId == deviceid);
    }
    if (has(branch)) {
        filter.add(Tbl::branch == branch);
    }

    auto orderBy = sql_chemistry::orderBy(Tbl::hardwareId)
        .orderBy(Tbl::branch)
        .orderBy(Tbl::deviceId);
    if (results) {
        orderBy.limit(results);
    }
    if (skip) {
        orderBy.offset(skip);
    }

    auto deviceBranches = db::DeviceBranchGateway{*txn}.load(filter, orderBy);

    if (deviceBranches.empty()) {
        response.setStatus(yacare::HTTPStatus::NoContent);
        return;
    }

    makeDeviceBranchesListResponse(response, deviceBranches);

}

YCR_RESPOND_TO("DELETE /v2/branch/remove_device", userInfo, hardware, deviceid)
{
    auto txn = configuration()->pool().masterWriteableTransaction();
    checkHardware(*txn, userInfo, hardware);

    db::DeviceBranchGateway{*txn}.remove(
        Tbl::hardwareId == hardware && Tbl::deviceId == deviceid);

    txn->commit();
}

} //namespace maps::fw_updater::storage
