#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/firmware_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/rollout_gateway.h>

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

namespace maps::fw_updater::storage {

YCR_RESPOND_TO("GET /v2/firmware/list", userInfo, hardware, slot = db::Slot::Rootfs, results = 0, skip = 0)
{
    auto txn = configuration()->pool().slaveTransaction();

    auto dbHardware = db::HardwareGateway{*txn}.tryLoadById(hardware);
    if (!dbHardware) {
        throw yacare::errors::BadRequest() << "Unknown hardware: " << hardware;
    }
    checkAcl(*txn, userInfo, *dbHardware);

    sql_chemistry::FiltersCollection filter(sql_chemistry::op::Logical::And);
    filter.add(db::table::Firmware::hardwareId == hardware);
    if (has(slot)) {
        filter.add(db::table::Firmware::slot == slot);
    }
    auto firmwares = db::FirmwareGateway{*txn}.load(filter,
        pageOrderBy(db::table::Firmware::id, results, skip, /*asc=*/false));

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

    makeFirmwaresListResponse(response, firmwares);
}

YCR_RESPOND_TO("DELETE /v2/firmware/delete", userInfo, hardware, slot, version)
{
    auto txn = configuration()->pool().masterWriteableTransaction();
    auto& s3Client = configuration()->s3Client();

    auto dbHardware = db::HardwareGateway{*txn}.loadById(hardware);
    checkAcl(*txn, userInfo, dbHardware);

    db::FirmwareGateway gtw{*txn};
    auto firmware = gtw.tryLoadOne(
        db::table::Firmware::hardwareId == hardware &&
        db::table::Firmware::slot == slot &&
        db::table::Firmware::version == version);

    if (!firmware) {
        throw yacare::errors::NotFound() << "Firmware does not exist";
    }
    if (db::RolloutGateway{*txn}.exists(db::table::Rollout::firmwareId == firmware->id())) {
        throw yacare::errors::Conflict() << "Rollout of this firmware exists";
    }

    auto urlPath = http::URL(firmware->url()).path();
    REQUIRE(urlPath.size() > 1, "Invalid S3 key in url " << firmware->url());
    // remove leading slash
    auto key = urlPath.substr(1);

    try {
        s3Client.deleteObject(key);
    } catch(const std::exception& e) {
        WARN() << "Failed to remove firmware from S3 by key `" << key << "`: " << e.what();
    }

    gtw.removeById(firmware->id());
    txn->commit();
}

} //namespace maps::fw_updater::storage
