#pragma once

#include "foundation/result.h"

#include <cstdint>
#include <string>
#include <string_view>
#include <vector>

namespace pp::foundation {

enum class LogLevel : std::uint8_t {
    trace,
    debug,
    info,
    warning,
    error,
};

struct LogRecord {
    LogLevel level = LogLevel::info;
    std::string component;
    std::string message;
    std::uint64_t frame_id = 0;
    std::uint64_t stroke_id = 0;
    std::uint64_t thread_id = 0;
};

class ILogSink {
public:
    virtual ~ILogSink() = default;
    virtual void write(const LogRecord& record) noexcept = 0;
};

class Logger {
public:
    explicit Logger(ILogSink& sink) noexcept;

    void set_min_level(LogLevel level) noexcept;
    [[nodiscard]] LogLevel min_level() const noexcept;

    [[nodiscard]] Status write(
        LogLevel level,
        std::string_view component,
        std::string_view message,
        std::uint64_t frame_id = 0,
        std::uint64_t stroke_id = 0,
        std::uint64_t thread_id = 0) noexcept;

private:
    ILogSink* sink_ = nullptr;
    LogLevel min_level_ = LogLevel::trace;
};

class MemoryLogSink final : public ILogSink {
public:
    void write(const LogRecord& record) noexcept override;
    [[nodiscard]] const std::vector<LogRecord>& records() const noexcept;
    void clear() noexcept;

private:
    std::vector<LogRecord> records_;
};

[[nodiscard]] const char* log_level_name(LogLevel level) noexcept;

}
