// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#include "logger.h"

#include <cstdarg>
#include <cstdint>
#include <ctime>
#include <chrono>
#include <iomanip>
#include <sstream>
#include <filesystem>

namespace dragonx {
namespace util {

Logger::Logger() = default;

Logger::~Logger()
{
    if (file_.is_open()) {
        file_.close();
    }
}

Logger& Logger::instance()
{
    static Logger logger;
    return logger;
}

bool Logger::init(const std::string& path)
{
    std::lock_guard<std::mutex> lock(mutex_);
    
    if (file_.is_open()) {
        file_.close();
    }

    // W7-4: cap the log's growth — if the existing file is already large, rotate it to a single .1
    // backup before reopening in append mode, so a long-lived or verbose session can't grow it
    // without bound.
    {
        std::error_code ec;
        const auto sz = std::filesystem::file_size(path, ec);
        constexpr std::uintmax_t kMaxLogBytes = 10ull * 1024ull * 1024ull;  // 10 MB
        if (!ec && sz > kMaxLogBytes) {
            std::filesystem::rename(path, path + ".1", ec);  // replaces any previous .1 backup
            if (ec) std::filesystem::remove(path, ec);       // fall back to truncation if rename fails
        }
    }

    file_.open(path, std::ios::out | std::ios::app);
    initialized_ = file_.is_open();

    if (initialized_) {
        // Write the banner directly, NOT via write(): write() re-locks the non-recursive mutex_ we
        // already hold here, which would deadlock (latent — init() was previously never called, W7-2).
        file_ << "=== Logger initialized ===" << std::endl;
        file_.flush();
    }

    return initialized_;
}

void Logger::write(const std::string& message)
{
    std::lock_guard<std::mutex> lock(mutex_);
    
    // Get current timestamp
    auto now = std::chrono::system_clock::now();
    auto time = std::chrono::system_clock::to_time_t(now);
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
        now.time_since_epoch()) % 1000;
    
    std::stringstream ss;
    // Reachable from worker/monitor threads — std::localtime shares a process-wide static tm, so use the
    // reentrant variant into a local tm (the logger's own mutex can't protect other localtime callers).
    std::tm tmv{};
#ifdef _WIN32
    localtime_s(&tmv, &time);
#else
    localtime_r(&time, &tmv);
#endif
    ss << std::put_time(&tmv, "%Y-%m-%d %H:%M:%S");
    ss << '.' << std::setfill('0') << std::setw(3) << ms.count();
    ss << " | " << message;
    
    std::string line = ss.str();
    
    // Write to file if open
    if (file_.is_open()) {
        file_ << line << std::endl;
        file_.flush();
    }
    
    // Also write to stdout in debug mode
#ifdef DRAGONX_DEBUG
    printf("%s\n", line.c_str());
#endif

    // Forward to callback (e.g. ConsoleTab) with the raw message
    if (callback_) {
        callback_(message);
    }
}

void Logger::writef(const char* format, ...)
{
    char buffer[4096];
    
    va_list args;
    va_start(args, format);
    vsnprintf(buffer, sizeof(buffer), format, args);
    va_end(args);
    
    write(buffer);
}

void Logger::setCallback(std::function<void(const std::string&)> cb)
{
    std::lock_guard<std::mutex> lock(mutex_);
    callback_ = std::move(cb);
}

} // namespace util
} // namespace dragonx
