#ifndef LOGGER_H
#define LOGGER_H

#include <string>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <chrono>
#include <mutex>

// Convenience macros for logging
#define LOG_INFO(message) Logger::getInstance().info(message)
#define LOG_WARN(message) Logger::getInstance().warn(message)
#define LOG_ERROR(message) Logger::getInstance().error(message)

// Logger class for handling output based on verbosity settings
// Implemented as a singleton to be used across the codebase
class Logger
{
    public:
        // Log levels
        enum class Level
        {
            INFO,
            WARN,
            ERROR
        };

        // Get the singleton instance
        static Logger &getInstance()
        {
            static Logger instance;
            return instance;
        }

        // Delete copy constructor and assignment operator
        Logger(const Logger &) = delete;
        Logger &operator=(const Logger &) = delete;

        // Set verbose mode
        void setVerboseMode(bool enable)
        {
            std::lock_guard<std::mutex> lock(m_mutex);
            m_verboseMode = enable;
        }

        // Get verbose mode
        bool getVerboseMode() const
        {
            std::lock_guard<std::mutex> lock(m_mutex);
            return m_verboseMode;
        }

        // Log a message with a specific level
        void log(const std::string &message, Level level = Level::INFO) const
        {
            std::lock_guard<std::mutex> lock(m_mutex);

            // Only output if verbose mode is enabled or if it's an error
            if (m_verboseMode || level == Level::ERROR)
            {
                // Get current time in ISO8601 format
                auto now = std::chrono::system_clock::now();
                auto now_c = std::chrono::system_clock::to_time_t(now);
                auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
                                  now.time_since_epoch()).count() % 1000;

                std::stringstream ss;
                ss << std::put_time(std::localtime(&now_c), "%Y-%m-%dT%H:%M:%S");
                ss << '.' << std::setfill('0') << std::setw(3) << now_ms;
                ss << ' ';

                // Add log level prefix
                switch (level)
                {
                    case Level::INFO:
                        ss << "[INFO] ";
                        break;
                    case Level::WARN:
                        ss << "[WARN] ";
                        break;
                    case Level::ERROR:
                        ss << "[ERRO] ";
                        break;
                }

                // Output the message
                std::cout << ss.str() << message << std::endl;
            }
        }

        // Convenience methods for different log levels
        void info(const std::string &message) const
        {
            log(message, Level::INFO);
        }

        void warn(const std::string &message) const
        {
            log(message, Level::WARN);
        }

        void error(const std::string &message) const
        {
            log(message, Level::ERROR);
        }

    private:
        // Private constructor for singleton
        Logger() : m_verboseMode(false) {}

        // Member variables
        mutable std::mutex m_mutex;
        bool m_verboseMode;
};

#endif // LOGGER_H
