#pragma once
#include <Logger.h>
//#include "../include/Logger.h"
//#include "../../../vcpkg_installed/x64-windows/include/spdlog/spdlog.h"
//#include "../../../vcpkg_installed/x64-windows/include/spdlog/sinks/sink.h"
//#include "../../../vcpkg_installed/x64-windows/include/spdlog/sinks/stdout_color_sinks.h"
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <any>

class SpdLogUtils {
public:
    enum class SpdLogLevel : int
    {
        trace = SPDLOG_LEVEL_TRACE,
        debug = SPDLOG_LEVEL_DEBUG,
        info = SPDLOG_LEVEL_INFO,
        warning = SPDLOG_LEVEL_WARN,
        error = SPDLOG_LEVEL_ERROR,
        critical = SPDLOG_LEVEL_CRITICAL,
        off = SPDLOG_LEVEL_OFF
    };
    static SpdLogLevel logLevelToSpdLogLevel(LogLevel level) {
        SpdLogLevel spdLevel = SpdLogLevel::trace;
        switch (level)
        {
        case LogLevel::trace:
            spdLevel = SpdLogLevel::trace;
            break;
        case LogLevel::debug:
            spdLevel = SpdLogLevel::debug;
            break;
        case LogLevel::info:
            spdLevel = SpdLogLevel::info;
            break;
        case LogLevel::warning:
            spdLevel = SpdLogLevel::warning;
            break;
        case LogLevel::error:
            spdLevel = SpdLogLevel::error;
            break;
        case LogLevel::critical:
            spdLevel = SpdLogLevel::critical;
            break;
        case LogLevel::off:
            spdLevel = SpdLogLevel::off;
            break;
        default:
            spdLevel = SpdLogLevel::trace;
            break;
        }
        return spdLevel;
    }
    static LogLevel spdLogLevelToLogLevel(SpdLogLevel level) {
        LogLevel l = LogLevel::trace;
        switch (level)
        {
        case SpdLogUtils::SpdLogLevel::trace:
            l = LogLevel::trace;
            break;
        case SpdLogUtils::SpdLogLevel::debug:
            l = LogLevel::debug;
            break;
        case SpdLogUtils::SpdLogLevel::info:
            l = LogLevel::info;
            break;
        case SpdLogUtils::SpdLogLevel::warning:
            l = LogLevel::warning;
            break;
        case SpdLogUtils::SpdLogLevel::error:
            l = LogLevel::error;
            break;
        case SpdLogUtils::SpdLogLevel::critical:
            l = LogLevel::critical;
            break;
        case SpdLogUtils::SpdLogLevel::off:
            l = LogLevel::off;
            break;
        default:
            break;
        }
        return l;
    }
    typedef spdlog::color_mode SpdLogColorMode;
    static SpdLogColorMode logColorModeToSpdLogColorMode(LogColorMode colorMode) {
        SpdLogColorMode cm = SpdLogColorMode::automatic;
        switch (colorMode)
        {
        case LogColorMode::automatic:
            cm = SpdLogColorMode::automatic;
            break;
        case LogColorMode::always:
            cm = SpdLogColorMode::always;
            break;
        case LogColorMode::never:
            cm = SpdLogColorMode::never;
            break;
        default:
            break;
        }
        return cm;
    }
};

class ConsoleLoggerSink::Impl {
private:
public:
    // spdlog sink
    std::shared_ptr<spdlog::sinks::stdout_color_sink_mt> m_sink;
    Impl(LogColorMode colorMode = LogColorMode::automatic)
        : m_sink(std::make_shared<spdlog::sinks::stdout_color_sink_mt>()) {
        SpdLogUtils::SpdLogColorMode cm = SpdLogUtils::logColorModeToSpdLogColorMode(colorMode);
        m_sink->set_color_mode(cm);
    }
    operator spdlog::sinks::sink& () {
        return *m_sink;
    }
};

ConsoleLoggerSink::ConsoleLoggerSink(const std::string& name, LogColorMode colorMode)
    : pImpl(std::make_unique<Impl>(colorMode)), m_colorMode(colorMode) {}
std::any ConsoleLoggerSink::getBaseSink()
{
    return std::any(pImpl->m_sink);
}
void ConsoleLoggerSink::flush() noexcept(false) {
    pImpl->m_sink->flush();
}
void ConsoleLoggerSink::setPattern(const std::string& pattern) {

    if (pImpl->m_sink)
    {
        try {
            pImpl->m_sink->set_pattern(pattern);
            m_pattern = pattern;
        }
        catch (const spdlog::spdlog_ex& ex)
        {
            throw LoggerException(LoggerErrorCode::PatternSetFailed, "Failed to set log pattern: " + std::string(ex.what()));
        }
    }
    else
        throw LoggerException(LoggerErrorCode::SinkNotInitialized, "Sink is not initialized.");
}
std::string ConsoleLoggerSink::pattern() const {
    return m_pattern;
}
void ConsoleLoggerSink::setLevel(LogLevel level) {
    if (pImpl->m_sink)
    {
        pImpl->m_sink->set_level(static_cast<spdlog::level::level_enum>(SpdLogUtils::logLevelToSpdLogLevel(level)));
        m_level = level;
    }
    else
        throw LoggerException(LoggerErrorCode::SinkNotInitialized, "Sink is not initialized.");
}
LogLevel ConsoleLoggerSink::level() const {
    return m_level;
}
void ConsoleLoggerSink::setColorMode(LogColorMode colorMode) {
    m_colorMode = colorMode;
}
void ConsoleLoggerSink::setColor(LogLevel level, LogColor colorCode) {
    // 设置不同日志级别的颜色代码
    if (pImpl->m_sink)
    {
        pImpl->m_sink->set_color(static_cast<spdlog::level::level_enum>(SpdLogUtils::logLevelToSpdLogLevel(level)), colorCode);
        m_color = colorCode;
    }
    else
        throw LoggerException(LoggerErrorCode::SinkNotInitialized, "Sink is not initialized.");
}


