// test_app_version_padding.cpp — the ALWAYS-ON anti-normalization guard for the version
// string threading (Phase V, V1 leading-zero fidelity). No REAPER, no framework.
//
// WHY THIS EXECUTABLE EXISTS: test_app_version.cpp pins stampVersion() relative to the
// live configure_file'd version string — but a relative assertion (e.g. appVersion() ==
// stampVersion()) is version-shape-blind: it holds whether the string was threaded
// verbatim or reconstructed from numeric components, at any version, padded or not.
// The live suite therefore cannot detect a reconstruct-from-components regression at all.
// Padded and unpadded versions are both legitimate (Daniel's ruling), so the guard must
// not depend on the shipped version's shape.
//
// MECHANISM: CMakeLists.txt runs the SAME src/version_generated.h.in template through
// configure_file a second time with a SYNTHETIC zero-padded version ("0.9.01") into
// generated_padding_canary/, and this executable compiles the SAME src/app_version.cpp
// against that header (include-dir substitution — this target never sees the live
// generated/ dir). The exact production derivation path — template substitution →
// REASAMPLER_VERSION_STRING → stampVersion()/appVersion() — is thereby exercised with an
// input whose normalized form provably differs from its verbatim form. If anyone
// reimplements the version string by reconstructing it from numeric components (a
// parse-and-reprint inside app_version.cpp, or project(VERSION) numeric macros added to
// the template), this binary renders "0.9.1" and the checks below fail — at ANY live
// version, which is exactly the property the live-version assertion cannot provide.
//
// The "0.9.01" literals below are the synthetic fixture and MUST match the canary
// configure_file input in CMakeLists.txt. They are NOT the shipped version: never bump
// them on release — their padded shape is the entire point.

#include "../src/core/version/app_version.h"

#include <cstdio>
#include <string>

using namespace reasampler;
using namespace reasampler::version;

static int g_fail = 0;
#define CHECK(cond) do { if(!(cond)) { \
    std::printf("FAIL line %d: %s\n", __LINE__, #cond); ++g_fail; } } while(0)

static void testPaddedVersionSurvivesThreadingVerbatim() {
    // The verbatim-threading invariant, exercised with an input where reconstruction and
    // verbatim DIFFER: a normalized render would be "0.9.1" and fail both checks.
    CHECK(stampVersion() == "0.9.01");
    if (isBeta()) CHECK(appVersion() == "0.9.01-beta");
    else          CHECK(appVersion() == "0.9.01");
}

static void testPaddedStampStillClassifiesStamped() {
    // A padded stamp stays classifiable on read-back: parse tolerates leading zeros
    // (raw preserved verbatim, triple normalized to integers — {0,9,1}).
    WritingVersion wv = classifyWritingVersion(stampVersion());
    CHECK(wv.kind == WritingVersion::Kind::Stamped);
    CHECK(wv.raw == "0.9.01");
    CHECK(wv.parsed.major == 0 && wv.parsed.minor == 9 && wv.parsed.patch == 1);
}

static void testCanaryFixtureIsNonVacuous() {
    // Self-check: the canary fixture must be zero-padded so that its verbatim string
    // differs from what a parse-and-reprint of its numeric triple would produce.
    // If the fixture is ever "fixed" to an unpadded form (e.g. "0.9.1"), the checks in
    // testPaddedVersionSurvivesThreadingVerbatim become vacuous — a reconstructed
    // string would match the fixture and the canary would pass even if the threading
    // regressed. Parse stampVersion() and reprint the triple; they must NOT match.
    WritingVersion wv = classifyWritingVersion(stampVersion());
    CHECK(wv.kind == WritingVersion::Kind::Stamped);  // fixture must be parseable
    const std::string reprinted =
        std::to_string(wv.parsed.major) + "." +
        std::to_string(wv.parsed.minor) + "." +
        std::to_string(wv.parsed.patch);
    // If this fails, one of two causes: (1) the fixture was changed to an unpadded form
    // (e.g. "0.9.1") — restore a zero-padded fixture (e.g. "0.9.01") in CMakeLists.txt
    // and update the matching literals in this file; or (2) the fixture is still padded
    // but app_version.cpp regressed to parse-and-reprint — in that case
    // testPaddedVersionSurvivesThreadingVerbatim also fails, pointing at the real cause.
    CHECK(reprinted != stampVersion());
}

int main() {
    testPaddedVersionSurvivesThreadingVerbatim();
    testPaddedStampStillClassifiesStamped();
    testCanaryFixtureIsNonVacuous();

    if (g_fail == 0) std::printf("app_version_padding: all tests passed\n");
    else             std::printf("app_version_padding: %d CHECK(s) FAILED\n", g_fail);
    return g_fail == 0 ? 0 : 1;
}
