#include <QtTest/QtTest>
#include "core.h"
#include "import_source.h"

using namespace rcx;

class TestImportSource : public QObject {
    Q_OBJECT
private slots:
    // Basic type tests
    void emptyInput();
    void noStructs();
    void singleEmptyStruct();
    void stdintTypes();
    void windowsTypes();
    void platformPointerTypes();
    void standardCTypes();
    void multiWordTypes();
    void floatDouble();
    void boolType();

    // Pointer tests
    void voidPointer();
    void typedPointer();
    void selfReferencingPointer();
    void doublePointer();

    // Array tests
    void primitiveArray();
    void charArrayToUtf8();
    void wcharArrayToUtf16();
    void floatArrayToVec2();
    void floatArrayToVec3();
    void floatArrayToVec4();
    void floatArray4x4ToMat4x4();
    void genericFloatArray();
    void structArray();

    // Comment offset tests
    void commentOffsets();
    void computedOffsets();
    void mixedOffsetsAutoDetect();

    // Multi-struct tests
    void multiStruct();
    void pointerCrossRef();

    // Forward declarations
    void forwardDeclaration();

    // Union handling
    void unionPickFirst();

    // Padding fields
    void paddingFieldExpansion();

    // static_assert
    void staticAssertTailPadding();

    // Embedded struct
    void embeddedStruct();

    // Typedef
    void typedefBasic();

    // Qualifiers
    void constVolatileQualifiers();
    void structPrefixOnType();

    // Edge cases
    void bitfieldSkipped();
    void hexArraySizes();
    void windowsStylePEB();
    void classKeyword();
    void inheritanceSkipped();

    // Round-trip test (requires generator.h)
    void basicRoundTrip();
};

// ── Helper ──

static int countRoots(const NodeTree& tree) {
    int n = 0;
    for (const auto& node : tree.nodes)
        if (node.parentId == 0 && node.kind == NodeKind::Struct) n++;
    return n;
}

static QVector<int> childrenOf(const NodeTree& tree, uint64_t parentId) {
    QVector<int> result;
    for (int i = 0; i < tree.nodes.size(); i++)
        if (tree.nodes[i].parentId == parentId) result.append(i);
    return result;
}

// ── Tests ──

void TestImportSource::emptyInput() {
    QString err;
    NodeTree tree = importFromSource(QString(), &err);
    QVERIFY(tree.nodes.isEmpty());
    QVERIFY(!err.isEmpty());
}

void TestImportSource::noStructs() {
    QString err;
    NodeTree tree = importFromSource(QStringLiteral("int x = 42;"), &err);
    QVERIFY(tree.nodes.isEmpty());
    QVERIFY(!err.isEmpty());
}

void TestImportSource::singleEmptyStruct() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Empty {};\n"
    ));
    QCOMPARE(countRoots(tree), 1);
    QCOMPARE(tree.nodes[0].name, QStringLiteral("Empty"));
    QCOMPARE(tree.nodes[0].kind, NodeKind::Struct);
}

void TestImportSource::stdintTypes() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Test {\n"
        "    uint8_t  a;\n"
        "    int8_t   b;\n"
        "    uint16_t c;\n"
        "    int16_t  d;\n"
        "    uint32_t e;\n"
        "    int32_t  f;\n"
        "    uint64_t g;\n"
        "    int64_t  h;\n"
        "};\n"
    ));
    QCOMPARE(countRoots(tree), 1);
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 8);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::UInt8);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Int8);
    QCOMPARE(tree.nodes[kids[2]].kind, NodeKind::UInt16);
    QCOMPARE(tree.nodes[kids[3]].kind, NodeKind::Int16);
    QCOMPARE(tree.nodes[kids[4]].kind, NodeKind::UInt32);
    QCOMPARE(tree.nodes[kids[5]].kind, NodeKind::Int32);
    QCOMPARE(tree.nodes[kids[6]].kind, NodeKind::UInt64);
    QCOMPARE(tree.nodes[kids[7]].kind, NodeKind::Int64);
}

