// Wi-Fi profile store and network selection policy (host).
#include "wifi_profiles.h"
#include "wifi_selection.h"
#include <cassert>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>

using namespace wearable;

static std::vector<const WiFiProfile *> order(const WiFiProfileStore &store) {
    std::array<const WiFiProfile *, WIFI_MAX_PROFILES> out{};
    const size_t n = store.selection_order(out);
    std::vector<const WiFiProfile *> result;
    for (size_t i = 0; i < n; ++i) result.push_back(out[i]);
    return result;
}

int main() {
    // ---- Profile: add / replace / remove / clear ------------------------
    MemoryWiFiStorage storage;
    WiFiProfileStore store(storage);
    store.load();
    assert(store.count() == 0);
    assert(store.add_or_replace("Home-WiFi", "secret1") == WiFiStoreResult::Ok);
    assert(store.count() == 1 && std::strcmp(store.at(0).ssid, "Home-WiFi") == 0);
    assert(std::strcmp(store.at(0).password, "secret1") == 0);
    // Replace keeps a single entry and keeps its proven status.
    assert(store.mark_success("Home-WiFi", 5) == WiFiStoreResult::Ok);
    assert(store.add_or_replace("Home-WiFi", "secret2") == WiFiStoreResult::Ok);
    assert(store.count() == 1 && std::strcmp(store.at(0).password, "secret2") == 0);
    assert(store.at(0).last_success == 5);
    assert(store.add_or_replace("Office", "off1") == WiFiStoreResult::Ok);
    assert(store.count() == 2);
    assert(store.remove("Home-WiFi") == WiFiStoreResult::Ok);
    assert(store.count() == 1 && std::strcmp(store.at(0).ssid, "Office") == 0);
    assert(store.remove("Nope") == WiFiStoreResult::NotFound);
    assert(store.clear() == WiFiStoreResult::Ok && store.count() == 0);
    // After any successful write, the store is initialized even when empty.
    assert(store.initialized());

    // ---- Validation ------------------------------------------------------
    assert(store.add_or_replace("", "x") == WiFiStoreResult::EmptySsid);
    std::string too_long(WIFI_SSID_MAX + 1, 'a');
    assert(store.add_or_replace(too_long.c_str(), "x") == WiFiStoreResult::SsidTooLong);
    assert(store.add_or_replace("bad\nssid", "x") == WiFiStoreResult::InvalidSsid);
    std::string long_pw(WIFI_PASSWORD_MAX + 1, 'p');
    assert(store.add_or_replace("Open", long_pw.c_str()) == WiFiStoreResult::PasswordTooLong);
    assert(store.add_or_replace("Open", "") == WiFiStoreResult::Ok);  // open network

    // ---- Max profile count ----------------------------------------------
    store.clear();
    for (size_t i = 0; i < WIFI_MAX_PROFILES; ++i) {
        std::string ssid = "Net" + std::to_string(i);
        assert(store.add_or_replace(ssid.c_str(), "pw") == WiFiStoreResult::Ok);
    }
    assert(store.count() == WIFI_MAX_PROFILES);
    assert(store.add_or_replace("Overflow", "pw") == WiFiStoreResult::Full);
    // Replacing an existing SSID still works at the cap.
    assert(store.add_or_replace("Net0", "pw2") == WiFiStoreResult::Ok);
    assert(store.count() == WIFI_MAX_PROFILES);

    // ---- Persistence across a reload ------------------------------------
    WiFiProfileStore reloaded(storage);
    reloaded.load();
    assert(reloaded.count() == WIFI_MAX_PROFILES);
    assert(std::strcmp(reloaded.at(0).ssid, "Net0") == 0);
    assert(std::strcmp(reloaded.at(0).password, "pw2") == 0);

    // ---- Corrupt data fallback ------------------------------------------
    // Corrupt is a DISTINCT state from Missing: bytes existed but failed
    // validation. That must never look like a first boot.
    storage.image.bytes[0] = 'X';  // break the magic
    WiFiProfileStore corrupt(storage);
    corrupt.load();
    assert(corrupt.count() == 0);              // fail closed, no crash
    assert(corrupt.initialized());             // present, so still initialized
    assert(corrupt.store_was_corrupt());       // ...but flagged corrupt
    assert(!corrupt.store_missing());
    assert(corrupt.load_state() != WiFiStoreLoadState::Missing);
    // A wrong version is also rejected as Corrupt.
    MemoryWiFiStorage versioned;
    versioned.present = true;
    versioned.image.size = 10;
    versioned.image.bytes[0] = 'W'; versioned.image.bytes[1] = 'F';
    versioned.image.bytes[2] = 'P'; versioned.image.bytes[3] = 'S';
    versioned.image.bytes[4] = 0xEE; versioned.image.bytes[5] = 1;
    WiFiProfileStore bad_version(versioned);
    bad_version.load();
    assert(bad_version.count() == 0 && bad_version.initialized() && bad_version.store_was_corrupt());
    // Truncated image with a valid magic is rejected by the CRC/size checks.
    MemoryWiFiStorage truncated;
    truncated.present = true;
    truncated.image = storage.image;
    truncated.image.size = 6;
    WiFiProfileStore bad_size(truncated);
    bad_size.load();
    assert(bad_size.count() == 0 && bad_size.initialized() && bad_size.store_was_corrupt());
    // A genuine storage read failure is neither Missing nor usable.
    MemoryWiFiStorage read_fail;
    read_fail.io_error = true;
    WiFiProfileStore unreadable(read_fail);
    unreadable.load();
    assert(unreadable.count() == 0 && unreadable.initialized() && !unreadable.store_missing());
    assert(unreadable.load_state() == WiFiStoreLoadState::IoError);

    // ---- B6: first-run migration vs. deliberate Forget All ---------------
    // A fresh, never-written store is NOT initialized, so a one-time build-time
    // migration is allowed. After Forget All the store stays initialized, so a
    // reload must never re-import the build-time credential.
    {
        MemoryWiFiStorage fresh;
        WiFiProfileStore first(fresh);
        first.load();
        assert(!first.initialized());          // never configured
        assert(first.store_missing());         // genuinely absent, not corrupt
        assert(!first.store_was_corrupt());
        assert(first.count() == 0);

        // First-boot migration imports the build-time SSID once.
        assert(first.add_or_replace("Dev-WiFi", "dev-pass") == WiFiStoreResult::Ok);
        assert(first.initialized() && first.count() == 1);

        // Forget All persists an initialized-but-empty store.
        assert(first.clear() == WiFiStoreResult::Ok);
        assert(first.count() == 0 && first.initialized());

        // Simulate a reboot: a fresh store object over the same NVS image.
        WiFiProfileStore after_reboot(fresh);
        after_reboot.load();
        assert(after_reboot.count() == 0);
        assert(after_reboot.initialized());
        // The build-time profile must NOT return.
        assert(after_reboot.find("Dev-WiFi") == nullptr);
        // ...and a fresh reload is still empty.
        WiFiProfileStore again(fresh);
        again.load();
        assert(again.count() == 0 && again.initialized());
    }
    // A store that is marked initialized with no profiles also blocks migration.
    {
        MemoryWiFiStorage fresh;
        WiFiProfileStore first(fresh);
        first.load();
        assert(first.mark_initialized() == WiFiStoreResult::Ok);
        assert(first.initialized() && first.count() == 0);
        WiFiProfileStore reload(fresh);
        reload.load();
        assert(reload.initialized() && reload.count() == 0);
        // mark_initialized is idempotent and must not clear existing profiles.
        assert(reload.add_or_replace("Keep", "pw") == WiFiStoreResult::Ok);
        assert(reload.mark_initialized() == WiFiStoreResult::Ok);
        assert(reload.count() == 1 && reload.initialized());
    }
    // A corrupt image is initialized-but-corrupt: zero usable profiles, but it
    // must NOT be treated as a first boot.
    {
        MemoryWiFiStorage broken;
        broken.present = true;
        broken.image.size = 12;
        broken.image.bytes[0] = 'W'; broken.image.bytes[1] = 'F';
        broken.image.bytes[2] = 'P'; broken.image.bytes[3] = 'S';
        broken.image.bytes[4] = WIFI_STORE_VERSION_INITIALIZED; broken.image.bytes[5] = 1;
        broken.image.bytes[6] = 1;
        WiFiProfileStore corrupt_store(broken);
        corrupt_store.load();
        assert(corrupt_store.count() == 0);
        assert(corrupt_store.initialized());
        assert(corrupt_store.store_was_corrupt());
    }
    // A v1 image (no initialized flag) decodes as initialized for compatibility.
    {
        MemoryWiFiStorage legacy;
        legacy.present = true;
        // Build a v1 image with one profile through a temporary v1 encoder is not
        // exposed, so construct the equivalent bytes directly and fix the CRC.
        auto &b = legacy.image.bytes;
        size_t n = 0;
        b[n++] = 'W'; b[n++] = 'F'; b[n++] = 'P'; b[n++] = 'S';
        b[n++] = WIFI_STORE_VERSION;  // v1
        b[n++] = 1;                    // one profile
        b[n++] = 3; b[n++] = 'O'; b[n++] = 'l'; b[n++] = 'd';
        b[n++] = 2; b[n++] = 'p'; b[n++] = 'w';
        for (unsigned i = 0; i < 4; ++i) b[n++] = 0;
        // crc32 over the image minus the trailing four bytes.
        uint32_t crc = 0xffffffffU;
        for (size_t i = 0; i < n; ++i) {
            crc ^= b[i];
            for (int j = 0; j < 8; ++j) crc = (crc >> 1) ^ (0xedb88320U & (0U - (crc & 1)));
        }
        crc = ~crc;
        for (unsigned i = 0; i < 4; ++i) b[n++] = uint8_t(crc >> (i * 8));
        legacy.image.size = n;
        WiFiProfileStore legacy_store(legacy);
        legacy_store.load();
        assert(legacy_store.initialized());
        assert(legacy_store.count() == 1 && std::strcmp(legacy_store.at(0).ssid, "Old") == 0);
    }

    // ---- Selection: last successful preferred ---------------------------
    {
        MemoryWiFiStorage s;
        WiFiProfileStore p(s);
        p.load();
        p.add_or_replace("A", "1");
        p.add_or_replace("B", "2");
        p.add_or_replace("C", "3");
        p.mark_success("B", 11);   // most recent
        p.mark_success("A", 4);    // older proof
        auto seq = order(p);
        assert(seq.size() == 3);
        // Proven networks first, newest proof first, then never-proven ones.
        assert(std::strcmp(seq[0]->ssid, "B") == 0);
        assert(std::strcmp(seq[1]->ssid, "A") == 0);
        assert(std::strcmp(seq[2]->ssid, "C") == 0);
    }
    // ---- Selection: no proven network keeps insertion order -------------
    {
        MemoryWiFiStorage s;
        WiFiProfileStore p(s);
        p.load();
        p.add_or_replace("One", "1");
        p.add_or_replace("Two", "2");
        auto seq = order(p);
        assert(std::strcmp(seq[0]->ssid, "One") == 0);
        assert(std::strcmp(seq[1]->ssid, "Two") == 0);
    }
    // ---- Selection: known network absent, fallback happens --------------
    {
        MemoryWiFiStorage s;
        WiFiProfileStore p(s);
        p.load();
        p.add_or_replace("Gone", "");
        p.add_or_replace("Present", "");
        // The store is independent of scanning; it always yields every known
        // network so the station manager can try each and fall back.
        assert(order(p).size() == 2);
    }

    // ---- Backoff is bounded and resets ---------------------------------
    WifiRetryBackoff backoff(2000, 60000);
    assert(backoff.next_delay() == 2000);
    backoff.failed(); assert(backoff.next_delay() == 4000);
    backoff.failed(); assert(backoff.next_delay() == 8000);
    for (int i = 0; i < 10; ++i) backoff.failed();
    assert(backoff.next_delay() == 60000);
    backoff.succeeded();
    assert(backoff.next_delay() == 2000 && backoff.attempts() == 0);

    // ---- No auto-SoftAP: only an explicit confirmed request starts it ----
    ProvisioningMode mode = ProvisioningMode::Normal;
    // Connection failures never change the mode.
    assert(access_point_should_run(mode) == false);
    assert(request_start_access_point(mode, false) == ProvisioningMode::Normal);
    assert(request_start_access_point(mode, true) == ProvisioningMode::StartingAccessPoint);
    mode = request_start_access_point(mode, true);
    assert(access_point_should_run(mode));
    mode = access_point_ready(mode, true);
    assert(mode == ProvisioningMode::Provisioning && access_point_should_run(mode));
    mode = exit_provisioning(mode);
    assert(mode == ProvisioningMode::Normal && !access_point_should_run(mode));
    // AP start failure is a distinct, reported state, not a silent retry.
    assert(access_point_ready(ProvisioningMode::StartingAccessPoint, false) == ProvisioningMode::Failed);
    assert(!access_point_should_run(ProvisioningMode::Failed));
    // A second start request while already provisioning is ignored.
    assert(request_start_access_point(ProvisioningMode::Provisioning, true) == ProvisioningMode::Provisioning);
    // Pending approval blocks opening Wi-Fi Setup.
    assert(!wifi_setup_allowed(true) && wifi_setup_allowed(false));

    // ---- Candidate classification ---------------------------------------
    assert(classify_candidate_failure(0, false) == CandidateResult::NotFound);
    assert(classify_candidate_failure(15, true) == CandidateResult::Failed);   // handshake timeout
    assert(classify_candidate_failure(205, true) == CandidateResult::Failed);  // bad password
    assert(classify_candidate_failure(202, true) == CandidateResult::Failed);  // auth fail
    assert(std::strcmp(candidate_reason(CandidateResult::Success), "Connected") == 0);
    assert(std::strcmp(candidate_reason(CandidateResult::NotFound), "Network not found") == 0);
    assert(std::strcmp(candidate_reason(CandidateResult::StorageError), "Connected; not saved") == 0);
    assert(std::string(candidate_reason(CandidateResult::Failed)).find("password") != std::string::npos);
    assert(std::string(candidate_reason(CandidateResult::NoCompatible)).size() <= 22);

    // ---- Candidate-first: a failed candidate preserves existing profiles --
    {
        MemoryWiFiStorage s;
        WiFiProfileStore p(s);
        p.load();
        p.add_or_replace("Keep", "keep-pass");
        // A candidate is only persisted on success. A failure never calls
        // add_or_replace, so the existing credential is untouched.
        const auto before = p.count();
        assert(p.count() == before && p.find("Keep") != nullptr);
        // Success then persists and marks proven.
        assert(p.add_or_replace("New", "new-pass") == WiFiStoreResult::Ok);
        assert(p.mark_success("New", 9) == WiFiStoreResult::Ok);
        assert(p.find("New")->last_success == 9);
        assert(p.find("Keep") != nullptr);
    }

    // ---- Stable SoftAP SSID ---------------------------------------------
    char ssid[32];
    provisioning_ssid("a2f3", ssid, sizeof(ssid));
    assert(std::strcmp(ssid, "AgentPager-A2F3") == 0);
    provisioning_ssid("A2F3", ssid, sizeof(ssid));
    assert(std::strcmp(ssid, "AgentPager-A2F3") == 0);
    provisioning_ssid(nullptr, ssid, sizeof(ssid));
    assert(std::strcmp(ssid, "AgentPager-0000") == 0);
    // Short suffixes pad with zeroes so the SSID length never varies.
    provisioning_ssid("1", ssid, sizeof(ssid));
    assert(std::strcmp(ssid, "AgentPager-1000") == 0);

    // ---- QR payload: standard Wi-Fi form, escaping, no device token ------
    char qr[160];
    provisioning_qr_payload("AgentPager-A2F3", PROVISIONING_AP_PASSWORD, qr, sizeof(qr));
    assert(std::string(qr) == "WIFI:T:WPA;S:AgentPager-A2F3;P:agentpager-setup;;");
    provisioning_qr_payload("a;b,c:d\"e\\f", "pw", qr, sizeof(qr));
    assert(std::string(qr).find("S:a\\;b\\,c\\:d\\\"e\\\\f;") != std::string::npos);
    provisioning_qr_payload("S", "P", qr, sizeof(qr));
    assert(std::strcmp(qr, "WIFI:T:WPA;S:S;P:P;;") == 0);
    // The fixed password only needs to satisfy WPA2 minimum length.
    assert(std::strlen(PROVISIONING_AP_PASSWORD) >= 8);

    // ---- Secrets never leak through formatting helpers -------------------
    // The profile struct's only human-facing projection is the SSID; there is
    // deliberately no function that returns a password for display.
    assert(std::strcmp(wifi_link_label(WifiLinkState::Connected), "CONNECTED") == 0);
    assert(std::strcmp(wifi_link_label(WifiLinkState::NoKnownWifi), "NO KNOWN WIFI") == 0);

    // ---- B7: candidate test always disconnects before it connects --------
    // The plan is the same whether or not the station is currently associated:
    // a disconnect is an unconditional precondition, so there is no code path
    // that connects on a live link.
    {
        CandidateStep steps[8]{};
        const size_t count = candidate_plan(CandidatePlan{true, true}, steps, 8);
        assert(count == 6);
        assert(steps[0] == CandidateStep::PauseReconnect);
        assert(steps[1] == CandidateStep::DisconnectCurrent);
        assert(steps[2] == CandidateStep::WaitDisconnected);
        assert(steps[3] == CandidateStep::SetConfig);
        assert(steps[4] == CandidateStep::Connect);
        assert(steps[5] == CandidateStep::AwaitResult);
        // Disconnect must precede connect in the ordering.
        size_t disconnect_at = 8, connect_at = 8;
        for (size_t i = 0; i < count; ++i) {
            if (steps[i] == CandidateStep::DisconnectCurrent) disconnect_at = i;
            if (steps[i] == CandidateStep::Connect) connect_at = i;
        }
        assert(disconnect_at < connect_at);
        // Identical plan when the station is idle.
        CandidateStep idle_steps[8]{};
        assert(candidate_plan(CandidatePlan{false, true}, idle_steps, 8) == count);
        for (size_t i = 0; i < count; ++i) assert(idle_steps[i] == steps[i]);
        // A null output still reports the count without writing.
        assert(candidate_plan(CandidatePlan{}, nullptr, 0) == count);
        // A short buffer is never overrun.
        CandidateStep tiny[2]{};
        assert(candidate_plan(CandidatePlan{}, tiny, 2) == count);
        assert(tiny[0] == CandidateStep::PauseReconnect && tiny[1] == CandidateStep::DisconnectCurrent);
    }
    // A failed candidate is dropped and the normal manager resumes, so a known
    // profile can reconnect; success keeps the link and persists.
    {
        const auto failed = candidate_aftermath(CandidateResult::Failed);
        assert(failed.disconnect_candidate && failed.resume_reconnect && !failed.persist);
        const auto not_found = candidate_aftermath(CandidateResult::NotFound);
        assert(not_found.disconnect_candidate && not_found.resume_reconnect && !not_found.persist);
        const auto no_compat = candidate_aftermath(CandidateResult::NoCompatible);
        assert(no_compat.disconnect_candidate && !no_compat.persist);
        const auto storage = candidate_aftermath(CandidateResult::StorageError);
        assert(!storage.disconnect_candidate && !storage.persist);
        const auto success = candidate_aftermath(CandidateResult::Success);
        assert(!success.disconnect_candidate && success.persist);
    }

    // ---- B9: migration policy distinguishes Missing / Valid / Corrupt -----
    // The pure policy is the single source of truth for the boot decision.
    {
        // Fresh first boot: key absent, build-time SSID present -> migrate once.
        assert(should_migrate_build_wifi(WiFiStoreLoadState::Missing, false, false, true));
        // No build-time SSID -> nothing to migrate.
        assert(!should_migrate_build_wifi(WiFiStoreLoadState::Missing, false, false, false));
        // Valid store (with or without profiles) -> never migrate.
        assert(!should_migrate_build_wifi(WiFiStoreLoadState::Present, true, false, true));
        // Corrupt store -> never migrate, even though initialized() is true.
        assert(!should_migrate_build_wifi(WiFiStoreLoadState::Present, true, true, true));
        assert(!should_migrate_build_wifi(WiFiStoreLoadState::IoError, true, true, true));
        // Unreadable store -> never migrate.
        assert(!should_migrate_build_wifi(WiFiStoreLoadState::IoError, true, false, true));
        // A defensive case: initialized true but state Missing cannot arise, yet
        // it must still refuse to migrate.
        assert(!should_migrate_build_wifi(WiFiStoreLoadState::Missing, true, false, true));
    }
    // End-to-end policy over real stores, matching the required scenarios.
    {
        const bool config_present = true;
        // (1) Fresh first boot: NVS key absent + build-time Wi-Fi -> migrate once.
        {
            MemoryWiFiStorage fresh;
            WiFiProfileStore store(fresh);
            store.load();
            assert(should_migrate_build_wifi(store.load_state(), store.initialized(),
                                             store.store_was_corrupt(), config_present));
            assert(store.add_or_replace("Dev-WiFi", "dev") == WiFiStoreResult::Ok);
            // After migrating, a reload/reboot never migrates again.
            WiFiProfileStore after(fresh);
            after.load();
            assert(after.count() == 1);
            assert(!should_migrate_build_wifi(after.load_state(), after.initialized(),
                                              after.store_was_corrupt(), config_present));
        }
        // (2) Forget All: valid initialized empty store -> no migration.
        {
            MemoryWiFiStorage s;
            WiFiProfileStore store(s);
            store.load();
            assert(store.add_or_replace("Home", "pw") == WiFiStoreResult::Ok);
            assert(store.clear() == WiFiStoreResult::Ok);
            WiFiProfileStore after_reboot(s);
            after_reboot.load();
            assert(after_reboot.count() == 0 && after_reboot.initialized());
            assert(!should_migrate_build_wifi(after_reboot.load_state(), after_reboot.initialized(),
                                              after_reboot.store_was_corrupt(), config_present));
            assert(after_reboot.find("Dev-WiFi") == nullptr);
        }
        // (3) Corrupt CRC: key exists, bad CRC, build-time Wi-Fi -> no migration.
        {
            MemoryWiFiStorage s;
            WiFiProfileStore writer(s);
            writer.load();
            assert(writer.add_or_replace("Home", "pw") == WiFiStoreResult::Ok);
            s.image.bytes[s.image.size - 1] ^= 0xFF;  // corrupt the CRC
            WiFiProfileStore store(s);
            store.load();
            assert(store.count() == 0 && store.store_was_corrupt());
            assert(!should_migrate_build_wifi(store.load_state(), store.initialized(),
                                              store.store_was_corrupt(), config_present));
        }
        // (4) Unsupported version: key exists, bad version -> no migration.
        {
            MemoryWiFiStorage s;
            s.present = true;
            s.image.size = 10;
            s.image.bytes[0] = 'W'; s.image.bytes[1] = 'F';
            s.image.bytes[2] = 'P'; s.image.bytes[3] = 'S';
            s.image.bytes[4] = 0x7F; s.image.bytes[5] = 0;
            WiFiProfileStore store(s);
            store.load();
            assert(store.count() == 0 && store.store_was_corrupt());
            assert(!should_migrate_build_wifi(store.load_state(), store.initialized(),
                                              store.store_was_corrupt(), config_present));
        }
        // (5) Truncated image: key exists, short payload -> no migration.
        {
            MemoryWiFiStorage s;
            s.present = true;
            s.image.size = 5;
            s.image.bytes[0] = 'W'; s.image.bytes[1] = 'F';
            s.image.bytes[2] = 'P'; s.image.bytes[3] = 'S'; s.image.bytes[4] = WIFI_STORE_VERSION_INITIALIZED;
            WiFiProfileStore store(s);
            store.load();
            assert(store.count() == 0 && store.store_was_corrupt());
            assert(!should_migrate_build_wifi(store.load_state(), store.initialized(),
                                              store.store_was_corrupt(), config_present));
        }
        // (6) Existing valid v1 and v2 stores load normally and never migrate.
        {
            MemoryWiFiStorage s;
            WiFiProfileStore writer(s);
            writer.load();
            assert(writer.add_or_replace("Home", "pw") == WiFiStoreResult::Ok);
            // v2 (current) image.
            WiFiProfileStore v2(s);
            v2.load();
            assert(v2.count() == 1 && v2.initialized() && !v2.store_was_corrupt());
            assert(!should_migrate_build_wifi(v2.load_state(), v2.initialized(),
                                              v2.store_was_corrupt(), config_present));
            // v1 image built by hand.
            MemoryWiFiStorage legacy;
            legacy.present = true;
            auto &b = legacy.image.bytes;
            size_t n = 0;
            b[n++] = 'W'; b[n++] = 'F'; b[n++] = 'P'; b[n++] = 'S';
            b[n++] = WIFI_STORE_VERSION;  // v1
            b[n++] = 1;
            b[n++] = 3; b[n++] = 'O'; b[n++] = 'l'; b[n++] = 'd';
            b[n++] = 2; b[n++] = 'p'; b[n++] = 'w';
            for (unsigned i = 0; i < 4; ++i) b[n++] = 0;
            uint32_t crc = 0xffffffffU;
            for (size_t i = 0; i < n; ++i) {
                crc ^= b[i];
                for (int j = 0; j < 8; ++j) crc = (crc >> 1) ^ (0xedb88320U & (0U - (crc & 1)));
            }
            crc = ~crc;
            for (unsigned i = 0; i < 4; ++i) b[n++] = uint8_t(crc >> (i * 8));
            legacy.image.size = n;
            WiFiProfileStore v1(legacy);
            v1.load();
            assert(v1.count() == 1 && v1.initialized() && !v1.store_was_corrupt());
            assert(!should_migrate_build_wifi(v1.load_state(), v1.initialized(),
                                              v1.store_was_corrupt(), config_present));
        }
    }

    // ---- Reverse validation: the OLD policy must fail the corrupt case -----
    // The previous rule was `!initialized && config_present`. Under it, a corrupt
    // store is indistinguishable from a first boot and would be migrated. Assert
    // that the old rule gives the WRONG answer where the new policy is correct.
    {
        MemoryWiFiStorage s;
        s.present = true;
        s.image.size = 12;
        s.image.bytes[0] = 'W'; s.image.bytes[1] = 'F';
        s.image.bytes[2] = 'P'; s.image.bytes[3] = 'S';
        s.image.bytes[4] = WIFI_STORE_VERSION_INITIALIZED; s.image.bytes[5] = 1;
        s.image.bytes[6] = 1;
        WiFiProfileStore store(s);
        store.load();
        const bool config_present = true;
        // The new policy refuses.
        assert(!should_migrate_build_wifi(store.load_state(), store.initialized(),
                                          store.store_was_corrupt(), config_present));
        // The old rule would have migrated. Simulate it against the raw load
        // state that a bool-returning storage produced for both missing and
        // corrupt images.
        const bool old_initialized = (store.load_state() == WiFiStoreLoadState::Present &&
                                      !store.store_was_corrupt());
        const bool old_policy_would_migrate = !old_initialized && config_present;
        assert(old_policy_would_migrate);  // proves the old rule was unsafe here
    }

    std::puts("Wi-Fi profile store, validation, selection, backoff, no-auto-SoftAP, candidate-first, "
              "initialized-marker/first-run migration, Missing/Valid/Corrupt policy, "
              "candidate-disconnect ordering, SSID/QR PASS");
}