#include "MarkdownGenerator.h"
#include "AiGenerator.h"
#include <Core/HAL/Filesystem/Filesystem.h>
#include <Core/Common/CoreUtils.h>
#include <Core/Memory/MemoryArena.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <Windows.h>

namespace Romeo
{
    struct ParsedSymbol
    {
        Juliet::String Name;
        Juliet::String ReturnType;
        Juliet::String Parameters;
        Juliet::String Type; // "class", "struct", "union", "enum", "enum class", "function"
        Juliet::String Namespace;
        Juliet::String ClassContext;
        bool IsConst = false;
    };

    struct Token
    {
        const char* Start = nullptr;
        size_t Length = 0;
        bool IsIdentifier = false;
    };

    static void EnsureDirectoryExists(const char* dir)
    {
        CreateDirectoryA(dir, nullptr);
    }

    struct StringBuilder
    {
        Juliet::Arena* Arena;
        char* Buffer = nullptr;
        size_t Size = 0;
        size_t Capacity = 0;

#if COMPILER_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#endif
        void Append(const char* format, ...)
        {
            va_list args;
            va_start(args, format);
            int len = vsnprintf(nullptr, 0, format, args);
            va_end(args);

            if (len <= 0) return;

            size_t uLen = static_cast<size_t>(len);

            if (Size + uLen + 1 > Capacity)
            {
                size_t newCap = Capacity == 0 ? 4096 : Capacity * 2;
                while (Size + uLen + 1 > newCap)
                {
                    newCap *= 2;
                }
                char* newBuf = Juliet::ArenaPushArray<char>(Arena, newCap JULIET_DEBUG_PARAM("StringBuilder"));
                if (Buffer)
                {
                    memcpy(newBuf, Buffer, Size);
                }
                Buffer = newBuf;
                Capacity = newCap;
            }

            va_start(args, format);
            vsnprintf(Buffer + Size, uLen + 1, format, args);
            va_end(args);
            Size += uLen;
            Buffer[Size] = '\0';
        }
#if COMPILER_CLANG
#pragma clang diagnostic pop
#endif
    };

    static Juliet::String ReadFileContent(Juliet::Arena* arena, const char* pathBuf)
    {
        FILE* f = nullptr;
        if (fopen_s(&f, pathBuf, "rb") != 0 || !f)
        {
            return {};
        }
        
        fseek(f, 0, SEEK_END);
        long size = ftell(f);
        fseek(f, 0, SEEK_SET);
        
        if (size <= 0)
        {
            fclose(f);
            return {};
        }
        
        char* buf = Juliet::ArenaPushArray<char>(arena, static_cast<size_t>(size + 1) JULIET_DEBUG_PARAM("FileReadContent"));
        size_t readBytes = fread(buf, 1, static_cast<size_t>(size), f);
        buf[readBytes] = '\0';
        
        fclose(f);
        return { buf, readBytes };
    }

    static Juliet::String GetRelativeString(const Juliet::String& absolutePath, const Juliet::String& workspaceRoot)
    {
        if (absolutePath.Size > workspaceRoot.Size)
        {
            const char* absPtr = CStr(absolutePath);
            const char* rootPtr = CStr(workspaceRoot);
            size_t rootSize = workspaceRoot.Size;

            size_t matched = 0;
            while (matched < rootSize && matched < absolutePath.Size)
            {
                char c1 = absPtr[matched];
                char c2 = rootPtr[matched];
                if (c1 == '/')
                {
                    c1 = '\\';
                }
                if (c2 == '/')
                {
                    c2 = '\\';
                }
                if (tolower(static_cast<unsigned char>(c1)) != tolower(static_cast<unsigned char>(c2)))
                {
                    break;
                }
                matched++;
            }

            if (matched == rootSize)
            {
                const char* rel = absPtr + rootSize;
                size_t newSize = absolutePath.Size - rootSize;
                if (*rel == '\\' || *rel == '/')
                {
                    rel++;
                    newSize--;
                }
                return { const_cast<char*>(rel), newSize };
            }
        }
        return absolutePath;
    }

    static Juliet::String BuildMarkdownFileName(Juliet::Arena* arena, const char* relativeBaseName, size_t baseLen)
    {
        char* buf = Juliet::ArenaPushArray<char>(arena, baseLen + 4 JULIET_DEBUG_PARAM("MdFileName"));
        for (size_t i = 0; i < baseLen; ++i)
        {
            char c = relativeBaseName[i];
            if (c == '\\' || c == '/' || c == ':')
            {
                buf[i] = '_';
            }
            else
            {
                buf[i] = c;
            }
        }
        buf[baseLen] = '.'; buf[baseLen+1] = 'm'; buf[baseLen+2] = 'd'; buf[baseLen+3] = '\0';
        return {buf, baseLen + 3};
    }

