#include "fixture.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/libs/introspection/include/comparison.h>
#include <maps/libs/introspection/include/stream_output.h>

#include <library/cpp/testing/gtest/gtest.h>


namespace maps::fw_updater::db {
using introspection::operator==;
using introspection::operator!=;
using introspection::operator<<;
} // namespace maps::fw_updater::db


namespace maps::fw_updater::db::tests {

namespace {

constexpr TId IDM_MRC_PROJECT_ID = 2;

const std::string HARDWARE_1 = "nanopi-neo4";
const std::string HARDWARE_2 = "raspberry-pi-3b";

const std::string DEVICE_A = "a";
const std::string DEVICE_B = "b";

Firmware createFirmware(const std::string& hardwareId,
                        Slot slot,
                        const std::string& version)
{
    return Firmware(hardwareId,
                    slot,
                    version,
                    "s3.yandex.ru/key",
                    195716838,
                    "22c14bc29040b2b2e0a3e9ed02d99eeb");
}

} // namespace

TEST_F(Fixture, test_hardware)
{
    Hardwares hardwares;
    {
        // Hardwares are created in data.sql
        auto txn = txnHandle();
        HardwareGateway gtw{*txn};
        hardwares = gtw.load();
        EXPECT_EQ(hardwares.size(), 2u);

        auto hardware1 = gtw.loadById(HARDWARE_1);
        EXPECT_EQ(hardware1.idmProjectId(), IDM_MRC_PROJECT_ID);
        ASSERT_TRUE(hardware1.attrs().has_value());
        EXPECT_EQ((*hardware1.attrs())["arch"].as<std::string>(), "arm64");

        auto hardware2 = gtw.loadById(HARDWARE_2);
        EXPECT_EQ(hardware2.idmProjectId(), IDM_MRC_PROJECT_ID);
        ASSERT_TRUE(hardware2.attrs().has_value());
        EXPECT_EQ((*hardware2.attrs())["arch"].as<std::string>(), "arm64");
        EXPECT_EQ((*hardware2.attrs())["version"].as<std::string>(), "3b");
    }
    {
        hardwares[0].clearAttrs();
        hardwares[1].setAttrs(json::Value::fromString("{\"attr\": \"new\"}"));

        auto txn = txnHandle();
        HardwareGateway{*txn}.update(hardwares);
        txn->commit();
    }
    {
        auto updatedHardwares = HardwareGateway{*txnHandle()}.load();
        EXPECT_EQ(updatedHardwares, hardwares);
    }
}


TEST_F(Fixture, test_firmware)
{

    Firmwares firmwares {
        createFirmware(HARDWARE_1, Slot::Rootfs, "1.0.0"),
        createFirmware(HARDWARE_1, Slot::Rootfs, "2.0.0"),
        createFirmware(HARDWARE_2, Slot::Rootfs, "1.0.0")
    };

    // Save
    {
        auto txn = txnHandle();
        FirmwareGateway{*txn}.insert(firmwares);
        txn->commit();
    }

    // Unique constraint violation
    {
        auto nonUniqueVersion = createFirmware(HARDWARE_1, Slot::Rootfs, "1.0.0");
        EXPECT_THROW(FirmwareGateway{*txnHandle()}.insert(nonUniqueVersion),
                     sql_chemistry::UniqueViolationError);
    }

    // Load
    {
        auto txn = txnHandle();
        FirmwareGateway gtw{*txn};

        auto loaded = gtw.load(sql_chemistry::orderBy(table::Firmware::id));
        EXPECT_EQ(loaded, firmwares);

        loaded = gtw.load(table::Firmware::hardwareId == HARDWARE_1,
                          sql_chemistry::orderBy(table::Firmware::id));
        ASSERT_EQ(loaded.size(), 2u);
        EXPECT_EQ(loaded[0], firmwares[0]);
        EXPECT_EQ(loaded[1], firmwares[1]);
        EXPECT_GT(loaded[1].versionSeq(), loaded[0].versionSeq());
    }
}

} // namespace maps::fw_updater::db::tests
