#include <maps/wikimap/mapspro/services/mrc/eye/lib/common/include/secure_config.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_object/tests/fixture.h>
#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_object/tests/common.h>

#include <maps/wikimap/mapspro/services/mrc/eye/lib/unit_test/include/frame.h>

#include <maps/wikimap/mapspro/services/mrc/libs/db/include/eye/frame_gateway.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/eye/recognition_gateway.h>

namespace maps::mrc::eye::tests {

Fixture::Fixture()
{
    // The config content doesn't matter in the tests for now so anyone will fit.
    static std::once_flag onceFlag;
    std::call_once(onceFlag, SecureConfig::initialize, config());

    { // create devices
        auto txn = newTxn();

        devices = {
            {db::eye::MrcDeviceAttrs{"M1"}},
            {db::eye::MrcDeviceAttrs{"M2"}},
        };

        db::eye::DeviceGateway(*txn).upsertx(devices);
        txn->commit();
    }

    // create frames

    const db::TId firstDeviceId = devices[0].id();
    const db::TId secondDeviceId = devices[1].id();

    frames = {
        /* 0 */ {firstDeviceId, identical, makeUrlContext(1, "cw0"), {1920, 1080}, time()},
        /* 1 */ {firstDeviceId, identical, makeUrlContext(2, "cw0"), {1920, 1080}, time()},
        /* 2 */ {firstDeviceId, identical, makeUrlContext(3, "cw0"), {1920, 1080}, time()},

        /* 3 */ {secondDeviceId, onLeftSide, makeUrlContext(4, "cw90"), {1080, 1920}, time()},
        /* 4 */ {secondDeviceId, onLeftSide, makeUrlContext(5, "cw90"), {1080, 1920}, time()},
    };

    frames[0].setDeleted(true);

    auto insert = [&](size_t begin, size_t end)
    {
        auto txn = newTxn();

        TArrayRef ref(frames.data() + begin, frames.data() + end);
        db::eye::FrameGateway(*txn).insertx(ref);

        txn->commit();
    };

    insert(0, 1);
    insert(1, 3);
    insert(3, 5);

    { // create recognitions
        auto txn = newTxn();

        db::eye::Recognitions recognitions {
            {
                frames[0].id(),
                common::ImageOrientation(common::Rotation::CW_0),
                db::eye::RecognitionType::DetectTrafficLight,
                db::eye::RecognitionSource::Model,
                version,
                db::eye::DetectedTrafficLights(
                    {
                        db::eye::DetectedTrafficLight{common::ImageBox{300, 330, 320, 350}, 0.8},
                        db::eye::DetectedTrafficLight{common::ImageBox{400, 430, 420, 450}, 0.9},
                    }
                )
            },
            {
                frames[0].id(),
                common::ImageOrientation(common::Rotation::CW_180),
                db::eye::RecognitionType::DetectTrafficLight,
                db::eye::RecognitionSource::Model,
                version,
                db::eye::DetectedTrafficLights(
                    {
                        db::eye::DetectedTrafficLight{common::ImageBox{300, 330, 320, 350}, 0.8}                    }
                )
            },
            {
                frames[4].id(),
                frames[4].orientation(),
                db::eye::RecognitionType::DetectTrafficLight,
                db::eye::RecognitionSource::Toloka,
                version
            }
        };

        db::eye::RecognitionGateway(*txn).insertx(recognitions);

        txn->commit();
    }
}

Fixture::~Fixture() {
    playground().postgres().truncateTables();
}

db::TId Fixture::frameIdAt(size_t index) const { return frames.at(index).id(); }

db::TIds Fixture::frameIdsAt(std::initializer_list<size_t> indexes) const
{
    db::TIds ids;

    for (const auto index: indexes) {
        ids.push_back(frameIdAt(index));
    }

    return ids;
}

db::eye::Recognition Fixture::recognitionFor(const db::eye::Frame& frame, int16_t version) const
{
    namespace table = db::eye::table;

    const auto result = db::eye::RecognitionGateway(*newTxn()).load(
        table::Recognition::frameId == frame.id()
        and table::Recognition::orientation == static_cast<int16_t>(frame.orientation())
        and table::Recognition::source == db::eye::RecognitionSource::Model
        and table::Recognition::type == db::eye::RecognitionType::DetectTrafficLight
        and table::Recognition::version == version
    );

    ASSERT(result.size() == 1);
    return result.front();
}

} // namespace maps::mrc::eye::tests
