// SPDX-FileCopyrightText: 2024 Yury Gubich <blue@macaw.me>
// SPDX-License-Identifier: GPL-3.0-or-later

#include "logger.h"

#include <iostream>
#include <chrono>

static constexpr std::string_view logLevelMap[] = {
    "TRACE",
    "DEBUG",
    "INFO",
    "WARNING",
    "ERROR",
    "FATAL",
};

static constexpr std::string_view colorMap[] = {
    "\033[90m",  // TRACE (Gray)
    "\033[34m",  // DEBUG (Blue)
    "\033[32m",  // INFO (Green)
    "\033[33m",  // WARNING (Yellow)
    "\033[31m",  // ERROR (Red)
    "\033[35m",  // FATAL (Magenta)
};

static constexpr std::string_view clear = "\033[0m";
static constexpr std::string_view bold = "\033[1m";

void writeTimestamp() {
    std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
    std::time_t time_now = std::chrono::system_clock::to_time_t(now);
    std::tm local_time = *std::localtime(&time_now);
    std::chrono::milliseconds millis = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;

    std::cout << "\033[90m" << std::put_time(&local_time, "%Y-%m-%d %H:%M:%S");
    std::cout << "." << std::setfill('0') << std::setw(3) << millis.count();
    std::cout << clear << ' ';
}

constexpr std::string_view getLogLevel(gloox::LogLevel level) {
    return (level >= 0 && level < 3) ? logLevelMap[Shared::Logger::convert(level)] : "UNKNOWN";
}

constexpr std::string_view getColor(gloox::LogLevel level) {
    return (level >= 0 && level < 3) ? colorMap[Shared::Logger::convert(level)] : "";
}

void writeTags(gloox::LogArea area) {
    if (area & gloox::LogAreaClassParser)
        std::cout << " [Parser]";
    if (area & gloox::LogAreaClassConnectionTCPBase)
        std::cout << " [Connection]";
    if (area & gloox::LogAreaClassClient)
        std::cout << " [Client]";
    if (area & gloox::LogAreaClassClientbase)
        std::cout << " [Client Base]";
    if (area & gloox::LogAreaClassComponent)
        std::cout << " [Component]";
    if (area & gloox::LogAreaClassDns)
        std::cout << " [DNS]";
    if (area & gloox::LogAreaClassConnectionHTTPProxy)
        std::cout << " [HTTP Proxy]";
    if (area & gloox::LogAreaClassConnectionSOCKS5Proxy)
        std::cout << " [SOCKS5 Proxy]";
    if (area & gloox::LogAreaClassConnectionTCPClient)
        std::cout << " [TCP Client]";
    if (area & gloox::LogAreaClassConnectionTCPServer)
        std::cout << " [TCP Server]";
    if (area & gloox::LogAreaClassS5BManager)
        std::cout << " [SOCKS5 Bytestream Manager]";
    if (area & gloox::LogAreaClassSOCKS5Bytestream)
        std::cout << " [SOCKS5 Bytestream]";
    if (area & gloox::LogAreaLinkLocalManager)
        std::cout << " [Link Local Manager]";
    if (area & gloox::LogAreaXmlIncoming)
        std::cout << " \033[1;36m[XML IN]";
    if (area & gloox::LogAreaXmlOutgoing)
        std::cout << " \033[1;35m[XML OUT]";
    if (area & gloox::LogAreaUser)
        std::cout << " [USER]";

    if (area == gloox::LogAreaClassDns)
        std::cout << '\t';
}

Shared::Logger::Level Shared::Logger::convert(gloox::LogLevel level) {
    switch (level) {
        case gloox::LogLevelDebug:
            return trace;
        case gloox::LogLevelError:
            return error;
        case gloox::LogLevelWarning:
            return warning;
    };

    return warning;
}

gloox::LogLevel Shared::Logger::convert(Level level) {
    switch (level) {
        case trace:
            return gloox::LogLevelDebug;
        case debug:
            return gloox::LogLevelWarning;
        case info:
            return gloox::LogLevelWarning;
        case warning:
            return gloox::LogLevelWarning;
        case error:
            return gloox::LogLevelError;
        case fatal:
            return gloox::LogLevelError;
    }

    return gloox::LogLevelWarning;
}

Shared::Logger::Logger(Level level):
    level(level)
{}

Shared::Logger::~Logger() {}

void Shared::Logger::handleLog(gloox::LogLevel level, gloox::LogArea area, const std::string& message) {
    writeTimestamp();
    std::cout << getColor(level) << bold << '[' << getLogLevel(level) << ']' << clear << bold;
    writeTags(area);
    std::cout << clear << '\t' << message << clear << std::endl;
}

void Shared::Logger::log(Level lvl, const std::string& message, const std::vector<std::string>& domain) const {
    if (lvl < level)
        return;

    writeTimestamp();
    std::cout << colorMap[lvl] << bold << '[' << logLevelMap[lvl] << ']' << clear;

    if (!domain.empty()) {
        std::cout << ' ' << bold << '[';

        bool first = true;
        for (const std::string& tag : domain) {
            if (first)
                first = false;
            else
                std::cout << ' ';

            std::cout << tag;
        }

        std::cout << ']' << clear;
    }
    std::cout << '\t' << message << clear << std::endl;
}