void TestImportSource::windowsTypes() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct WinTypes {\n"
        "    BYTE a;\n"
        "    WORD b;\n"
        "    DWORD c;\n"
        "    QWORD d;\n"
        "    ULONG e;\n"
        "    LONG f;\n"
        "    USHORT g;\n"
        "    UCHAR h;\n"
        "    BOOLEAN i;\n"
        "    BOOL j;\n"
        "    CHAR k;\n"
        "    WCHAR l;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 12);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::UInt8);   // BYTE
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::UInt16);  // WORD
    QCOMPARE(tree.nodes[kids[2]].kind, NodeKind::UInt32);  // DWORD
    QCOMPARE(tree.nodes[kids[3]].kind, NodeKind::UInt64);  // QWORD
    QCOMPARE(tree.nodes[kids[4]].kind, NodeKind::UInt32);  // ULONG
    QCOMPARE(tree.nodes[kids[5]].kind, NodeKind::Int32);   // LONG
    QCOMPARE(tree.nodes[kids[6]].kind, NodeKind::UInt16);  // USHORT
    QCOMPARE(tree.nodes[kids[7]].kind, NodeKind::UInt8);   // UCHAR
    QCOMPARE(tree.nodes[kids[8]].kind, NodeKind::UInt8);   // BOOLEAN
    QCOMPARE(tree.nodes[kids[9]].kind, NodeKind::Int32);   // BOOL
    QCOMPARE(tree.nodes[kids[10]].kind, NodeKind::Int8);   // CHAR
    QCOMPARE(tree.nodes[kids[11]].kind, NodeKind::UInt16); // WCHAR
}

void TestImportSource::platformPointerTypes() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct PtrTypes {\n"
        "    PVOID a;\n"
        "    HANDLE b;\n"
        "    SIZE_T c;\n"
        "    ULONG_PTR d;\n"
        "    uintptr_t e;\n"
        "    size_t f;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 6);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Pointer64);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Pointer64);
    QCOMPARE(tree.nodes[kids[2]].kind, NodeKind::UInt64);
    QCOMPARE(tree.nodes[kids[3]].kind, NodeKind::UInt64);
    QCOMPARE(tree.nodes[kids[4]].kind, NodeKind::UInt64);
    QCOMPARE(tree.nodes[kids[5]].kind, NodeKind::UInt64);
}

void TestImportSource::standardCTypes() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct CTypes {\n"
        "    char a;\n"
        "    short b;\n"
        "    int c;\n"
        "    long d;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 4);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Int8);    // char
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Int16);   // short
    QCOMPARE(tree.nodes[kids[2]].kind, NodeKind::Int32);   // int
    QCOMPARE(tree.nodes[kids[3]].kind, NodeKind::Int32);   // long
}

void TestImportSource::multiWordTypes() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct MultiWord {\n"
        "    unsigned char a;\n"
        "    unsigned short b;\n"
        "    unsigned int c;\n"
        "    unsigned long d;\n"
        "    long long e;\n"
        "    unsigned long long f;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 6);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::UInt8);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::UInt16);
    QCOMPARE(tree.nodes[kids[2]].kind, NodeKind::UInt32);
    QCOMPARE(tree.nodes[kids[3]].kind, NodeKind::UInt32);
    QCOMPARE(tree.nodes[kids[4]].kind, NodeKind::Int64);
    QCOMPARE(tree.nodes[kids[5]].kind, NodeKind::UInt64);
}

void TestImportSource::floatDouble() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct FD {\n"
        "    float a;\n"
        "    double b;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 2);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Float);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Double);
}

void TestImportSource::boolType() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct B {\n"
        "    bool a;\n"
        "    _Bool b;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 2);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Bool);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Bool);
}

void TestImportSource::voidPointer() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct VP {\n"
        "    void* ptr;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Pointer64);
    QCOMPARE(tree.nodes[kids[0]].name, QStringLiteral("ptr"));
    QCOMPARE(tree.nodes[kids[0]].refId, uint64_t(0)); // void* has no target
}

