#pragma once

#include <mutex>
#include <sstream>
#include <string>

namespace softbus::core {

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

class Logger {
public:
    static Logger& instance();

    void setLevel(LogLevel level);
    void log(LogLevel level, const char* file, const char* func, const std::string& tag,
             const std::string& message);
    void log(LogLevel level, const char* file, const char* func, const std::string& tag,
             const std::string& traceId, const std::string& message);

    template <typename... Args>
    void infoAt(const char* file, const char* func, const std::string& tag, Args&&... args)
    {
        log(LogLevel::Info, file, func, tag, concat(std::forward<Args>(args)...));
    }

    template <typename... Args>
    void infoTraceAt(const char* file, const char* func, const std::string& tag,
                     const std::string& traceId, Args&&... args)
    {
        log(LogLevel::Info, file, func, tag, traceId, concat(std::forward<Args>(args)...));
    }

    template <typename... Args>
    void warnAt(const char* file, const char* func, const std::string& tag, Args&&... args)
    {
        log(LogLevel::Warn, file, func, tag, concat(std::forward<Args>(args)...));
    }

    template <typename... Args>
    void warnTraceAt(const char* file, const char* func, const std::string& tag,
                     const std::string& traceId, Args&&... args)
    {
        log(LogLevel::Warn, file, func, tag, traceId, concat(std::forward<Args>(args)...));
    }

    template <typename... Args>
    void errorAt(const char* file, const char* func, const std::string& tag, Args&&... args)
    {
        log(LogLevel::Error, file, func, tag, concat(std::forward<Args>(args)...));
    }

    template <typename... Args>
    void errorTraceAt(const char* file, const char* func, const std::string& tag,
                      const std::string& traceId, Args&&... args)
    {
        log(LogLevel::Error, file, func, tag, traceId, concat(std::forward<Args>(args)...));
    }

    template <typename... Args>
    void debugAt(const char* file, const char* func, const std::string& tag, Args&&... args)
    {
        log(LogLevel::Debug, file, func, tag, concat(std::forward<Args>(args)...));
    }

private:
    Logger() = default;

    template <typename T>
    static std::string concat(T&& value)
    {
        std::ostringstream oss;
        oss << std::forward<T>(value);
        return oss.str();
    }

    template <typename T, typename... Rest>
    static std::string concat(T&& first, Rest&&... rest)
    {
        std::ostringstream oss;
        oss << std::forward<T>(first);
        ((oss << std::forward<Rest>(rest)), ...);
        return oss.str();
    }

    LogLevel level_{LogLevel::Info};
    std::mutex mutex_;
};

} // namespace softbus::core

#define SB_LOG_DEBUG(tag, ...) \
    ::softbus::core::Logger::instance().debugAt(__FILE__, __func__, tag, ##__VA_ARGS__)

#define SB_LOG_INFO(tag, ...) \
    ::softbus::core::Logger::instance().infoAt(__FILE__, __func__, tag, ##__VA_ARGS__)

#define SB_LOG_INFO_TRACE(tag, traceId, ...) \
    ::softbus::core::Logger::instance().infoTraceAt(__FILE__, __func__, tag, traceId, ##__VA_ARGS__)

#define SB_LOG_WARN(tag, ...) \
    ::softbus::core::Logger::instance().warnAt(__FILE__, __func__, tag, ##__VA_ARGS__)

#define SB_LOG_WARN_TRACE(tag, traceId, ...) \
    ::softbus::core::Logger::instance().warnTraceAt(__FILE__, __func__, tag, traceId, ##__VA_ARGS__)

#define SB_LOG_ERROR(tag, ...) \
    ::softbus::core::Logger::instance().errorAt(__FILE__, __func__, tag, ##__VA_ARGS__)

#define SB_LOG_ERROR_TRACE(tag, traceId, ...) \
    ::softbus::core::Logger::instance().errorTraceAt(__FILE__, __func__, tag, traceId, ##__VA_ARGS__)