class FileLoggerSink::Impl {
private:
public:
    // spdlog sink
    std::shared_ptr<spdlog::sinks::basic_file_sink_mt> m_sink;
    Impl(const std::string& filePath, bool truncate, FileEventHandler evh) {
        spdlog::file_event_handlers feh;
        feh.before_open = evh.beforeOpen;
        feh.after_open = evh.afterOpen;
        feh.before_close = evh.beforeClose;
        feh.after_close = evh.afterClose;
        m_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filePath, truncate, feh);
    }
    operator spdlog::sinks::sink&() {
        return *m_sink;
    }
};
FileLoggerSink::FileLoggerSink(const std::string& name, bool truncate, FileEventHandler eventHandler)
    : pImpl(std::make_unique<Impl>(name, truncate, eventHandler)) {}
std::any FileLoggerSink::getBaseSink()
{
    return std::any(pImpl->m_sink);
}
void FileLoggerSink::flush() noexcept(false) {
    pImpl->m_sink->flush();
}
void FileLoggerSink::setPattern(const std::string& pattern) {
    if (pImpl->m_sink)
    {
        try {
            pImpl->m_sink->set_pattern(pattern);
            m_pattern = pattern;
        }
        catch (const spdlog::spdlog_ex& ex)
        {
            throw LoggerException(LoggerErrorCode::PatternSetFailed, "Failed to set log pattern: " + std::string(ex.what()));
        }
    }
    else
        throw LoggerException(LoggerErrorCode::SinkNotInitialized, "Sink is not initialized.");
}
std::string FileLoggerSink::pattern() const {
    return m_pattern;
}
void FileLoggerSink::setLevel(LogLevel level) {
    if (pImpl->m_sink)
    {
        pImpl->m_sink->set_level(static_cast<spdlog::level::level_enum>(SpdLogUtils::logLevelToSpdLogLevel(level)));
        m_level = level;
    }
    else
        throw LoggerException(LoggerErrorCode::SinkNotInitialized, "Sink is not initialized.");
}
LogLevel FileLoggerSink::level() const {
    return m_level;
}


class Logger::LoggerImpl {
private:
public:
    std::shared_ptr<spdlog::logger> logger;
    LoggerImpl(const std::string& name) {
        logger = spdlog::get(name);
        if (!logger)
        {
            logger = spdlog::stdout_color_mt(name);
        }
    }
    template <typename SinksIterator>
    LoggerImpl(const std::string& name, SinksIterator&& begin, SinksIterator&& end) {
        std::vector< std::shared_ptr<spdlog::sinks::sink>> sinks;
        for (auto it = begin; it != end; ++it) {
            auto sinkSharedPtr = (*it)->getBaseSink();
            sinks.push_back(std::any_cast<std::shared_ptr<spdlog::sinks::sink>> (sinkSharedPtr));
        }
        logger = std::make_shared<spdlog::logger>(name, sinks.begin(), sinks.end());
        if (logger)
            spdlog::register_logger(logger);
    }
    ~LoggerImpl() {
        try {
            logger->flush();
        }
        catch (...) {}
        try {
            spdlog::drop(logger->name());
        }
        catch (...) {}
    }

};

Logger::Logger(const std::string &name) : pImpl(std::make_unique<Logger::LoggerImpl>(name))
{
}
template <typename SinksIterator>
Logger::Logger(const std::string& name, SinksIterator&& sinksBegin, SinksIterator&& sinksEnd)
    : pImpl(std::make_unique<Logger::LoggerImpl>(name, std::forward<SinksIterator>(sinksBegin), std::forward<SinksIterator>(sinksEnd)))
{

}

Logger::~Logger()
{

}


void Logger::setGlobalLevel(LogLevel level) noexcept(false)
{
    SpdLogUtils::SpdLogLevel l = SpdLogUtils::logLevelToSpdLogLevel(level);
    spdlog::set_level(static_cast<spdlog::level::level_enum>(l));
}

void Logger::setLevel(LogLevel level) noexcept(false)
{
    if (pImpl->logger)
    {
        SpdLogUtils::SpdLogLevel l = SpdLogUtils::logLevelToSpdLogLevel(level);
        pImpl->logger->set_level(static_cast<spdlog::level::level_enum>(l));
    }
}

LogLevel Logger::level() const noexcept
{
    //level::level_enum logger::level()
    return SpdLogUtils::spdLogLevelToLogLevel(static_cast<SpdLogUtils::SpdLogLevel>(pImpl->logger->level()));
}

// 带参数日志打印
void Logger::trace(const std::string &msg) noexcept
{
    if (pImpl->logger)
    {
        pImpl->logger->trace(msg);
    }
}

void Logger::debug(const std::string &msg) noexcept
{
    if (pImpl->logger)
    {
        pImpl->logger->debug(msg);
    }
}

void Logger::info(const std::string &msg) noexcept
{
    if (pImpl->logger)
    {
        pImpl->logger->info(msg);
    }
}

void Logger::warning(const std::string &msg) noexcept
{
    if (pImpl->logger)
    {
        pImpl->logger->warn(msg);
    }
}

void Logger::error(const std::string &msg) noexcept
{
    if (pImpl->logger)
    {
        pImpl->logger->error(msg);
    }
}

void Logger::critical(const std::string &msg) noexcept
{
    if (pImpl->logger)
    {
        pImpl->logger->critical(msg);
    }
}