void TestImportSource::typedPointer() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Target {\n"
        "    int x;\n"
        "};\n"
        "struct HasPtr {\n"
        "    Target* pTarget;\n"
        "};\n"
    ));
    QCOMPARE(countRoots(tree), 2);
    // Find HasPtr
    int hasPtrIdx = -1;
    for (int i = 0; i < tree.nodes.size(); i++) {
        if (tree.nodes[i].name == QStringLiteral("HasPtr") && tree.nodes[i].parentId == 0) {
            hasPtrIdx = i; break;
        }
    }
    QVERIFY(hasPtrIdx >= 0);
    auto kids = childrenOf(tree, tree.nodes[hasPtrIdx].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Pointer64);
    QVERIFY(tree.nodes[kids[0]].refId != 0);
    // refId should point to Target struct
    int targetIdx = tree.indexOfId(tree.nodes[kids[0]].refId);
    QVERIFY(targetIdx >= 0);
    QCOMPARE(tree.nodes[targetIdx].name, QStringLiteral("Target"));
}

void TestImportSource::selfReferencingPointer() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Node {\n"
        "    int value;\n"
        "    Node* next;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 2);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Pointer64);
    QCOMPARE(tree.nodes[kids[1]].refId, tree.nodes[0].id);
}

void TestImportSource::doublePointer() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct DP {\n"
        "    void** ppData;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Pointer64);
}

void TestImportSource::primitiveArray() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct PA {\n"
        "    int32_t values[10];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Array);
    QCOMPARE(tree.nodes[kids[0]].arrayLen, 10);
    QCOMPARE(tree.nodes[kids[0]].elementKind, NodeKind::Int32);
}

void TestImportSource::charArrayToUtf8() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct CA {\n"
        "    char name[64];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::UTF8);
    QCOMPARE(tree.nodes[kids[0]].strLen, 64);
}

void TestImportSource::wcharArrayToUtf16() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct WC {\n"
        "    wchar_t name[32];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::UTF16);
    QCOMPARE(tree.nodes[kids[0]].strLen, 32);
}

void TestImportSource::floatArrayToVec2() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct V {\n"
        "    float pos[2];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Vec2);
}

void TestImportSource::floatArrayToVec3() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct V {\n"
        "    float pos[3];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Vec3);
}

void TestImportSource::floatArrayToVec4() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct V {\n"
        "    float rot[4];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Vec4);
}

void TestImportSource::floatArray4x4ToMat4x4() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct M {\n"
        "    float matrix[4][4];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Mat4x4);
}

void TestImportSource::genericFloatArray() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct GF {\n"
        "    float values[8];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Array);
    QCOMPARE(tree.nodes[kids[0]].arrayLen, 8);
    QCOMPARE(tree.nodes[kids[0]].elementKind, NodeKind::Float);
}

void TestImportSource::structArray() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Item {\n"
        "    int id;\n"
        "};\n"
        "struct Container {\n"
        "    Item items[5];\n"
        "};\n"
    ));
    QCOMPARE(countRoots(tree), 2);
    // Find Container
    int contIdx = -1;
    for (int i = 0; i < tree.nodes.size(); i++) {
        if (tree.nodes[i].name == QStringLiteral("Container") && tree.nodes[i].parentId == 0) {
            contIdx = i; break;
        }
    }
    QVERIFY(contIdx >= 0);
    auto kids = childrenOf(tree, tree.nodes[contIdx].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Array);
    QCOMPARE(tree.nodes[kids[0]].arrayLen, 5);
    QCOMPARE(tree.nodes[kids[0]].elementKind, NodeKind::Struct);
}

void TestImportSource::commentOffsets() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Offsets {\n"
        "    uint64_t vtable; // 0x0\n"
        "    float health; // 0x8\n"
        "    uint8_t _pad000C[0x4]; // 0xC\n"
        "    double score; // 0x10\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    // vtable at 0x0
    QCOMPARE(tree.nodes[kids[0]].offset, 0);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::UInt64);
    // health at 0x8
    QCOMPARE(tree.nodes[kids[1]].offset, 8);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Float);
    // _pad at 0xC -> hex nodes
    // score at 0x10
    // Find the double
    bool foundDouble = false;
    for (int k : kids) {
        if (tree.nodes[k].kind == NodeKind::Double) {
            QCOMPARE(tree.nodes[k].offset, 0x10);
            foundDouble = true;
        }
    }
    QVERIFY(foundDouble);
}