    static Juliet::String Trim(const char* start, const char* end)
    {
        if (end < start)
        {
            return {};
        }
        while (start < end && (*start == ' ' || *start == '\t' || *start == '\n' || *start == '\r'))
        {
            start++;
        }
        while (end > start && (*(end - 1) == ' ' || *(end - 1) == '\t' || *(end - 1) == '\n' || *(end - 1) == '\r'))
        {
            end--;
        }
        return { const_cast<char*>(start), static_cast<size_t>(end - start) };
    }

    static const char* SkipCommentsAndStrings(const char* p, const char* end)
    {
        while (p < end)
        {
            if (*p == '/' && p + 1 < end && *(p + 1) == '/')
            {
                p += 2;
                while (p < end && *p != '\n' && *p != '\r') { p++; }
            }
            else if (*p == '/' && p + 1 < end && *(p + 1) == '*')
            {
                p += 2;
                while (p < end && !(*p == '*' && p + 1 < end && *(p + 1) == '/')) { p++; }
                if (p < end) { p += 2; }
            }
            else if (*p == '"')
            {
                p++;
                while (p < end && *p != '"')
                {
                    if (*p == '\\') { p += 2; }
                    else { p++; }
                }
                if (p < end) { p++; }
            }
            else if (*p == '\'')
            {
                p++;
                while (p < end && *p != '\'')
                {
                    if (*p == '\\') { p += 2; }
                    else { p++; }
                }
                if (p < end) { p++; }
            }
            else
            {
                break;
            }
        }
        return p;
    }

