/* -*- mode: c++ -*-
 * Copyright 2025 Dracal Technologies Inc. All rights reserved.
 */

#include <dracal/common/log.hpp>
#include <dracal/common/platform.hpp>

#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>

#include <iostream>

#if defined(DRACAL_WINDOWS)
    #include <cstdlib>
#endif

namespace dracal::common {

// cppcheck-suppress unusedPrivateFunction
// cppcheck-suppress unmatchedSuppression
std::shared_ptr<spdlog::logger> &global_logger::get() {
    static std::shared_ptr<spdlog::logger> g_logger;
    return g_logger;
}

bool initialize_logging(const std::string &log_filename, const std::string &format, const bool enable_std_output,
                        const size_t max_file_size, [[maybe_unused]] const bool service) {
    static bool is_initialized = false;

    if (is_initialized) {
        return true;
    }

#if defined(DRACAL_WINDOWS)
    const char *data_folder = service ? std::getenv("PROGRAMDATA") : std::getenv("APPDATA");
    if (data_folder == nullptr) {
        std::cout << "Error: Unable to get data folder environment variable" << std::endl;
        return false;
    }

    const fs::path folder = fs::path(std::string(data_folder)) / "Dracal";
#elif defined(DRACAL_APPLE)
    const fs::path service_folder = "/Library/Logs/Dracal";
    fs::path app_folder;

    if (!service) {
        const char *home = std::getenv("HOME");
        app_folder =
            (home == nullptr) ? fs::temp_directory_path() / "Dracal" : fs::path(home) / "Library" / "Logs" / "Dracal";
    }

    const fs::path folder = service ? service_folder : app_folder;
#elif defined(DRACAL_LINUX)
    const fs::path service_folder = "/var/log/dracal";
    fs::path app_folder;

    if (!service) {
        const char *home = std::getenv("HOME");
        app_folder =
            (home == nullptr) ? fs::temp_directory_path() / "Dracal" : fs::path(home) / ".local" / "share" / "Dracal";
    }

    const fs::path folder = service ? service_folder : app_folder;
#endif

    if (!fs::exists(folder)) {
        std::error_code ec;
        if (!fs::create_directories(folder, ec)) {
            std::cout << "Error: Unable to create log directory: " << folder.string() << " (" << ec.message() << ")"
                      << std::endl;
            return false;
        }
    }

    const fs::path log_file = folder / std::string(log_filename + "-log.log");
    const std::string log_app_filename = log_file.string();

    try {
        std::vector<spdlog::sink_ptr> sinks;

        auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(log_app_filename, max_file_size, 3);
        sinks.push_back(file_sink);

        if (enable_std_output) {
#if defined(DRACAL_WINDOWS)
            auto console_sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
#else
            auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
#endif
            sinks.push_back(console_sink);
        }

        global_logger::get() = std::make_shared<spdlog::logger>("dracal", sinks.begin(), sinks.end());
        global_logger::get()->set_pattern(format);
        global_logger::get()->set_level(spdlog::level::info);
        spdlog::set_default_logger(global_logger::get());
        spdlog::flush_every(std::chrono::milliseconds(100));

        is_initialized = true;

        dracal_info("=============================================================");
        dracal_info("logs initialized using log app file({})", log_app_filename);
        return true;
    } catch (const spdlog::spdlog_ex &ex) {
        std::cout << "Error: Unable to create log file: " << log_app_filename << " (" << ex.what() << ")" << std::endl;
        return false;
    }
}

void enable_trace_logs(const bool enable) {
    auto &logger = global_logger::get();
    if (!logger) {
        return;
    }

    auto current_level = logger->level();
    auto new_level = enable ? spdlog::level::trace : spdlog::level::info;

    if (new_level <= current_level) {
        logger->set_level(new_level);
        dracal_info("trace logging {}", enable ? "enabled" : "disabled");
    }
}

void enable_debug_logs(const bool enable) {
    auto &logger = global_logger::get();
    if (!logger) {
        return;
    }

    auto current_level = logger->level();
    auto new_level = enable ? spdlog::level::debug : spdlog::level::info;

    if (new_level <= current_level) {
        logger->set_level(new_level);
        dracal_info("debug logging {}", enable ? "enabled" : "disabled");
    }
}

void quiet_logs() {
    auto &logger = global_logger::get();
    if (!logger) {
        return;
    }

    dracal_info("logging disabled");
    logger->set_level(spdlog::level::off);
}

} // namespace dracal::common