void TestImportSource::computedOffsets() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Computed {\n"
        "    uint8_t a;\n"
        "    uint16_t b;\n"
        "    uint32_t c;\n"
        "    uint64_t d;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 4);
    QCOMPARE(tree.nodes[kids[0]].offset, 0);  // uint8_t at 0
    QCOMPARE(tree.nodes[kids[1]].offset, 1);  // uint16_t at 1
    QCOMPARE(tree.nodes[kids[2]].offset, 3);  // uint32_t at 3
    QCOMPARE(tree.nodes[kids[3]].offset, 7);  // uint64_t at 7
}

void TestImportSource::mixedOffsetsAutoDetect() {
    // If any field has a comment offset, all should use comment mode
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Mixed {\n"
        "    uint32_t a; // 0x0\n"
        "    uint32_t b;\n"
        "    uint32_t c; // 0x10\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(tree.nodes[kids[0]].offset, 0);
    // b has no comment offset, in comment mode it gets computed offset 4
    QCOMPARE(tree.nodes[kids[1]].offset, 4);
    // c has comment offset 0x10
    QCOMPARE(tree.nodes[kids[2]].offset, 0x10);
}

void TestImportSource::multiStruct() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct A {\n"
        "    int x;\n"
        "};\n"
        "struct B {\n"
        "    float y;\n"
        "};\n"
        "struct C {\n"
        "    double z;\n"
        "};\n"
    ));
    QCOMPARE(countRoots(tree), 3);
}

void TestImportSource::pointerCrossRef() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct A {\n"
        "    int value;\n"
        "};\n"
        "struct B {\n"
        "    A* ref;\n"
        "};\n"
    ));
    // Find B's pointer field
    int bIdx = -1;
    for (int i = 0; i < tree.nodes.size(); i++) {
        if (tree.nodes[i].name == QStringLiteral("B") && tree.nodes[i].parentId == 0) {
            bIdx = i; break;
        }
    }
    QVERIFY(bIdx >= 0);
    auto kids = childrenOf(tree, tree.nodes[bIdx].id);
    QCOMPARE(kids.size(), 1);
    QVERIFY(tree.nodes[kids[0]].refId != 0);
    // Should point to A
    int aIdx = tree.indexOfId(tree.nodes[kids[0]].refId);
    QVERIFY(aIdx >= 0);
    QCOMPARE(tree.nodes[aIdx].name, QStringLiteral("A"));
}

void TestImportSource::forwardDeclaration() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Bar;\n"
        "struct Foo {\n"
        "    Bar* pBar;\n"
        "};\n"
        "struct Bar {\n"
        "    int val;\n"
        "};\n"
    ));
    QCOMPARE(countRoots(tree), 2);
    // Foo's pBar should resolve to Bar
    int fooIdx = -1;
    for (int i = 0; i < tree.nodes.size(); i++) {
        if (tree.nodes[i].name == QStringLiteral("Foo") && tree.nodes[i].parentId == 0) {
            fooIdx = i; break;
        }
    }
    QVERIFY(fooIdx >= 0);
    auto kids = childrenOf(tree, tree.nodes[fooIdx].id);
    QCOMPARE(kids.size(), 1);
    QVERIFY(tree.nodes[kids[0]].refId != 0);
}

void TestImportSource::unionPickFirst() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct WithUnion {\n"
        "    union {\n"
        "        float asFloat;\n"
        "        uint32_t asInt;\n"
        "    };\n"
        "    int after;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    // Should have 2 fields: asFloat (first union member) + after
    QCOMPARE(kids.size(), 2);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Float);
    QCOMPARE(tree.nodes[kids[0]].name, QStringLiteral("asFloat"));
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Int32);
    QCOMPARE(tree.nodes[kids[1]].name, QStringLiteral("after"));
}

void TestImportSource::paddingFieldExpansion() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Padded {\n"
        "    uint8_t _pad0000[0x10];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    // 0x10 = 16 bytes, should be 2x Hex64 (best fit)
    QCOMPARE(kids.size(), 2);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Hex64);
    QCOMPARE(tree.nodes[kids[0]].offset, 0);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Hex64);
    QCOMPARE(tree.nodes[kids[1]].offset, 8);
}

