#pragma once

#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <mutex>
#include <ctime>

namespace UsdLayerManager {

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

class Logger {
public:
    static Logger& Instance();
    
    void SetLogLevel(LogLevel level);
    void SetLogFile(const std::string& filename);
    
    void Debug(const std::string& message);
    void Info(const std::string& message);
    void Warning(const std::string& message);
    void Error(const std::string& message);
    
    template<typename... Args>
    void Debug(const std::string& format, Args... args) {
        Log(LogLevel::Debug, FormatString(format, args...));
    }
    
    template<typename... Args>
    void Info(const std::string& format, Args... args) {
        Log(LogLevel::Info, FormatString(format, args...));
    }
    
    template<typename... Args>
    void Warning(const std::string& format, Args... args) {
        Log(LogLevel::Warning, FormatString(format, args...));
    }
    
    template<typename... Args>
    void Error(const std::string& format, Args... args) {
        Log(LogLevel::Error, FormatString(format, args...));
    }

private:
    Logger();
    ~Logger();
    
    Logger(const Logger&) = delete;
    Logger& operator=(const Logger&) = delete;
    
    void Log(LogLevel level, const std::string& message);
    std::string GetTimestamp();
    std::string LevelToString(LogLevel level);
    
    template<typename T>
    std::string FormatString(const std::string& format, T value) {
        std::ostringstream oss;
        oss << format << value;
        return oss.str();
    }
    
    template<typename T, typename... Args>
    std::string FormatString(const std::string& format, T value, Args... args) {
        std::ostringstream oss;
        oss << format << value;
        return FormatString(oss.str(), args...);
    }
    
    LogLevel m_logLevel;
    std::ofstream m_logFile;
    std::mutex m_mutex;
};

// Convenience macros
#define LOG_DEBUG(msg) UsdLayerManager::Logger::Instance().Debug(msg)
#define LOG_INFO(msg) UsdLayerManager::Logger::Instance().Info(msg)
#define LOG_WARNING(msg) UsdLayerManager::Logger::Instance().Warning(msg)
#define LOG_ERROR(msg) UsdLayerManager::Logger::Instance().Error(msg)

} // namespace UsdLayerManager
