#include <doctest/doctest.h>

#include "fgc/DiagParser.h"

using namespace fgc;

namespace {
// A DIAG stream as captured on the wire: DG lines interleaved with an ST line.
const char* kDiag =
    "DG BEGIN Y\n"
    "ST Y:A,5,5,80084000,0,8,8,S P:A,5,5,80084000,0,8,8,S\n"
    "DG Y S12500 FWD ERR_PEAK 234 ERR_RMS 45 ERR_STILL 89 CS_MIN 10 CS_MAX 20 "
    "SG_MIN 456 PWM_AVG 128 FLAGS 0x00000000 PASS\n"
    "DG Y S25000 REV ERR_PEAK 300 ERR_RMS 50 ERR_STILL 100 CS_MIN 11 CS_MAX 21 "
    "SG_MIN 400 PWM_AVG 130 FLAGS 0x00000000 PASS\n"
    "DG Y RESULT PASS\n"
    "DG BEGIN P\n"
    "DG P S12500 FWD ERR_PEAK -1 ERR_RMS -1 ERR_STILL -1 CS_MIN 12 CS_MAX 22 "
    "SG_MIN 0 PWM_AVG 64 FLAGS 0x00000020 FAIL\n"
    "DG P RESULT FAIL\n"
    "DG DONE\n";
}  // namespace

TEST_CASE("parseDiag decodes axes, tests and results") {
    DiagResult d = parseDiag(kDiag);
    REQUIRE(d.valid);
    CHECK(d.done);
    REQUIRE(d.axes.size() == 2);

    const DiagAxis& y = d.axes[0];
    CHECK(y.axis == 'Y');
    CHECK(y.has_result);
    CHECK(y.pass);
    REQUIRE(y.tests.size() == 2);
    CHECK(y.tests[0].speed == 12500);
    CHECK(y.tests[0].fwd);
    CHECK(y.tests[0].err_peak == 234);
    CHECK(y.tests[0].err_still == 89);
    CHECK(y.tests[0].cs_min == 10);
    CHECK(y.tests[0].cs_max == 20);
    CHECK(y.tests[0].sg_min == 456);
    CHECK(y.tests[0].pass);
    CHECK_FALSE(y.tests[1].fwd);  // REV

    const DiagAxis& p = d.axes[1];
    CHECK(p.axis == 'P');
    CHECK_FALSE(p.pass);          // RESULT FAIL
    REQUIRE(p.tests.size() == 1);
    CHECK(p.tests[0].err_peak == -1);   // no encoder
    CHECK(p.tests[0].flags == 0x20u);
    CHECK_FALSE(p.tests[0].pass);
}

TEST_CASE("DiagResult::allPass requires every axis result to pass") {
    CHECK_FALSE(parseDiag(kDiag).allPass());        // P failed
    DiagResult ok = parseDiag(
        "DG BEGIN Y\nDG Y RESULT PASS\nDG BEGIN P\nDG P RESULT PASS\nDG DONE\n");
    CHECK(ok.allPass());
}

TEST_CASE("parseDiag handles empty/garbage and incomplete streams") {
    CHECK_FALSE(parseDiag("").valid);
    CHECK_FALSE(parseDiag("ST Y:A,1,1,0,0,0,0,S\nnothing\n").valid);

    DiagResult inc = parseDiag("DG BEGIN Y\nDG Y RESULT PASS\n");  // no DG DONE
    CHECK(inc.valid);
    CHECK_FALSE(inc.done);
}

TEST_CASE("formatDiag renders a summary") {
    auto bad = formatDiag(DiagResult{});
    REQUIRE(bad.size() == 1);
    CHECK(bad[0].find("no diagnostic") != std::string::npos);

    auto good = formatDiag(parseDiag(kDiag));
    std::string joined;
    for (const auto& l : good) joined += l + "\n";
    CHECK(joined.find("[Y]") != std::string::npos);
    CHECK(joined.find("[P]") != std::string::npos);
    CHECK(joined.find("overall: FAIL") != std::string::npos);
}