void TestImportSource::staticAssertTailPadding() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Sized {\n"
        "    uint32_t x;\n"
        "};\n"
        "static_assert(sizeof(Sized) == 0x10, \"Size check\");\n"
    ));
    // x is 4 bytes, static_assert says 0x10 = 16
    // Should have tail padding from offset 4 to 16 (12 bytes)
    int span = tree.structSpan(tree.nodes[0].id);
    QCOMPARE(span, 0x10);
}

void TestImportSource::embeddedStruct() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Inner {\n"
        "    int a;\n"
        "};\n"
        "struct Outer {\n"
        "    Inner embedded;\n"
        "    float after;\n"
        "};\n"
    ));
    int outerIdx = -1;
    for (int i = 0; i < tree.nodes.size(); i++) {
        if (tree.nodes[i].name == QStringLiteral("Outer") && tree.nodes[i].parentId == 0) {
            outerIdx = i; break;
        }
    }
    QVERIFY(outerIdx >= 0);
    auto kids = childrenOf(tree, tree.nodes[outerIdx].id);
    QCOMPARE(kids.size(), 2);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Struct);
    QCOMPARE(tree.nodes[kids[0]].structTypeName, QStringLiteral("Inner"));
    QVERIFY(tree.nodes[kids[0]].refId != 0);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Float);
}

void TestImportSource::typedefBasic() {
    NodeTree tree = importFromSource(QStringLiteral(
        "typedef uint32_t MyInt;\n"
        "struct TD {\n"
        "    MyInt value;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::UInt32);
}

void TestImportSource::constVolatileQualifiers() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Quals {\n"
        "    const uint32_t a;\n"
        "    volatile int32_t b;\n"
        "    const volatile uint8_t c;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 3);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::UInt32);
    QCOMPARE(tree.nodes[kids[1]].kind, NodeKind::Int32);
    QCOMPARE(tree.nodes[kids[2]].kind, NodeKind::UInt8);
}

void TestImportSource::structPrefixOnType() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Inner {\n"
        "    int val;\n"
        "};\n"
        "struct Outer {\n"
        "    struct Inner member;\n"
        "};\n"
    ));
    int outerIdx = -1;
    for (int i = 0; i < tree.nodes.size(); i++) {
        if (tree.nodes[i].name == QStringLiteral("Outer") && tree.nodes[i].parentId == 0) {
            outerIdx = i; break;
        }
    }
    QVERIFY(outerIdx >= 0);
    auto kids = childrenOf(tree, tree.nodes[outerIdx].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Struct);
    QCOMPARE(tree.nodes[kids[0]].structTypeName, QStringLiteral("Inner"));
}

void TestImportSource::bitfieldSkipped() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct BF {\n"
        "    uint32_t normal;\n"
        "    uint32_t bitA : 4;\n"
        "    uint32_t bitB : 12;\n"
        "    uint32_t after;\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    // Bitfields should be skipped, only normal + after
    QCOMPARE(kids.size(), 2);
    QCOMPARE(tree.nodes[kids[0]].name, QStringLiteral("normal"));
    QCOMPARE(tree.nodes[kids[1]].name, QStringLiteral("after"));
}

void TestImportSource::hexArraySizes() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct HexArr {\n"
        "    uint8_t data[0x20];\n"
        "};\n"
    ));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Array);
    QCOMPARE(tree.nodes[kids[0]].arrayLen, 0x20);
}

void TestImportSource::windowsStylePEB() {
    // Test with Windows PEB-style struct (no comment offsets)
    NodeTree tree = importFromSource(QStringLiteral(
        "struct PEB64 {\n"
        "    BOOLEAN InheritedAddressSpace;\n"
        "    BOOLEAN ReadImageFileExecOptions;\n"
        "    BOOLEAN BeingDebugged;\n"
        "    BOOLEAN BitField;\n"
        "    PVOID Mutant;\n"
        "    PVOID ImageBaseAddress;\n"
        "};\n"
    ));
    QCOMPARE(countRoots(tree), 1);
    QCOMPARE(tree.nodes[0].name, QStringLiteral("PEB64"));
    auto kids = childrenOf(tree, tree.nodes[0].id);
    QCOMPARE(kids.size(), 6);
    // First 4 are BOOLEAN (UInt8)
    for (int i = 0; i < 4; i++)
        QCOMPARE(tree.nodes[kids[i]].kind, NodeKind::UInt8);
    // Last 2 are PVOID (Pointer64)
    QCOMPARE(tree.nodes[kids[4]].kind, NodeKind::Pointer64);
    QCOMPARE(tree.nodes[kids[5]].kind, NodeKind::Pointer64);
}

