#include "eye/frame_gateway.h"
#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_panel/tests/fixture.h>

#include <maps/wikimap/mapspro/services/mrc/eye/lib/detect_panel/include/sequence.h>

#include <library/cpp/testing/unittest/registar.h>

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

namespace {

std::vector<db::TIdSet> collectIds(const std::vector<db::eye::Frames>& sequences)
{
    std::vector<db::TIdSet> result;

    for (const auto& sequence: sequences) {
        db::TIdSet ids;

        for (const auto& frame: sequence) {
            ids.insert(frame.id());
        }

        result.push_back(std::move(ids));
    }

    return result;
}

bool equal(const std::vector<db::TIdSet>& lhs, std::vector<db::TIdSet> rhs)
{
    if (lhs.size() != rhs.size()) {
        return false;
    }

    for (const auto& ids: lhs) {
        const auto it = std::find(rhs.begin(), rhs.end(), ids);

        if (it == rhs.end()) {
            return false;
        }

        rhs.erase(it);
    }

    return true;
}

} // namespace

Y_UNIT_TEST_SUITE_F(sequence, Fixture)
{

Y_UNIT_TEST(full_sequence)
{
    const db::TIdSet frameIds = frameIdSetAt({0, 1, 2, 3, 4, 5});
    const size_t margin = 3;
    const std::chrono::seconds timeMargin(3);

    const std::vector<db::TIdSet> expected {
        frameIdSetAt({0, 1, 2, 3, 4, 5}),
    };

    const auto sequences = makeFrameSequences(*newTxn(), frameIds, margin, timeMargin);
    UNIT_ASSERT(equal(expected, collectIds(sequences)));
}

Y_UNIT_TEST(part_of_sequence)
{
    const db::TIdSet frameIds = frameIdSetAt({1, 3, 5});
    const size_t margin = 1;
    const std::chrono::seconds timeMargin(3);

    const std::vector<db::TIdSet> expected {
        frameIdSetAt({0, 1, 2, 3, 4, 5}),
    };

    const auto sequences = makeFrameSequences(*newTxn(), frameIds, margin, timeMargin);
    UNIT_ASSERT(equal(expected, collectIds(sequences)));
}

Y_UNIT_TEST(part_of_sequence_with_deleted_excluded)
{
    {
        auto txn = newTxn();
        frames[2].setDeleted(true);
        db::eye::FrameGateway(*txn).updatex(frames[2]);
        txn->commit();
    }

    const db::TIdSet frameIds = frameIdSetAt({1, 3, 5});
    const size_t margin = 1;
    const std::chrono::seconds timeMargin(3);

    const std::vector<db::TIdSet> expected {
        frameIdSetAt({0, 1, 3, 4, 5}),
    };

    const auto sequences = makeFrameSequences(*newTxn(), frameIds, margin, timeMargin);
    UNIT_ASSERT(equal(expected, collectIds(sequences)));
}


Y_UNIT_TEST(gap_in_sequence)
{
    const db::TIdSet frameIds = frameIdSetAt({1, 5});
    const size_t margin = 1;
    const std::chrono::seconds timeMargin(10);

    const std::vector<db::TIdSet> expected {
        frameIdSetAt({0, 1}),
        frameIdSetAt({4, 5}),
    };

    const auto sequences = makeFrameSequences(*newTxn(), frameIds, margin, timeMargin);
    UNIT_ASSERT(equal(expected, collectIds(sequences)));
}

Y_UNIT_TEST(time_gap_in_sequence)
{
    const db::TIdSet frameIds = frameIdSetAt({1, 4, 5});
    const size_t margin = 3;
    const std::chrono::seconds timeMargin(1);

    const std::vector<db::TIdSet> expected {
        frameIdSetAt({0, 1}),
        frameIdSetAt({3, 4, 5}),
    };

    const auto sequences = makeFrameSequences(*newTxn(), frameIds, margin, timeMargin);
    UNIT_ASSERT(equal(expected, collectIds(sequences)));
}

Y_UNIT_TEST(two_device)
{
    const db::TIdSet frameIds = frameIdSetAt({3, 4, 5, 6, 9});
    const size_t margin = 2;
    const std::chrono::seconds timeMargin(5);

    const std::vector<db::TIdSet> expected {
        frameIdSetAt({1, 2, 3, 4, 5}),
        frameIdSetAt({6, 7, 8, 9}),
    };

    const auto sequences = makeFrameSequences(*newTxn(), frameIds, margin, timeMargin);
    UNIT_ASSERT(equal(expected, collectIds(sequences)));
}

} // Y_UNIT_TEST_SUITE

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