/*
 * Unit tests for UserData, GroupData, GpoData, DomainData, ClientData,
 * ServiceData: field getters/setters, data integrity, memory safety.
 *
 * NOTE: authdb defines enum values (UserData=1, GroupData=3, etc.) that
 * shadow the class names. We must use the 'class' keyword to disambiguate.
 */
#include <gtest/gtest.h>
#include <cstring>
#include <string>
#include <vector>
#include <uuidp.h>

#include "user.h"
#include "group.h"
#include "gpo.h"
#include "domain.h"
#include "client.h"
#include "service.h"
#include "backend.h"
#include "authdb.h"
#include "types.h"
#include "exception.h"
#include "cluster.h"

// AuthClientLock is declared extern in authdb.h but never defined
namespace authdb { std::mutex AuthClientLock; }

// ═══════════════════════════════════════
//  UserData
// ═══════════════════════════════════════

TEST(UserDataTest, SetAndGetUsername) {
    uuid::uuid id;
    id.generate();
    class authdb::UserData u(id);
    u.setUserName("alice");
    EXPECT_EQ(u.getUsername(), "alice");
}

TEST(UserDataTest, SetAndGetAllFields) {
    uuid::uuid id;
    id.generate();
    class authdb::UserData u(id);
    u.setUserName("bob");
    u.setFirstName("Bob");
    u.setLastName("Builder");
    u.setMail("bob@example.com");
    u.setPwHash("sha512:abcdef");

    EXPECT_EQ(u.getUsername(), "bob");
    EXPECT_EQ(u.getFirstname(), "Bob");
    EXPECT_EQ(u.getLastname(), "Builder");
    EXPECT_EQ(u.getMail(), "bob@example.com");
    EXPECT_EQ(u.getPwHash(), "sha512:abcdef");
}

TEST(UserDataTest, AvatarBinaryData) {
    uuid::uuid id;
    id.generate();
    class authdb::UserData u(id);

    std::vector<char> avatar = {0x00, 0x01, 0x02, (char)0xFF, (char)0xFE};
    u.setAvatar(avatar);

    std::vector<char> out;
    u.getAvatar(out);
    EXPECT_EQ(out.size(), avatar.size());
    EXPECT_EQ(out, avatar);
}

TEST(UserDataTest, OverwriteField) {
    uuid::uuid id;
    id.generate();
    class authdb::UserData u(id);
    u.setUserName("first");
    EXPECT_EQ(u.getUsername(), "first");
    u.setUserName("second");
    EXPECT_EQ(u.getUsername(), "second");
}

TEST(UserDataTest, DestructionWithDataNoLeak) {
    uuid::uuid id;
    id.generate();
    {
        class authdb::UserData u(id);
        u.setUserName("leaktest");
        u.setFirstName("First");
        u.setLastName("Last");
        u.setMail("leak@test.com");
        u.setPwHash("hash123");

        std::vector<char> bigAvatar(4096, 'A');
        u.setAvatar(bigAvatar);
    }
    SUCCEED();
}

// ═══════════════════════════════════════
//  GroupData
// ═══════════════════════════════════════

TEST(GroupDataTest, SetAndGetName) {
    uuid::uuid gid;
    gid.generate();
    class authdb::GroupData g(gid);
    g.setName("admins");
    EXPECT_EQ(g.getName(), "admins");
}

TEST(GroupDataTest, AddAndCheckMember) {
    uuid::uuid gid;
    gid.generate();
    class authdb::GroupData g(gid);
    g.setName("testgroup");

    uuid::uuid m1, m2;
    m1.generate();
    m2.generate();

    g.addMember(m1);
    g.addMember(m2);

    EXPECT_TRUE(g.isMember(m1));
    EXPECT_TRUE(g.isMember(m2));

    uuid::uuid alien;
    alien.generate();
    EXPECT_FALSE(g.isMember(alien));
}

TEST(GroupDataTest, RemoveMember) {
    uuid::uuid gid;
    gid.generate();
    class authdb::GroupData g(gid);
    g.setName("rmgroup");

    uuid::uuid m1;
    m1.generate();
    g.addMember(m1);
    EXPECT_TRUE(g.isMember(m1));

    g.remMember(m1);
    EXPECT_FALSE(g.isMember(m1));
}

TEST(GroupDataTest, GetMembers) {
    uuid::uuid gid;
    gid.generate();
    class authdb::GroupData g(gid);
    g.setName("listgroup");

    uuid::uuid m1, m2, m3;
    m1.generate();
    m2.generate();
    m3.generate();

    g.addMember(m1);
    g.addMember(m2);
    g.addMember(m3);

    std::vector<uuid::uuid> members;
    g.getMembers(members);
    EXPECT_EQ(members.size(), 3u);
}

TEST(GroupDataTest, CopyConstructor) {
    uuid::uuid gid;
    gid.generate();
    class authdb::GroupData orig(gid);
    orig.setName("copygroup");

    uuid::uuid m1;
    m1.generate();
    orig.addMember(m1);

    class authdb::GroupData copy(orig);
    EXPECT_EQ(copy.getName(), "copygroup");
    EXPECT_TRUE(copy.isMember(m1));
}

TEST(GroupDataTest, ManyMembersNoLeak) {
    uuid::uuid gid;
    gid.generate();
    {
        class authdb::GroupData g(gid);
        g.setName("biggroup");
        for (int i = 0; i < 100; ++i) {
            uuid::uuid m;
            m.generate();
            g.addMember(m);
        }
    }
    SUCCEED();
}

// ═══════════════════════════════════════
//  GpoData
// ═══════════════════════════════════════

