#include "foundation/log.h"
#include "test_harness.h"

#include <string_view>

using pp::foundation::LogLevel;
using pp::foundation::Logger;
using pp::foundation::MemoryLogSink;
using pp::foundation::StatusCode;
using pp::foundation::log_level_name;

namespace {

void writes_structured_records(pp::tests::Harness& h)
{
    MemoryLogSink sink;
    Logger logger(sink);

    const auto status = logger.write(LogLevel::info, "paint", "stroke committed", 7, 11, 3);

    PP_EXPECT(h, status.ok());
    PP_EXPECT(h, sink.records().size() == 1U);
    PP_EXPECT(h, sink.records()[0].level == LogLevel::info);
    PP_EXPECT(h, sink.records()[0].component == std::string_view("paint"));
    PP_EXPECT(h, sink.records()[0].message == std::string_view("stroke committed"));
    PP_EXPECT(h, sink.records()[0].frame_id == 7U);
    PP_EXPECT(h, sink.records()[0].stroke_id == 11U);
    PP_EXPECT(h, sink.records()[0].thread_id == 3U);
}

void filters_below_minimum_level(pp::tests::Harness& h)
{
    MemoryLogSink sink;
    Logger logger(sink);
    logger.set_min_level(LogLevel::warning);

    PP_EXPECT(h, logger.min_level() == LogLevel::warning);
    PP_EXPECT(h, logger.write(LogLevel::debug, "ui", "layout pass").ok());
    PP_EXPECT(h, logger.write(LogLevel::warning, "ui", "slow layout").ok());
    PP_EXPECT(h, sink.records().size() == 1U);
    PP_EXPECT(h, sink.records()[0].level == LogLevel::warning);
}

void rejects_empty_component_or_message(pp::tests::Harness& h)
{
    MemoryLogSink sink;
    Logger logger(sink);

    const auto empty_component = logger.write(LogLevel::error, "", "message");
    const auto empty_message = logger.write(LogLevel::error, "renderer", "");

    PP_EXPECT(h, !empty_component.ok());
    PP_EXPECT(h, empty_component.code == StatusCode::invalid_argument);
    PP_EXPECT(h, !empty_message.ok());
    PP_EXPECT(h, empty_message.code == StatusCode::invalid_argument);
    PP_EXPECT(h, sink.records().empty());
}

void exposes_stable_level_names_and_clear(pp::tests::Harness& h)
{
    MemoryLogSink sink;
    Logger logger(sink);

    PP_EXPECT(h, log_level_name(LogLevel::trace) == std::string_view("trace"));
    PP_EXPECT(h, log_level_name(LogLevel::error) == std::string_view("error"));
    PP_EXPECT(h, logger.write(LogLevel::info, "assets", "loaded").ok());
    PP_EXPECT(h, sink.records().size() == 1U);
    sink.clear();
    PP_EXPECT(h, sink.records().empty());
}

}

int main()
{
    pp::tests::Harness harness;
    harness.run("writes_structured_records", writes_structured_records);
    harness.run("filters_below_minimum_level", filters_below_minimum_level);
    harness.run("rejects_empty_component_or_message", rejects_empty_component_or_message);
    harness.run("exposes_stable_level_names_and_clear", exposes_stable_level_names_and_clear);
    return harness.finish();
}
