// Standalone tests for reasampler::lane_keys — no REAPER, no test framework. The pure
// managed/manual lane-name heuristic that resolves D2 design points #1 (auto-tag
// exemption) and #2 (durable lane identity vs ordinal renumber).
//
// Covers:
//  1. isManagedLaneName: only the "reasampler:" prefix is managed; everything else
//     (empty, user comp names, near-miss prefixes) is manual.
//  2. managedLaneKey: managed name -> its durable key; manual/unnamed -> nullopt.
//  3. Round-trip: managedLaneKey(laneNameForMode(m)) == "reasampler:" + m, so the
//     Wave-3 minting path and the read path cannot drift.

#include "../src/core/view/lane_keys.h"

#include <cstdio>
#include <string>

using namespace reasampler;
using namespace reasampler::view;

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 testIsManagedLaneName() {
    // Tool-minted managed names.
    CHECK(isManagedLaneName("reasampler:design"));
    CHECK(isManagedLaneName("reasampler:arrange"));
    CHECK(isManagedLaneName("reasampler:"));         // prefix alone still ours (odd but managed)

    // Manual / user lanes are never managed.
    CHECK(!isManagedLaneName(""));                   // unnamed lane ⇒ manual
    CHECK(!isManagedLaneName("Comp 1"));             // user comp lane
    CHECK(!isManagedLaneName("Lead vocal"));
    CHECK(!isManagedLaneName("reasample"));          // near-miss, no colon ⇒ not ours
    CHECK(!isManagedLaneName("Reasampler:design"));  // case-sensitive prefix
    CHECK(!isManagedLaneName(" reasampler:x"));      // leading space ⇒ not a prefix match
}

static void testManagedLaneKey() {
    // Managed lane: the durable name IS the key.
    auto k = managedLaneKey("reasampler:design");
    CHECK(k.has_value() && *k == "reasampler:design");

    // Manual / unnamed lanes have no managed key (⇒ treated as manual, never driven).
    CHECK(!managedLaneKey("").has_value());
    CHECK(!managedLaneKey("Comp 1").has_value());
    CHECK(!managedLaneKey("guitar-double").has_value());
}

static void testIsOnManualLane() {
    // Non-fixed-lane track: concept does not apply regardless of name.
    CHECK(!isOnManualLane(false, ""));          // normal track, unnamed ⇒ not manual
    CHECK(!isOnManualLane(false, "Comp 1"));    // normal track, user name ⇒ not manual
    CHECK(!isOnManualLane(false, "reasampler:design"));  // normal track, managed name ⇒ not manual

    // Fixed-lane track: managed lane (tool-prefixed) ⇒ NOT manual (tool drives it).
    CHECK(!isOnManualLane(true, "reasampler:design"));
    CHECK(!isOnManualLane(true, "reasampler:arrange"));
    CHECK(!isOnManualLane(true, "reasampler:"));  // prefix-only: still managed

    // Fixed-lane track: unnamed lane (empty P_LANENAME) ⇒ manual.
    // REAPER starts fixed lanes unnamed; an item on an unnamed fixed lane is a user
    // comp lane and must be exempt from auto-tag.
    CHECK(isOnManualLane(true, ""));

    // Fixed-lane track: user-named but non-managed ⇒ manual.
    CHECK(isOnManualLane(true, "Comp 1"));
    CHECK(isOnManualLane(true, "Lead vocal"));
    CHECK(isOnManualLane(true, "reasample"));    // near-miss, no colon ⇒ manual
    CHECK(isOnManualLane(true, "Reasampler:x")); // wrong case ⇒ manual
}

static void testRoundTrip() {
    // Minting then reading must agree: managedLaneKey(laneNameForMode(m)) recovers the
    // prefixed name for every mode id.
    for (const std::string mode : {std::string("arrange"), std::string("design"),
                                   std::string("mixdown")}) {
        const std::string name = laneNameForMode(mode);
        CHECK(name == "reasampler:" + mode);
        CHECK(isManagedLaneName(name));
        auto key = managedLaneKey(name);
        CHECK(key.has_value() && *key == "reasampler:" + mode);
    }
}

static void testModeIdFromLaneName() {
    // The exact inverse of laneNameForMode: recover the owning mode from a managed name.
    // Used by the Wave-3 load-time reconcile to rebuild ownership from durable names.
    for (const std::string mode : {std::string("arrange"), std::string("design"),
                                   std::string("mixdown"), std::string("mode:with:colons")}) {
        auto recovered = modeIdFromLaneName(laneNameForMode(mode));
        CHECK(recovered.has_value() && *recovered == mode);  // modeIdFromLaneName∘laneNameForMode == id
    }

    // Manual / unnamed lanes carry no mode (⇒ left off the ownership index on reconcile).
    CHECK(!modeIdFromLaneName("").has_value());
    CHECK(!modeIdFromLaneName("Comp 1").has_value());
    CHECK(!modeIdFromLaneName("Reasampler:design").has_value());  // wrong case ⇒ manual

    // Prefix-only with no mode suffix is illegal for a managed lane ⇒ no mode recovered
    // (defensive: reconcile skips it rather than recording an empty-mode ownership).
    CHECK(!modeIdFromLaneName("reasampler:").has_value());
}

int main() {
    testIsManagedLaneName();
    testManagedLaneKey();
    testIsOnManualLane();
    testRoundTrip();
    testModeIdFromLaneName();

    if (g_fail == 0) std::printf("All tests passed.\n");
    return g_fail ? 1 : 0;
}