TEST(GpoDataTest, SetAndGetName) {
    uuid::uuid gpoid;
    gpoid.generate();
    authdb::GpoData gpo(gpoid);
    gpo.setName("AllowLogin");
    EXPECT_STREQ(gpo.getName(), "AllowLogin");
}

TEST(GpoDataTest, SetAndGetDesc) {
    uuid::uuid gpoid;
    gpoid.generate();
    authdb::GpoData gpo(gpoid);
    gpo.setDesc("Controls login access");
    EXPECT_STREQ(gpo.getDesc(), "Controls login access");
}

TEST(GpoDataTest, GpoValueDefaultFalse) {
    uuid::uuid gpoid;
    gpoid.generate();
    authdb::GpoData gpo(gpoid);
    gpo.setGpoValue(false);
    EXPECT_FALSE(gpo.getGpoValue());
}

TEST(GpoDataTest, GpoValueTrue) {
    uuid::uuid gpoid;
    gpoid.generate();
    authdb::GpoData gpo(gpoid);
    gpo.setGpoValue(true);
    EXPECT_TRUE(gpo.getGpoValue());
}

TEST(GpoDataTest, AllowedList) {
    uuid::uuid gpoid;
    gpoid.generate();
    authdb::GpoData gpo(gpoid);
    gpo.setName("TestGPO");
    gpo.setGpoValue(false);

    uuid::uuid a1, a2;
    a1.generate();
    a2.generate();

    std::vector<uuid::uuid> allowed = {a1, a2};
    gpo.setAllowed(allowed);

    std::vector<uuid::uuid> out;
    gpo.getAlowed(out);
    EXPECT_EQ(out.size(), 2u);
}

TEST(GpoDataTest, CopyConstructor) {
    uuid::uuid gpoid;
    gpoid.generate();
    authdb::GpoData orig(gpoid);
    orig.setName("CopyGPO");
    orig.setGpoValue(true);

    authdb::GpoData copy(orig);
    EXPECT_STREQ(copy.getName(), "CopyGPO");
    EXPECT_TRUE(copy.getGpoValue());
}

// ═══════════════════════════════════════
//  DomainData
// ═══════════════════════════════════════

TEST(DomainDataTest, SetAndGetDomainName) {
    uuid::uuid did;
    did.generate();
    class authdb::DomainData d(did);
    d.setDomainName("example.com");
    EXPECT_EQ(d.getDomainName(), "example.com");
}

TEST(DomainDataTest, StorageType) {
    uuid::uuid did;
    did.generate();
    class authdb::DomainData d(did);
    d.setStorageType(0);
    EXPECT_EQ(d.getStorageType(), 0);
}

TEST(DomainDataTest, StorageOptions) {
    uuid::uuid did;
    did.generate();
    class authdb::DomainData d(did);
    d.setStorageOptions("/var/lib/authdb/example.db");
    EXPECT_EQ(d.getStorageOptions(), "/var/lib/authdb/example.db");
}

// ═══════════════════════════════════════
//  ClientData
// ═══════════════════════════════════════

TEST(ClientDataTest, SetAndGetName) {
    uuid::uuid clid;
    clid.generate();
    class authdb::ClientData c(clid);
    c.setName("webserver");
    EXPECT_STREQ(c.getName(), "webserver");
}

TEST(ClientDataTest, SetAndGetKey) {
    uuid::uuid clid;
    clid.generate();
    class authdb::ClientData c(clid);
    c.setKey("supersecretkey");
    EXPECT_STREQ(c.getKey(), "supersecretkey");
}

TEST(ClientDataTest, SetAndGetKrbKey) {
    uuid::uuid clid;
    clid.generate();
    class authdb::ClientData c(clid);
    c.setKrbKey("aabbccdd");
    EXPECT_EQ(c.getKrbKey(), "aabbccdd");
}

// ═══════════════════════════════════════
//  ServiceData
// ═══════════════════════════════════════

TEST(ServiceDataTest, SetAndGetServiceName) {
    uuid::uuid sid;
    sid.generate();
    class authdb::ServiceData s(sid);
    s.setServiceName("HTTP");
    EXPECT_STREQ(s.getServiceName(), "HTTP");
}

TEST(ServiceDataTest, SetAndGetHostName) {
    uuid::uuid sid;
    sid.generate();
    class authdb::ServiceData s(sid);
    s.setHostName("mail.example.com");
    EXPECT_STREQ(s.getHostName(), "mail.example.com");
}

TEST(ServiceDataTest, SetAndGetAllFields) {
    uuid::uuid sid;
    sid.generate();
    class authdb::ServiceData s(sid);
    s.setServiceName("ldap");
    s.setHostName("dc.example.com");
    s.setClientId("client-uuid-hex");
    s.setKrbKey("krbkeyhex");
    s.setSecret("sharedpass");

    EXPECT_STREQ(s.getServiceName(), "ldap");
    EXPECT_STREQ(s.getHostName(), "dc.example.com");
    EXPECT_STREQ(s.getClientId(), "client-uuid-hex");
    EXPECT_EQ(s.getKrbKey(), "krbkeyhex");
    EXPECT_STREQ(s.getSecret(), "sharedpass");
}

TEST(ServiceDataTest, DestructionNoLeak) {
    uuid::uuid sid;
    sid.generate();
    {
        class authdb::ServiceData s(sid);
        s.setServiceName("imap");
        s.setHostName("imap.example.com");
        s.setClientId("cidstring");
        s.setKrbKey("longhexkey1234567890abcdef");
        s.setSecret("secretvalue");
    }
    SUCCEED();
}