void TestImportSource::classKeyword() {
    NodeTree tree = importFromSource(QStringLiteral(
        "class MyClass {\n"
        "    int value;\n"
        "};\n"
    ));
    QCOMPARE(countRoots(tree), 1);
    QCOMPARE(tree.nodes[0].classKeyword, QStringLiteral("class"));
}

void TestImportSource::inheritanceSkipped() {
    NodeTree tree = importFromSource(QStringLiteral(
        "struct Base {\n"
        "    int a;\n"
        "};\n"
        "struct Derived : public Base {\n"
        "    float b;\n"
        "};\n"
    ));
    QCOMPARE(countRoots(tree), 2);
    int derivedIdx = -1;
    for (int i = 0; i < tree.nodes.size(); i++) {
        if (tree.nodes[i].name == QStringLiteral("Derived") && tree.nodes[i].parentId == 0) {
            derivedIdx = i; break;
        }
    }
    QVERIFY(derivedIdx >= 0);
    auto kids = childrenOf(tree, tree.nodes[derivedIdx].id);
    QCOMPARE(kids.size(), 1);
    QCOMPARE(tree.nodes[kids[0]].kind, NodeKind::Float);
}

void TestImportSource::basicRoundTrip() {
    // Build a simple tree manually, export it, then re-import and compare
    NodeTree original;
    {
        Node s;
        s.kind = NodeKind::Struct;
        s.name = QStringLiteral("RoundTrip");
        s.structTypeName = QStringLiteral("RoundTrip");
        s.parentId = 0;
        s.offset = 0;
        int sIdx = original.addNode(s);
        uint64_t sId = original.nodes[sIdx].id;

        Node f1;
        f1.kind = NodeKind::UInt32;
        f1.name = QStringLiteral("field_a");
        f1.parentId = sId;
        f1.offset = 0;
        original.addNode(f1);

        Node f2;
        f2.kind = NodeKind::Float;
        f2.name = QStringLiteral("field_b");
        f2.parentId = sId;
        f2.offset = 4;
        original.addNode(f2);

        Node f3;
        f3.kind = NodeKind::UInt64;
        f3.name = QStringLiteral("field_c");
        f3.parentId = sId;
        f3.offset = 8;
        original.addNode(f3);
    }

    // Create source text that matches what generator would produce
    QString source = QStringLiteral(
        "struct RoundTrip {\n"
        "    uint32_t field_a; // 0x0\n"
        "    float field_b; // 0x4\n"
        "    uint64_t field_c; // 0x8\n"
        "};\n"
        "static_assert(sizeof(RoundTrip) == 0x10, \"Size mismatch\");\n"
    );

    NodeTree reimported = importFromSource(source);
    QCOMPARE(countRoots(reimported), 1);
    QCOMPARE(reimported.nodes[0].name, QStringLiteral("RoundTrip"));

    auto origKids = childrenOf(original, original.nodes[0].id);
    auto reimpKids = childrenOf(reimported, reimported.nodes[0].id);

    // Compare field count (reimported may have extra padding nodes from static_assert)
    // Check that the first 3 fields match
    QVERIFY(reimpKids.size() >= 3);
    for (int i = 0; i < 3; i++) {
        QCOMPARE(reimported.nodes[reimpKids[i]].kind, original.nodes[origKids[i]].kind);
        QCOMPARE(reimported.nodes[reimpKids[i]].name, original.nodes[origKids[i]].name);
        QCOMPARE(reimported.nodes[reimpKids[i]].offset, original.nodes[origKids[i]].offset);
    }
}

QTEST_MAIN(TestImportSource)
#include "test_import_source.moc"
