/*
 * Unit tests for RecordIndex:
 * - Add/remove entries
 * - Name-based and UUID-based lookups
 * - Offset retrieval
 * - Type-based listing
 * - Invalidation
 */
#include <gtest/gtest.h>
#include <cstring>
#include <vector>
#include <uuidp.h>

#include "index.h"
#include "types.h"

using namespace authdb;

class RecordIndexTest : public ::testing::Test {
protected:
    RecordIndex idx;
    uuid::uuid u1, u2, u3;

    void SetUp() override {
        u1.generate();
        u2.generate();
        u3.generate();
    }
};

TEST_F(RecordIndexTest, AddAndFindByUuid) {
    idx.addEntry(u1, UserData, "username", 100);
    EXPECT_TRUE(idx.findByUuid(UserData, u1));
    EXPECT_FALSE(idx.findByUuid(UserData, u2));
}

TEST_F(RecordIndexTest, AddAndFindByName) {
    idx.addEntry(u1, UserData, "alice", 100);
    uuid::uuid found;
    EXPECT_TRUE(idx.findByName(UserData, "username", "alice", found));
    EXPECT_EQ(found, u1);
}

TEST_F(RecordIndexTest, FindByNameNotFound) {
    idx.addEntry(u1, UserData, "alice", 100);
    uuid::uuid found;
    EXPECT_FALSE(idx.findByName(UserData, "username", "bob", found));
}

TEST_F(RecordIndexTest, FindOffset) {
    idx.addEntry(u1, UserData, "alice", 200);
    size_t off = idx.findOffset(u1, UserData);
    EXPECT_EQ(off, 200u);
}

TEST_F(RecordIndexTest, ListByType) {
    idx.addEntry(u1, UserData, "alice", 100);
    idx.addEntry(u2, UserData, "bob", 200);
    idx.addEntry(u3, GroupData, "admins", 300);

    std::vector<uuid::uuid> users;
    idx.listByType(UserData, users);
    EXPECT_EQ(users.size(), 2u);

    std::vector<uuid::uuid> groups;
    idx.listByType(GroupData, groups);
    EXPECT_EQ(groups.size(), 1u);
}

TEST_F(RecordIndexTest, RemoveEntry) {
    idx.addEntry(u1, UserData, "alice", 100);
    EXPECT_TRUE(idx.findByUuid(UserData, u1));

    idx.removeEntry(u1, UserData);
    EXPECT_FALSE(idx.findByUuid(UserData, u1));
}

TEST_F(RecordIndexTest, RemoveNonexistentNoError) {
    uuid::uuid fake;
    fake.generate();
    idx.removeEntry(fake, UserData);
    SUCCEED();
}

TEST_F(RecordIndexTest, DifferentTypesCanShareUuid) {
    idx.addEntry(u1, UserData, "alice", 100);
    idx.addEntry(u1, GroupData, "alicegroup", 200);

    EXPECT_TRUE(idx.findByUuid(UserData, u1));
    EXPECT_TRUE(idx.findByUuid(GroupData, u1));

    EXPECT_EQ(idx.findOffset(u1, UserData), 100u);
    EXPECT_EQ(idx.findOffset(u1, GroupData), 200u);
}

TEST_F(RecordIndexTest, Invalidation) {
    idx.addEntry(u1, UserData, "alice", 100);
    idx.invalidate();
    EXPECT_FALSE(idx.valid());
}

TEST_F(RecordIndexTest, BulkEntries) {
    for (int i = 0; i < 100; ++i) {
        uuid::uuid uid;
        uid.generate();
        std::string name = "user_" + std::to_string(i);
        idx.addEntry(uid, UserData, name.c_str(), i * 64);
    }

    std::vector<uuid::uuid> all;
    idx.listByType(UserData, all);
    EXPECT_EQ(all.size(), 100u);
}
