/**

 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 * http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */



/**

 * Description: Logger context.

 */

#include "datasystem/common/log/spdlog/logger_context.h"



#include <cstdlib>

#include <exception>

#include <iostream>

#include <map>

#include <string>

#include <vector>



#include <limits.h>

#include <unistd.h>



#include <spdlog/async.h>

#include <spdlog/sinks/rotating_file_sink.h>

#include <spdlog/sinks/stdout_color_sinks.h>



#include "datasystem/common/log/spdlog/log_severity.h"



namespace datasystem {



constexpr auto LOG_LEVEL_OFF = ds_spdlog::level::off;



static std::vector<std::string> GetLogFiles(const LogParam &logParam)

{

    std::vector<std::string> logFiles;

    char resolvedPath[PATH_MAX + 1] = { 0 };



    if (realpath(logParam.logDir.c_str(), resolvedPath) == nullptr) {

        return logFiles;

    }



    logFiles.reserve(logParam.fileNamePatterns.size());

    std::string absoluteLogDir(resolvedPath);



    for (const auto &pattern : logParam.fileNamePatterns) {

        logFiles.emplace_back(absoluteLogDir + "/" + pattern + ".log");

    }



    return logFiles;

}



static void FlushLogger(DsLogger logger)

{

    if (logger) {

        logger->flush();

    }

}



static const std::map<std::string, ds_spdlog::level::level_enum> &GetLogLevelMap()

{

    static const std::map<std::string, ds_spdlog::level::level_enum> LOG_LEVEL_MAP = {

        { "INFO", ds_spdlog::level::info },

        { "WARNING", ds_spdlog::level::warn },

        { "ERROR", ds_spdlog::level::err },

        { "FATAL", ds_spdlog::level::critical }

    };

    return LOG_LEVEL_MAP;

}



static ds_spdlog::level::level_enum GetLogLevel(const std::string &level)

{

    auto iter = GetLogLevelMap().find(level);

    return iter == GetLogLevelMap().end() ? ds_spdlog::level::info : iter->second;

}



LoggerContext::LoggerContext(const GlobalLogParam &globalLogParam) noexcept : globalLogParam_(globalLogParam)

{

    ds_spdlog::drop_all();

    if (!ds_spdlog::thread_pool()) {

        ds_spdlog::init_thread_pool(static_cast<size_t>(globalLogParam_.maxAsyncQueueSize),

                                    static_cast<size_t>(globalLogParam_.asyncThreadCount));

    }

    ds_spdlog::flush_every(std::chrono::seconds(globalLogParam_.logBufSecs));

}



DsLogger LoggerContext::CreateLogger(const LogParam &logParam)

{

    try {

        std::vector<ds_spdlog::sink_ptr> sinks{};

        std::vector<std::string> logFiles = GetLogFiles(logParam);

        for (size_t i = 0; i < logFiles.size(); ++i) {

            auto rotatingSink = std::make_shared<ds_spdlog::sinks::rotating_file_sink_mt>(

                logFiles[i], logParam.maxSize * log_param::SIZE_MEGA_BYTES, logParam.maxFiles);

            const auto log2FileLevel = ToSpdlogLevel(LogSeverity(i % (NUM_SEVERITIES - 1)));

            rotatingSink->set_level(log2FileLevel);

            (void)sinks.emplace_back(rotatingSink);

        }



        const auto stderrLogLevel = (logParam.stderrLogLevel != "FATAL")

                                        ? GetLogLevel(logParam.stderrLogLevel)

                                        : (logParam.alsoLog2Stderr ? GetLogLevel(logParam.logLevel) : LOG_LEVEL_OFF);

        if (stderrLogLevel != LOG_LEVEL_OFF) {

            auto errSink = std::make_shared<ds_spdlog::sinks::stderr_color_sink_mt>();

            errSink->set_level(stderrLogLevel);

            sinks.emplace_back(errSink);

        }



        std::shared_ptr<ds_spdlog::logger> logger;

        if (logParam.logAsync) {

            logger = std::make_shared<ds_spdlog::async_logger>(DS_LOGGER_NAME, sinks.begin(), sinks.end(),

                                                               ds_spdlog::thread_pool(),

                                                               ds_spdlog::async_overflow_policy::overrun_oldest);

        } else {

            logger = std::make_shared<ds_spdlog::logger>(DS_LOGGER_NAME, sinks.begin(), sinks.end());

        }



        ds_spdlog::initialize_logger(logger);

        logger->set_pattern(logParam.pattern, ds_spdlog::pattern_time_type::local);


        const auto logLevel = GetLogLevel(logParam.logLevel);

        logger->set_level(logLevel);

        if (!logParam.logAsync) {

            logger->flush_on(logLevel);

        }



        return logger;

    } catch (std::exception &e) {

        std::cerr << "Failed to init logger, error: " << e.what() << std::endl;

        return nullptr;

    }

}



DsLogger LoggerContext::GetLogger(const std::string &loggerName) const noexcept

{

    return ds_spdlog::get(loggerName);

}



DsLogger LoggerContext::GetDefaultLogger() noexcept

{

    return ds_spdlog::default_logger();

}



void LoggerContext::DropLogger(const std::string &loggerName) const noexcept

{

    ds_spdlog::drop(loggerName);

}



bool LoggerContext::ForceFlush(std::chrono::microseconds) const noexcept

{

    ds_spdlog::apply_all(FlushLogger);

    return true;

}



}  // namespace datasystem