    static const char* GetNextToken(const char* p, const char* end, Token& token)
    {
        p = SkipCommentsAndStrings(p, end);
        while (p < end && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r'))
        {
            p = SkipCommentsAndStrings(p + 1, end);
        }

        if (p >= end)
        {
            token.Start = nullptr;
            token.Length = 0;
            token.IsIdentifier = false;
            return end;
        }

        token.Start = p;

        char first = *p;
        if (isalpha(first) || first == '_' || first == '~')
        {
            token.IsIdentifier = true;
            p++;
            while (p < end && (isalnum(*p) || *p == '_' || *p == ':'))
            {
                p++;
            }
            token.Length = static_cast<size_t>(p - token.Start);
        }
        else
        {
            token.IsIdentifier = false;
            p++;
            token.Length = 1;
        }

        return p;
    }

    static bool IsForwardDeclaration(const char* p, const char* end)
    {
        p = SkipCommentsAndStrings(p, end);
        while (p < end)
        {
            char c = *p;
            if (c == ';')
            {
                return true;
            }
            if (c == '{' || c == ':')
            {
                return false;
            }
            p = SkipCommentsAndStrings(p + 1, end);
        }
        return false;
    }

    static const char* FindMatchingParen(const char* p, const char* end)
    {
        if (p == nullptr) { return nullptr; }
        int parenDepth = 1;
        p = SkipCommentsAndStrings(p, end);
        while (p < end && parenDepth > 0)
        {
            char c = *p;
            if (c == '(') { parenDepth++; }
            else if (c == ')') { parenDepth--; }
            p = SkipCommentsAndStrings(p + 1, end);
        }
        return p;
    }

    static bool IsKeyword(const Token& tk)
    {
        if (!tk.IsIdentifier) { return false; }
        const char* keywords[] = { "if", "for", "while", "switch", "catch", "template", "return", "throw", "sizeof", "decltype" };
        for (const char* kw : keywords)
        {
            if (tk.Length == strlen(kw) && strncmp(tk.Start, kw, tk.Length) == 0)
            {
                return true;
            }
        }
        return false;
    }

    static bool IsFunctionFollowup(const char* p, const char* end, bool& opensBody, bool& isConst)
    {
        if (p == nullptr) { return false; }
        p = SkipCommentsAndStrings(p, end);
        if (p >= end) { return false; }

        char c = *p;
        if (c == ';') { opensBody = false; return true; }
        if (c == '{') { opensBody = true; return true; }
        if (c == ':') { opensBody = true; return true; }

        Token t;
        const char* next = GetNextToken(p, end, t);
        if (t.Length > 0)
        {
            if (t.IsIdentifier)
            {
                const char* kw[] = { "const", "override", "final", "noexcept", "throw" };
                for (const char* k : kw)
                {
                    if (t.Length == strlen(k) && strncmp(t.Start, k, t.Length) == 0)
                    {
                        if (strcmp(k, "const") == 0) { isConst = true; }
                        return IsFunctionFollowup(next, end, opensBody, isConst);
                    }
                }
            }
            else if (t.Length == 1 && t.Start[0] == '=')
            {
                opensBody = false;
                return true;
            }
        }
        return false;
    }

    struct NamespaceScope
    {
        char Name[64];
        int OpenBraceDepth;
    };

    struct ClassScope
    {
        char Name[64];
        int OpenBraceDepth;
    };

    static void GetCurrentNamespace(char* buf, size_t maxLen, NamespaceScope* stack, int count)
    {
        buf[0] = '\0';
        size_t written = 0;
        for (int i = 0; i < count; ++i)
        {
            if (i > 0)
            {
                if (written + 2 < maxLen)
                {
                    strcat_s(buf, maxLen, "::");
                    written += 2;
                }
            }
            size_t nameLen = strlen(stack[i].Name);
            if (written + nameLen < maxLen)
            {
                strcat_s(buf, maxLen, stack[i].Name);
                written += nameLen;
            }
        }
    }

    static void GetCurrentClassContext(char* buf, size_t maxLen, ClassScope* stack, int count)
    {
        buf[0] = '\0';
        size_t written = 0;
        for (int i = 0; i < count; ++i)
        {
            if (i > 0)
            {
                if (written + 2 < maxLen)
                {
                    strcat_s(buf, maxLen, "::");
                    written += 2;
                }
            }
            size_t nameLen = strlen(stack[i].Name);
            if (written + nameLen < maxLen)
            {
                strcat_s(buf, maxLen, stack[i].Name);
                written += nameLen;
            }
        }
    }

    static void ParseSymbolsFromFile(
        Juliet::Arena* arena,
        const Juliet::String& filePath,
        Juliet::VectorArena<ParsedSymbol, 128>& outSymbols
    )
    {
        Assert(arena != nullptr);
        
        char pathBuf[512];
        size_t copyLen = filePath.Size < 511 ? filePath.Size : 511;
        memcpy(pathBuf, filePath.Data, copyLen);
        pathBuf[copyLen] = '\0';

        FILE* srcFile = nullptr;
        if (fopen_s(&srcFile, pathBuf, "rb") != 0 || !srcFile) { return; }

        fseek(srcFile, 0, SEEK_END);
        long fileSize = ftell(srcFile);
        fseek(srcFile, 0, SEEK_SET);

        if (fileSize <= 0)
        {
            fclose(srcFile);
            return;
        }

        char* buffer = Juliet::ArenaPushArray<char>(arena, static_cast<size_t>(fileSize) + 1 JULIET_DEBUG_PARAM("SourceFileBuffer"));
        size_t readBytes = fread(buffer, 1, static_cast<size_t>(fileSize), srcFile);
        buffer[readBytes] = '\0';
        fclose(srcFile);

        const char* start = buffer;
        const char* end = buffer + readBytes;
        const char* cursor = start;

        Token tokenHistory[8] = {};
        
        NamespaceScope namespaceStack[16] = {};
        int namespaceStackCount = 0;

        ClassScope classStack[16] = {};
        int classStackCount = 0;

        char pendingNamespace[64] = "";
        char pendingClass[64] = "";
        char pendingClassType[16] = "";

        int braceDepth = 0;
        bool inFunctionBody = false;
        int functionStartBraceDepth = -1;

        const char* lastStatementEnd = start;

        Token t;
        while (cursor < end)
        {
            cursor = GetNextToken(cursor, end, t);
            if (t.Length == 0) { break; }

            // Shift token history
            for (int i = 7; i > 0; --i)
            {
                tokenHistory[i] = tokenHistory[i - 1];
            }
            tokenHistory[0] = t;

            if (t.IsIdentifier)
            {
                if (strncmp(t.Start, "namespace", t.Length) == 0 && !inFunctionBody)
                {
                    Token nameT;
                    cursor = GetNextToken(cursor, end, nameT);
                    if (nameT.Length > 0 && nameT.IsIdentifier)
                    {
                        size_t copySize = nameT.Length < 63 ? nameT.Length : 63;
                        memcpy(pendingNamespace, nameT.Start, copySize);
                        pendingNamespace[copySize] = '\0';
                    }
                    else
                    {
                        strcpy_s(pendingNamespace, "anonymous");
                        // backtracking cursor if we read a brace or something else
                        cursor = nameT.Start;
                    }
                }
                else if ((strncmp(t.Start, "class", t.Length) == 0 || 
                          strncmp(t.Start, "struct", t.Length) == 0 || 
                          strncmp(t.Start, "union", t.Length) == 0) && !inFunctionBody)
                {
                    // Check if friend declaration
                    bool isFriend = false;
                    if (tokenHistory[1].Length == 6 && strncmp(tokenHistory[1].Start, "friend", 6) == 0)
                    {
                        isFriend = true;
                    }

                    if (!isFriend)
                    {
                        size_t typeLen = t.Length < 15 ? t.Length : 15;
                        memcpy(pendingClassType, t.Start, typeLen);
                        pendingClassType[typeLen] = '\0';

                        Token nameT;
                        cursor = GetNextToken(cursor, end, nameT);
                        
                        // Handle API export macros (e.g. JULIET_API) by skipping them
                        if (nameT.Length > 0 && nameT.IsIdentifier && 
                            nameT.Length > 4 && strncmp(nameT.Start + nameT.Length - 4, "_API", 4) == 0)
                        {
                            cursor = GetNextToken(cursor, end, nameT);
                        }

                        if (nameT.Length > 0 && nameT.IsIdentifier)
                        {
                            if (!IsForwardDeclaration(nameT.Start + nameT.Length, end))
                            {
                                size_t copySize = nameT.Length < 63 ? nameT.Length : 63;
                                memcpy(pendingClass, nameT.Start, copySize);
                                pendingClass[copySize] = '\0';
                            }
                        }
                        else
                        {
                            strcpy_s(pendingClass, "anonymous");
                            cursor = nameT.Start;
                        }
                    }
                }
                else if (strncmp(t.Start, "enum", t.Length) == 0 && !inFunctionBody)
                {
                    Token classT;
                    const char* nextCursor = GetNextToken(cursor, end, classT);
                    bool isEnumClass = false;
                    if (classT.Length == 5 && strncmp(classT.Start, "class", 5) == 0)
                    {
                        isEnumClass = true;
                        cursor = nextCursor;
                    }

                    Token nameT;
                    cursor = GetNextToken(cursor, end, nameT);
                    if (nameT.Length > 0 && nameT.IsIdentifier)
                    {
                        ParsedSymbol sym = {};
                        sym.Name = Juliet::StringCopy(arena, { const_cast<char*>(nameT.Start), nameT.Length });
                        if (isEnumClass)
                        {
                            sym.Type = ConstString("enum class");
                        }
                        else
                        {
                            sym.Type = ConstString("enum");
                        }
                        
                        char nsBuf[128];
                        GetCurrentNamespace(nsBuf, sizeof(nsBuf), namespaceStack, namespaceStackCount);
                        sym.Namespace = Juliet::StringCopy(arena, Juliet::WrapString(nsBuf));

                        char clBuf[128];
                        GetCurrentClassContext(clBuf, sizeof(clBuf), classStack, classStackCount);
                        sym.ClassContext = Juliet::StringCopy(arena, Juliet::WrapString(clBuf));

                        outSymbols.PushBack(sym);
                    }
                }
                else if ((strncmp(t.Start, "public", t.Length) == 0 || 
                          strncmp(t.Start, "private", t.Length) == 0 || 
                          strncmp(t.Start, "protected", t.Length) == 0) && !inFunctionBody)
                {
                    // Check for trailing colon
                    Token colonT;
                    const char* tempC = GetNextToken(cursor, end, colonT);
                    if (colonT.Length == 1 && colonT.Start[0] == ':')
                    {
                        cursor = tempC;
                        lastStatementEnd = cursor;
                    }
                }
            }
            else if (t.Length == 1)
            {
                char sym = t.Start[0];
                if (sym == '{')
                {
                    braceDepth++;
                    if (!inFunctionBody)
                    {
                        if (pendingNamespace[0] != '\0' && namespaceStackCount < 16)
                        {
                            strcpy_s(namespaceStack[namespaceStackCount].Name, pendingNamespace);
                            namespaceStack[namespaceStackCount].OpenBraceDepth = braceDepth;
                            namespaceStackCount++;
                            pendingNamespace[0] = '\0';
                        }
                        else if (pendingClass[0] != '\0' && classStackCount < 16)
                        {
                            strcpy_s(classStack[classStackCount].Name, pendingClass);
                            classStack[classStackCount].OpenBraceDepth = braceDepth;
                            classStackCount++;

                            // Also store the class itself as a symbol!
                            ParsedSymbol classSym = {};
                            classSym.Name = Juliet::StringCopy(arena, Juliet::WrapString(classStack[classStackCount-1].Name));
                            classSym.Type = Juliet::StringCopy(arena, Juliet::WrapString(pendingClassType));

                            char nsBuf[128];
                            GetCurrentNamespace(nsBuf, sizeof(nsBuf), namespaceStack, namespaceStackCount);
                            classSym.Namespace = Juliet::StringCopy(arena, Juliet::WrapString(nsBuf));

                            char clBuf[128];
                            // Get class context *excluding* the one we just pushed
                            GetCurrentClassContext(clBuf, sizeof(clBuf), classStack, classStackCount - 1);
                            classSym.ClassContext = Juliet::StringCopy(arena, Juliet::WrapString(clBuf));

                            outSymbols.PushBack(classSym);

                            pendingClass[0] = '\0';
                        }
                        else
                        {
                            inFunctionBody = true;
                            functionStartBraceDepth = braceDepth;
                        }
                    }
                    lastStatementEnd = cursor;
                }
                else if (sym == '}')
                {
                    if (inFunctionBody)
                    {
                        if (braceDepth == functionStartBraceDepth)
                        {
                            inFunctionBody = false;
                            functionStartBraceDepth = -1;
                        }
                    }
                    else
                    {
                        if (classStackCount > 0 && classStack[classStackCount - 1].OpenBraceDepth == braceDepth)
                        {
                            classStackCount--;
                        }
                        else if (namespaceStackCount > 0 && namespaceStack[namespaceStackCount - 1].OpenBraceDepth == braceDepth)
                        {
                            namespaceStackCount--;
                        }
                    }
                    braceDepth--;
                    lastStatementEnd = cursor;
                }
                else if (sym == ';')
                {
                    pendingNamespace[0] = '\0';
                    pendingClass[0] = '\0';
                    lastStatementEnd = cursor;
                }
                else if (sym == '(' && !inFunctionBody)
                {
                    Token funcNameT = tokenHistory[1];
                    if (funcNameT.Length > 0 && funcNameT.IsIdentifier && !IsKeyword(funcNameT))
                    {
                        const char* matchingParen = FindMatchingParen(t.Start + 1, end);
                        
                        bool opensBody = false;
                        bool isConst = false;
                        if (matchingParen != nullptr && IsFunctionFollowup(matchingParen, end, opensBody, isConst))
                        {
                            Juliet::String returnTypeRaw = Trim(lastStatementEnd, funcNameT.Start);
                            Juliet::String paramsRaw = Trim(t.Start + 1, matchingParen - 1);

                            char activeClass[128] = "";
                            GetCurrentClassContext(activeClass, sizeof(activeClass), classStack, classStackCount);
                            
                            bool hasReturnType = (returnTypeRaw.Size > 0);
                            bool isConstructorOrDestructor = false;
                            
                            if (classStackCount > 0)
                            {
                                const char* className = classStack[classStackCount - 1].Name;
                                size_t cNameLen = strlen(className);
                                
                                if (funcNameT.Length == cNameLen && strncmp(funcNameT.Start, className, cNameLen) == 0)
                                {
                                    isConstructorOrDestructor = true;
                                }
                                else if (funcNameT.Length == cNameLen + 1 && funcNameT.Start[0] == '~' && 
                                         strncmp(funcNameT.Start + 1, className, cNameLen) == 0)
                                {
                                    isConstructorOrDestructor = true;
                                }
                            }

                            if (hasReturnType || isConstructorOrDestructor)
                            {
                                ParsedSymbol funcSym = {};
                                funcSym.Name = Juliet::StringCopy(arena, { const_cast<char*>(funcNameT.Start), funcNameT.Length });
                                funcSym.ReturnType = Juliet::StringCopy(arena, returnTypeRaw);
                                funcSym.Parameters = Juliet::StringCopy(arena, paramsRaw);
                                funcSym.Type = ConstString("function");
                                funcSym.IsConst = isConst;

                                char nsBuf[128];
                                GetCurrentNamespace(nsBuf, sizeof(nsBuf), namespaceStack, namespaceStackCount);
                                funcSym.Namespace = Juliet::StringCopy(arena, Juliet::WrapString(nsBuf));

                                funcSym.ClassContext = Juliet::StringCopy(arena, Juliet::WrapString(activeClass));

                                outSymbols.PushBack(funcSym);
                            }
                        }
                    }
                }
            }
        }
    }

    static void FormatAndWriteSymbols(StringBuilder& sb, Juliet::Arena* arena, const Juliet::String& filePath)
    {
        printf("Romeo: FormatAndWriteSymbols start for %.*s\n", static_cast<int>(filePath.Size), CStr(filePath));
        Juliet::VectorArena<ParsedSymbol, 128> symbols = {};
        symbols.Create(arena JULIET_DEBUG_ONLY(, "ParsedSymbols"));

        printf("Romeo: Calling ParseSymbolsFromFile...\n");
        ParseSymbolsFromFile(arena, filePath, symbols);
        printf("Romeo: ParseSymbolsFromFile returned, symbols count: %zu\n", symbols.Size());

        if (symbols.Size() == 0)
        {
            sb.Append("*No symbols detected.*\n");
            return;
        }

        // Collect all distinct namespaces
        printf("Romeo: Collecting namespaces...\n");
        Juliet::VectorArena<Juliet::String, 16> namespaces = {};
        namespaces.Create(arena JULIET_DEBUG_ONLY(, "NamespacesList"));

        for (const ParsedSymbol& sym : symbols)
        {
            bool found = false;
            for (const Juliet::String& ns : namespaces)
            {
                if (Juliet::StringCompare(ns, sym.Namespace) == 0)
                {
                    found = true;
                    break;
                }
            }
            if (!found)
            {
                namespaces.PushBack(sym.Namespace);
            }
        }
        printf("Romeo: Namespace collection done.\n");

        for (const Juliet::String& ns : namespaces)
        {
            printf("Romeo: Formatting namespace %.*s...\n", static_cast<int>(ns.Size), CStr(ns));
            if (ns.Size > 0)
            {
                sb.Append("### Namespace `%.*s`\n\n", static_cast<int>(ns.Size), CStr(ns));
            }
            else
            {
                sb.Append("### Global Scope\n\n");
            }

            // Print Structs/Classes/Unions
            bool hasTypes = false;
            for (const ParsedSymbol& sym : symbols)
            {
                if (Juliet::StringCompare(sym.Namespace, ns) == 0 && 
                    (Juliet::StringCompare(sym.Type, ConstString("class")) == 0 || 
                     Juliet::StringCompare(sym.Type, ConstString("struct")) == 0 ||
                     Juliet::StringCompare(sym.Type, ConstString("union")) == 0))
                {
                    if (!hasTypes)
                    {
                        sb.Append("#### Classes, Structs & Unions\n");
                        hasTypes = true;
                    }
                    if (sym.ClassContext.Size > 0)
                    {
                        sb.Append("- `%.*s %.*s::%.*s`\n", 
                                static_cast<int>(sym.Type.Size), CStr(sym.Type),
                                static_cast<int>(sym.ClassContext.Size), CStr(sym.ClassContext),
                                static_cast<int>(sym.Name.Size), CStr(sym.Name));
                    }
                    else
                    {
                        sb.Append("- `%.*s %.*s`\n", 
                                static_cast<int>(sym.Type.Size), CStr(sym.Type),
                                static_cast<int>(sym.Name.Size), CStr(sym.Name));
                    }
                }
            }
            if (hasTypes) { sb.Append("\n"); }

            // Print Enums
            bool hasEnums = false;
            for (const ParsedSymbol& sym : symbols)
            {
                if (Juliet::StringCompare(sym.Namespace, ns) == 0 && 
                    (Juliet::StringCompare(sym.Type, ConstString("enum")) == 0 || 
                     Juliet::StringCompare(sym.Type, ConstString("enum class")) == 0))
                {
                    if (!hasEnums)
                    {
                        sb.Append("#### Enums\n");
                        hasEnums = true;
                    }
                    if (sym.ClassContext.Size > 0)
                    {
                        sb.Append("- `%.*s %.*s::%.*s`\n", 
                                static_cast<int>(sym.Type.Size), CStr(sym.Type),
                                static_cast<int>(sym.ClassContext.Size), CStr(sym.ClassContext),
                                static_cast<int>(sym.Name.Size), CStr(sym.Name));
                    }
                    else
                    {
                        sb.Append("- `%.*s %.*s`\n", 
                                static_cast<int>(sym.Type.Size), CStr(sym.Type),
                                static_cast<int>(sym.Name.Size), CStr(sym.Name));
                    }
                }
            }
            if (hasEnums) { sb.Append("\n"); }

            // Print Functions/Methods
            bool hasFuncs = false;
            for (const ParsedSymbol& sym : symbols)
            {
                if (Juliet::StringCompare(sym.Namespace, ns) == 0 && 
                    Juliet::StringCompare(sym.Type, ConstString("function")) == 0)
                {
                    if (!hasFuncs)
                    {
                        sb.Append("#### Functions & Methods\n");
                        hasFuncs = true;
                    }
                    
                    sb.Append("- `");
                    if (sym.ReturnType.Size > 0)
                    {
                        sb.Append("%.*s ", static_cast<int>(sym.ReturnType.Size), CStr(sym.ReturnType));
                    }
                    if (sym.ClassContext.Size > 0)
                    {
                        sb.Append("%.*s::", static_cast<int>(sym.ClassContext.Size), CStr(sym.ClassContext));
                    }
                    sb.Append("%.*s(%.*s)%s`\n", 
                            static_cast<int>(sym.Name.Size), CStr(sym.Name),
                            static_cast<int>(sym.Parameters.Size), CStr(sym.Parameters),
                            sym.IsConst ? " const" : "");
                }
            }
            if (hasFuncs) { sb.Append("\n"); }
        }
    }

    void MD_GenerateDocumentation(Database& db, const Juliet::String& workspaceRoot, const Juliet::String& docsDir)
    {
        EnsureDirectoryExists(CStr(docsDir));

        // Clean up any stale broken C__*.md files from prior runs
        {
            char searchPattern[512];
            snprintf(searchPattern, sizeof(searchPattern), "%s\\C__*.md", CStr(docsDir));

            WIN32_FIND_DATAA findData;
            HANDLE hFind = FindFirstFileA(searchPattern, &findData);
            if (hFind != INVALID_HANDLE_VALUE)
            {
                do
                {
                    char filePath[512];
                    snprintf(filePath, sizeof(filePath), "%s\\%s", CStr(docsDir), findData.cFileName);
                    DeleteFileA(filePath);
                } while (FindNextFileA(hFind, &findData) != 0);
                FindClose(hFind);
            }
        }

        char overviewPath[512];
        snprintf(overviewPath, sizeof(overviewPath), "%s\\overview.md", CStr(docsDir));

        FILE* overviewFile = nullptr;
        if (fopen_s(&overviewFile, overviewPath, "w") != 0 || !overviewFile)
        {
            return;
        }

        fprintf(overviewFile, "# Juliet C++ Codebase Overview\n\n");
        fprintf(overviewFile, "This is an auto-generated index of all C++ sources in the workspace.\n\n");
        fprintf(overviewFile, "## Files\n\n");

        FILETIME ft;
        GetSystemTimeAsFileTime(&ft);
        uint64_t currentTime = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;

        // Temporary arena for parsing symbols and formatting
        Juliet::ArenaParams params{};
        Juliet::Arena* tempArena = Juliet::ArenaAllocate(params JULIET_DEBUG_PARAM("MdGeneratorTempArena"));
        Assert(tempArena != nullptr);

        // Separate arena for AI generation (gets cleared after each file to prevent exhaustion)
        Juliet::ArenaParams aiParams{};
        Juliet::Arena* aiArena = Juliet::ArenaAllocate(aiParams JULIET_DEBUG_PARAM("AiGeneratorArena"));
        Assert(aiArena != nullptr);

        size_t fileIndex = 0;
        size_t totalFiles = db.Entries.Size();

        for (FileEntry& entry : db.Entries)
        {
            fileIndex++;
            const Juliet::String& primaryPath = (entry.HeaderPath.Size > 0) ? entry.HeaderPath : entry.CppPath;
            if (primaryPath.Size == 0)
            {
                continue;
            }

            bool hasCpp = (entry.CppPath.Size > 0);
            bool isHeaderInSrc = (entry.HeaderPath.Size > 0 && strstr(CStr(entry.HeaderPath), "\\src\\") != nullptr);
            if (isHeaderInSrc && !hasCpp)
            {
                entry.LastMarkdownCreatedTime = currentTime;
                continue;
            }

            Juliet::String relStr = GetRelativeString(primaryPath, workspaceRoot);
            
            char baseName[512];
            size_t copyLen = relStr.Size < 511 ? relStr.Size : 511;
            memcpy(baseName, relStr.Data, copyLen);
            baseName[copyLen] = '\0';

            size_t baseLen = strlen(baseName);
            if (baseLen > 4 && strcmp(baseName + baseLen - 4, ".cpp") == 0)
            {
                baseName[baseLen - 4] = '\0';
                baseLen -= 4;
            }
            if (baseLen > 2 && strcmp(baseName + baseLen - 2, ".h") == 0)
            {
                baseName[baseLen - 2] = '\0';
                baseLen -= 2;
            }

            Juliet::String mdFileName = BuildMarkdownFileName(db.DbArena, baseName, baseLen);

            fprintf(overviewFile, "- [%s](%s)\n", baseName, CStr(mdFileName));

            bool needsUpdate = (entry.LastMarkdownCreatedTime == 0) ||
                               (entry.Description.Size == 0) ||
                               (entry.LastModifiedHeader > entry.LastMarkdownCreatedTime) ||
                               (entry.LastModifiedCpp > entry.LastMarkdownCreatedTime);

            if (needsUpdate)
            {
                bool aiNeedsUpdate = false;
                if (entry.Description.Size == 0)
                {
                    aiNeedsUpdate = true;
                }
                else if (entry.HeaderPath.Size > 0)
                {
                    if (entry.LastModifiedHeader > entry.LastAIGeneratedTime)
                    {
                        aiNeedsUpdate = true;
                    }
                }
                else if (entry.CppPath.Size > 0)
                {
                    if (entry.LastModifiedCpp > entry.LastAIGeneratedTime)
                    {
                        aiNeedsUpdate = true;
                    }
                }

                if (aiNeedsUpdate)
                {
                    printf("Romeo: [%zu/%zu] Generating AI description for '%s'...\n",
                           fileIndex, totalFiles, baseName);

                    Juliet::ArenaClear(aiArena);
                    if (AI_GenerateDescription(aiArena, entry))
                    {
                        // Copy the description to the persistent db arena so it survives aiArena clear
                        entry.Description = Juliet::StringCopy(db.DbArena, entry.Description);
                        entry.LastAIGeneratedTime = currentTime;
                        printf("Romeo: [%zu/%zu] Done.\n", fileIndex, totalFiles);
                    }
                    else
                    {
                        entry.LastAIGeneratedTime = currentTime;
                        printf("Romeo: [%zu/%zu] AI generation failed.\n", fileIndex, totalFiles);
                    }
                }

                Juliet::ArenaClear(tempArena);

                char mdFilePath[512];
                snprintf(mdFilePath, sizeof(mdFilePath), "%s\\%s", CStr(docsDir), CStr(mdFileName));

                StringBuilder sb = { tempArena };
                sb.Append("# %s\n\n", baseName);
                
                sb.Append("## Source Files\n");
                if (entry.HeaderPath.Size > 0)
                {
                    Juliet::String hRel = GetRelativeString(entry.HeaderPath, workspaceRoot);
                    sb.Append("- Header: `%.*s`\n", static_cast<int>(hRel.Size), hRel.Data);
                }
                if (entry.CppPath.Size > 0)
                {
                    Juliet::String cRel = GetRelativeString(entry.CppPath, workspaceRoot);
                    sb.Append("- Source: `%.*s`\n", static_cast<int>(cRel.Size), cRel.Data);
                }
                sb.Append("\n");

                sb.Append("## AI Description\n");
                if (entry.Description.Size > 0)
                {
                    sb.Append("%.*s\n", static_cast<int>(entry.Description.Size), entry.Description.Data);
                }
                else
                {
                    sb.Append("*No AI description generated yet.*\n");
                }
                sb.Append("\n");

                sb.Append("## Symbols\n\n");
                printf("Romeo: [%zu/%zu] Parsing symbols...\n", fileIndex, totalFiles);
                if (entry.HeaderPath.Size > 0)
                {
                    FormatAndWriteSymbols(sb, tempArena, entry.HeaderPath);
                }
                else if (entry.CppPath.Size > 0)
                {
                    FormatAndWriteSymbols(sb, tempArena, entry.CppPath);
                }
                printf("Romeo: [%zu/%zu] Symbols done.\n", fileIndex, totalFiles);

                // Compare with existing file to decide if we write to disk
                Juliet::String existing = ReadFileContent(tempArena, mdFilePath);
                Juliet::String newContent = { sb.Buffer, sb.Size };
                if (existing.Size == 0 || Juliet::StringCompare(existing, newContent) != 0)
                {
                    FILE* mdFile = nullptr;
                    if (fopen_s(&mdFile, mdFilePath, "wb") == 0 && mdFile)
                    {
                        fwrite(sb.Buffer, 1, sb.Size, mdFile);
                        fclose(mdFile);
                        printf("Romeo: [%zu/%zu] File written/updated.\n", fileIndex, totalFiles);
                    }
                }
                else
                {
                    printf("Romeo: [%zu/%zu] File unchanged. No modification made.\n", fileIndex, totalFiles);
                }
                entry.LastMarkdownCreatedTime = currentTime;
            }
        }

        printf("Rome: Done processing batch\n");

        Juliet::ArenaRelease(aiArena);
        Juliet::ArenaRelease(tempArena);
        fclose(overviewFile);
    }
}
