#pragma once

#include <cstdlib>
#include <iostream>
#include <string_view>

namespace pp::tests {

class Harness {
public:
    void expect(bool condition, std::string_view expression, std::string_view file, int line)
    {
        ++assertions_;
        if (!condition) {
            ++failures_;
            std::cerr << file << ":" << line << ": expectation failed: " << expression << "\n";
        }
    }

    template <typename Callback>
    void run(std::string_view name, Callback callback)
    {
        const auto failures_before = failures_;
        callback(*this);
        ++tests_;
        if (failures_ == failures_before) {
            std::cout << "[pass] " << name << "\n";
        } else {
            std::cout << "[fail] " << name << "\n";
        }
    }

    [[nodiscard]] int finish() const
    {
        std::cout << "{\"tests\":" << tests_
                  << ",\"assertions\":" << assertions_
                  << ",\"failures\":" << failures_ << "}\n";
        return failures_ == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
    }

private:
    int tests_ = 0;
    int assertions_ = 0;
    int failures_ = 0;
};

}

#define PP_EXPECT(harness, expression) \
    (harness).expect(static_cast<bool>(expression), #expression, __FILE__, __LINE__)
