#include "Logging.h"
#include <imgui.h>
#include <cstdarg>
#include <cstdio>
#include <iostream>

// Logger.cpp or a logger helper header
#include <fstream>
#include <filesystem>
#include <windows.h>
#include <mutex>
#include <dbghelp.h>

class ScopedLogFileWriter
{
public:
    ScopedLogFileWriter()
    {
        // Resolve user folder
        char userPath[MAX_PATH];
        DWORD len = GetEnvironmentVariableA("USERPROFILE", userPath, MAX_PATH);
        if (len == 0 || len >= MAX_PATH)
            return;

        std::filesystem::path logPath = std::filesystem::path(userPath) / ".CreateEngine";
        std::filesystem::create_directories(logPath);
        logPath /= ".latest.log";

        stream.open(logPath, std::ios::out | std::ios::app); // append mode
    }

    ~ScopedLogFileWriter()
    {
        if (stream.is_open())
            stream.close();
    }

    std::ofstream &Get() { return stream; }

private:
    std::ofstream stream;
};

std::vector<Logger::LogMessage> Logger::s_Messages;
bool Logger::s_AutoScroll = true;
bool Logger::s_ShowOk = true;
bool Logger::s_ShowInfo = true;
bool Logger::s_ShowWarning = true;
bool Logger::s_ShowError = true;
bool Logger::s_ShowDebug = false;
bool Logger::s_ShowVerbose = false;

const char *Logger::ToString(Level level)
{
    switch (level)
    {
    case Ok:
        return "Ok";
    case Info:
        return "Info";
    case Warning:
        return "Warning";
    case Error:
        return "Error";
    case Debug:
        return "Debug";
    case Verbose:
        return "Verbose";
    default:
        return "Unknown";
    }
}

const char *Logger::GetAnsiColor(Level level)
{
    switch (level)
    {
    case Ok:
        return "\033[1;32m"; // Bold green
    case Info:
        return "\033[0;37m"; // Gray
    case Warning:
        return "\033[1;33m"; // Yellow
    case Error:
        return "\033[1;31m"; // Red
    case Debug:
        return "\033[1;36m"; // Cyan
    case Verbose:
        return "\033[0;90m"; // Dim gray
    default:
        return "\033[0m"; // Reset
    }
}

void Logger::Log(Level level, const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    LogVA(level, fmt, args);
    va_end(args);
}

void Logger::LogVA(Level level, const char *fmt, va_list args)
{
    char buffer[1024];
    vsnprintf(buffer, sizeof(buffer), fmt, args);
    s_Messages.push_back({buffer, level});

    const char *levelStr = ToString(level);
    const char *ansiColor = GetAnsiColor(level);

    std::string formatted = std::string("[Logger][") + levelStr + "] " + buffer;

    std::cout << ansiColor << formatted << "\033[0m\n";

    ScopedLogFileWriter logFile;
    auto &out = logFile.Get();
    if (out.is_open())
        out << formatted << "\n";
}

void Logger::LogOk(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    LogVA(Ok, fmt, args);
    va_end(args);
}

void Logger::LogInfo(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    LogVA(Info, fmt, args);
    va_end(args);
}

void Logger::LogWarning(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    LogVA(Warning, fmt, args);
    va_end(args);
}

void Logger::LogError(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    LogVA(Error, fmt, args);
    va_end(args);
}

void Logger::LogDebug(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    LogVA(Debug, fmt, args);
    va_end(args);
}

void Logger::LogVerbose(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    LogVA(Verbose, fmt, args);
    va_end(args);
}

void Logger::LogDepricated(const char *fmt, ...)
{
    va_list args;
    va_start(args, fmt);

    char buffer[1024];
    vsnprintf(buffer, sizeof(buffer), fmt, args);

    std::string full = std::string("[DEPRECATED] ") + buffer;
    LogVA(Warning, full.c_str(), args);

    va_end(args);
}

void Logger::LogDepricatedWithCaller(const char *msg, const std::source_location location)
{
    std::string formatted = std::string("[DEPRECATED] ") + msg +
                            " (called from " + location.file_name() +
                            ":" + std::to_string(location.line()) +
                            " in " + location.function_name() + ")";
    Log(Warning, "%s", formatted.c_str());
}

