#include "foundation/log.h"

namespace pp::foundation {

namespace {

[[nodiscard]] bool should_write(LogLevel level, LogLevel min_level) noexcept
{
    return static_cast<std::uint8_t>(level) >= static_cast<std::uint8_t>(min_level);
}

}

Logger::Logger(ILogSink& sink) noexcept
    : sink_(&sink)
{
}

void Logger::set_min_level(LogLevel level) noexcept
{
    min_level_ = level;
}

LogLevel Logger::min_level() const noexcept
{
    return min_level_;
}

Status Logger::write(
    LogLevel level,
    std::string_view component,
    std::string_view message,
    std::uint64_t frame_id,
    std::uint64_t stroke_id,
    std::uint64_t thread_id) noexcept
{
    if (component.empty()) {
        return Status::invalid_argument("log component must not be empty");
    }

    if (message.empty()) {
        return Status::invalid_argument("log message must not be empty");
    }

    if (!should_write(level, min_level_)) {
        return Status::success();
    }

    sink_->write(LogRecord {
        .level = level,
        .component = std::string(component),
        .message = std::string(message),
        .frame_id = frame_id,
        .stroke_id = stroke_id,
        .thread_id = thread_id,
    });
    return Status::success();
}

void MemoryLogSink::write(const LogRecord& record) noexcept
{
    records_.push_back(record);
}

const std::vector<LogRecord>& MemoryLogSink::records() const noexcept
{
    return records_;
}

void MemoryLogSink::clear() noexcept
{
    records_.clear();
}

const char* log_level_name(LogLevel level) noexcept
{
    switch (level) {
    case LogLevel::trace:
        return "trace";
    case LogLevel::debug:
        return "debug";
    case LogLevel::info:
        return "info";
    case LogLevel::warning:
        return "warning";
    case LogLevel::error:
        return "error";
    }

    return "unknown";
}

}
