//
// Created by marob on 12/27/2023.
//

#ifndef LOGGER_H
#define LOGGER_H
#include "ISXMr.h"
#include <sstream>
#include <iostream>
#include <iomanip>

enum class LogLevel {
    Debug,
    Info,
    Warning,
    Error
};

class Logger {
public:
    Logger(const Logger &) = delete;

    Logger &operator=(const Logger &) = delete;

    static Logger &instance() {
        static Logger singleton;
        return singleton;
    }

    void SetLogLevel(LogLevel level) {
        currentLogLevel = level;
    }

    Logger &operator<<(LogLevel level) {
        SetLogLevel(level);
        return *this;
    }

    template<typename T>
    Logger &operator<<(const T &value) {
        std::ostringstream &tss = getThreadLocalStream();
        tss << value;
        return *this;
    }

    Logger &operator<<(std::ostream & (*pf)(std::ostream &)) {
        std::ostringstream &tss = getThreadLocalStream();
        pf(tss);
        if (pf == static_cast<std::ostream& (*)(std::ostream &)>(std::endl)) {
            string str = tss.str();
            pISInterface->Printf(formatMessage(str).c_str());
            tss.str("");
            tss.clear();
        }
        return *this;
    }

private:
    Logger() = default;

    LogLevel currentLogLevel = LogLevel::Info;

    static std::ostringstream &getThreadLocalStream() {
        thread_local std::ostringstream tss;
        return tss;
    }

    static void rtrim_newlines(std::string &s) {
        s.erase(std::find_if(s.rbegin(), s.rend(), [](const unsigned char ch) {
            return ch != '\n' && ch != '\r';
        }).base(), s.end());
    }

    std::string formatMessage(std::string &message) const {
        const auto now = std::chrono::system_clock::now();
        const auto now_c = std::chrono::system_clock::to_time_t(now);

        std::tm now_tm = {};
        localtime_s(&now_tm, &now_c); // Thread-safe on Windows

        std::ostringstream oss;
        rtrim_newlines(message);
        oss << "[" << std::put_time(&now_tm, "%H:%M:%S") << "] - " << getColorCode(currentLogLevel) << message << "\ax";
        // Reset color to default at the end
        return oss.str();
    }

    static std::string getColorCode(const LogLevel level) {
        switch (level) {
            case LogLevel::Debug: return "\ay"; // Yellow
            case LogLevel::Info: return "\ag"; // Green
            case LogLevel::Warning: return "\ao"; // Orange
            case LogLevel::Error: return "\ar"; // Red
            default: return "\aw"; // White
        }
    }
};


// Global logger instance for ease of access
#define logger Logger::instance()
#define log Logger::instance() << LogLevel::Info
#define logd Logger::instance() << LogLevel::Debug
#define logw Logger::instance() << LogLevel::Warning
#define loge Logger::instance() << LogLevel::Error


#endif //LOGGER_H