void Logger::LogDepricatedWithStackTrace(const char *msg)
{
    static std::once_flag symInitFlag;
    std::call_once(symInitFlag, []()
                   {
        SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME);
        SymInitialize(GetCurrentProcess(), nullptr, TRUE); });

    HANDLE process = GetCurrentProcess();
    void *stack[32];
    USHORT frames = CaptureStackBackTrace(0, 32, stack, nullptr);

    std::string formatted = std::string("[DEPRECATED] ") + msg + "\n  Call stack:";

    for (USHORT i = 1; i < frames; ++i) // skip self
    {
        DWORD64 addr = (DWORD64)(stack[i]);

        // Resolve function name
        char symbolBuffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME];
        PSYMBOL_INFO symbol = (PSYMBOL_INFO)symbolBuffer;
        symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
        symbol->MaxNameLen = MAX_SYM_NAME;

        std::string line;

        if (SymFromAddr(process, addr, nullptr, symbol))
        {
            line = std::string("    -> ") + symbol->Name;
        }
        else
        {
            // fallback: raw address
            char addrStr[64];
            snprintf(addrStr, sizeof(addrStr), "0x%llx", (unsigned long long)addr);
            line = std::string("    -> [unresolved symbol] ") + addrStr;
        }

        // Resolve file + line number
        IMAGEHLP_LINE64 lineInfo = {};
        lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
        DWORD lineDisp = 0;

        if (SymGetLineFromAddr64(process, addr, &lineDisp, &lineInfo))
        {
            line += " (" + std::string(lineInfo.FileName) + ":" + std::to_string(lineInfo.LineNumber) + ")";
        }

        formatted += "\n" + line;
    }

    Log(Warning, "%s", formatted.c_str());
}

void Logger::Clear()
{
    s_Messages.clear();
}

void Logger::Draw(const char *title)
{
    ImGui::Begin(title);

    if (ImGui::Button("Clear"))
        Clear();
    ImGui::SameLine();
    ImGui::Checkbox("Auto-scroll", &s_AutoScroll);
    ImGui::Separator();

    // New Ok checkbox
    ImGui::Checkbox("Ok", &s_ShowOk);
    ImGui::SameLine();
    ImGui::Checkbox("Info", &s_ShowInfo);
    ImGui::SameLine();
    ImGui::Checkbox("Warning", &s_ShowWarning);
    ImGui::SameLine();
    ImGui::Checkbox("Error", &s_ShowError);
    ImGui::SameLine();
    ImGui::Checkbox("Debug", &s_ShowDebug);
    ImGui::SameLine();
    ImGui::Checkbox("Verbose", &s_ShowVerbose);
    ImGui::Separator();

    ImGui::BeginChild("LogMessages");

    // Filter
    std::vector<const LogMessage *> filtered;
    for (auto &m : s_Messages)
    {
        if ((m.level == Ok && !s_ShowOk) ||
            (m.level == Info && !s_ShowInfo) ||
            (m.level == Warning && !s_ShowWarning) ||
            (m.level == Error && !s_ShowError) ||
            (m.level == Debug && !s_ShowDebug) ||
            (m.level == Verbose && !s_ShowVerbose))
            continue;
        filtered.push_back(&m);
    }

    ImGuiListClipper clipper;
    clipper.Begin((int)filtered.size());
    while (clipper.Step())
    {
        for (int idx = clipper.DisplayStart; idx < clipper.DisplayEnd; idx++)
        {
            auto msg = filtered[idx];
            ImVec4 col;
            switch (msg->level)
            {
            case Ok:
                col = ImVec4(0.6f, 1.0f, 0.6f, 1.0f);
                break;
            case Info:
                col = ImVec4(0.8f, 0.8f, 0.8f, 1.0f);
                break;
            case Warning:
                col = ImVec4(1.0f, 0.85f, 0.3f, 1.0f);
                break;
            case Error:
                col = ImVec4(1.0f, 0.3f, 0.3f, 1.0f);
                break;
            case Debug:
                col = ImVec4(0.3f, 0.9f, 1.0f, 1.0f);
                break;
            case Verbose:
                col = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
                break;
            default:
                col = ImVec4(1, 1, 1, 1);
                break;
            }

            ImGui::PushStyleColor(ImGuiCol_Text, col);
            ImGui::Text("[%s] %s", ToString(msg->level), msg->text.c_str());
            ImGui::PopStyleColor();
        }
    }
    clipper.End();

    if (s_AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY())
        ImGui::SetScrollHereY(1.0f);

    ImGui::EndChild();
    ImGui::End();
}