#include <iostream>
#include <string>
#include <Windows.h>
#include <ctime>
#include <sstream>
#include <iomanip>
#include <array>
#include <string_view>
#include <mutex>
#include <chrono>

extern int isDebug; // 0 = OFF, 1 = ON

std::string colorOut_256(std::string_view s, int ForeColor = 7)
{
    std::ostringstream oss;
    oss << "\033[38;5;" << ForeColor << "m" << s << "\033[m";
    return oss.str();
}

std::string getSystemTime() {
    using namespace std::chrono;
    const auto now = system_clock::now();
    const auto now_time_t = system_clock::to_time_t(now);
    const auto ms = duration_cast<milliseconds>(now.time_since_epoch()).count() % 1000;

    std::tm ltm{};
    localtime_s(&ltm, &now_time_t);
    std::ostringstream oss;
    oss << std::put_time(&ltm, "%Y-%m-%d %H:%M:%S")
        << '.' << std::setw(3) << std::setfill('0') << ms;
    return oss.str();
}

// Logger mutex to ensure whole log lines are emitted atomically
static std::mutex loggerMutex;

void postLog(const std::string& message, int level) {
    const auto timeNow = getSystemTime();

    constexpr std::array<const char*, 5> levelNames = {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"};
    constexpr std::array<int, 5> levelColors = {34, 27, 220, 196, 124};

    const int idx = (level >= 0 && level < static_cast<int>(levelNames.size())) ? level : 1; // default to INFO

    // Lock to make reading isDebug and all output atomic across threads
    std::scoped_lock lock(loggerMutex);

    if (isDebug == 0) {
        if (idx == 0) return; // skip DEBUG when debug is off
        std::cout << "[" << timeNow << " - " << levelNames[idx] << "] " << message << "\n";
        return;
    }

    // isDebug == 1 -> colored output
    const std::string levelDisplay = colorOut_256(levelNames[idx], levelColors[idx]);
    std::ostream& out = (idx >= 3) ? static_cast<std::ostream&>(std::cerr) : static_cast<std::ostream&>(std::cout);
    out << "[" << timeNow << " - " << levelDisplay << "] " << message << "\n";
}