#include "wifi_manager.h"

#include <algorithm>
#include <atomic>
#include <cstdio>
#include <cstring>
#include <new>
#include <string>
#include <vector>

#include "board.h"
#include "cloud_client.h"
#include "esp_event.h"
#include "esp_http_server.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "esp_netif.h"
#include "esp_timer.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "board_network.h"
#include "portal_html.h"
#include "wifi_usb_protocol.h"
#include "wifi_connection_ownership.h"
#include "wifi_store_nvs.h"

namespace {

constexpr const char *TAG = "WIFI";
constexpr int kMaxScanResults = 20;
constexpr uint32_t kConnectTimeoutMs = 15000;
// Bounded wait for the station to leave an existing association before a
// candidate test connects. Kept well under kConnectTimeoutMs.
constexpr uint32_t kDisconnectSettleMs = 3000;
constexpr uint32_t kIdleProvisioningTimeoutMs = 15u * 60u * 1000u;

EventGroupHandle_t s_events;
wearable::NvsWiFiStorage s_storage;
wearable::WiFiProfileStore s_store(s_storage);
SemaphoreHandle_t s_lock;
esp_netif_t *s_sta_netif = nullptr;
esp_netif_t *s_ap_netif = nullptr;
httpd_handle_t s_portal = nullptr;

std::atomic<wearable::WifiLinkState> s_state{wearable::WifiLinkState::NoKnownWifi};
std::atomic<wearable::ProvisioningMode> s_provisioning{wearable::ProvisioningMode::Normal};
std::atomic<wearable::CandidateResult> s_last_candidate{wearable::CandidateResult::Pending};
std::atomic<bool> s_ap_running{false};
// Recency sequence for mark_success. Seeded from the highest persisted value
// after load() so a network connected after a reboot is always the newest.
std::atomic<uint32_t> s_success_sequence{0};

char s_current_ssid[wearable::WIFI_SSID_MAX + 1] = {};
int s_current_profile = -1;
// Last requested SSID for Portal status; worker credentials live in its request.
char s_candidate_ssid[wearable::WIFI_SSID_MAX + 1] = {};
std::atomic<bool> s_candidate_pending{false};
// Protected by TransitionGuard. A worker may only finish its own reservation.
uint32_t s_candidate_generation = 0;
uint32_t s_provision_generation = 0;
bool s_portal_stopping = false;

struct CandidateRequest {
    wearable::ConnectionAttempt attempt;
    uint32_t provision_generation = 0;
    char ssid[wearable::WIFI_SSID_MAX + 1] = {};
    char password[wearable::WIFI_PASSWORD_MAX + 1] = {};
};

// ---------------------------------------------------------------------------
// Connection ownership
//
// Exactly one actor owns the station interface at a time. The pure machine in
// wifi_connection_ownership.h decides what may happen; this binding performs the
// driver calls. Every connect/waiter/GOT_IP resolution is bound to a generation,
// so a stale station waiter can never consume a candidate's GOT_IP (B6), a
// candidate failure releases ownership so the station reconnects a known profile
// (B4), and a steady connection is never re-established on a timer (B3).
//
// Concurrency: `s_own` is shared by station_task, candidate_task (Portal), the
// USB task, the Wi-Fi event handler and board callbacks. All of them run on
// different FreeRTOS tasks, so the machine is NOT treated as lock-free: every
// read, transition and resolve is serialized by the dedicated `s_own_lock`
// recursive mutex through the Own* helpers below. The lock is held only for the
// duration of an ownership read/mutation (a few dozen instructions); it is
// never held across esp_wifi_* calls, vTaskDelay, logging or store access, so
// no task can be blocked on the Wi-Fi driver while holding it.
// ---------------------------------------------------------------------------
wearable::ConnectionOwnership s_own;
SemaphoreHandle_t s_own_lock = nullptr;
SemaphoreHandle_t s_transition_lock = nullptr;
SemaphoreHandle_t s_portal_lock = nullptr;

// Serialize an ownership decision together with its driver/state/store effects.
// OwnGuard still protects only the pure machine. Never hold TransitionGuard
// during a connection wait, vTaskDelay, or HTTP server start/stop. Lock order:
// PortalGuard -> TransitionGuard -> (OwnGuard OR Guard), never OwnGuard <-> Guard.
struct TransitionGuard {
    TransitionGuard() { if (s_transition_lock) xSemaphoreTakeRecursive(s_transition_lock, portMAX_DELAY); }
    ~TransitionGuard() { if (s_transition_lock) xSemaphoreGiveRecursive(s_transition_lock); }
    TransitionGuard(const TransitionGuard &) = delete;
    TransitionGuard &operator=(const TransitionGuard &) = delete;
};
struct PortalGuard {
    PortalGuard() { if (s_portal_lock) xSemaphoreTake(s_portal_lock, portMAX_DELAY); }
    ~PortalGuard() { if (s_portal_lock) xSemaphoreGive(s_portal_lock); }
    PortalGuard(const PortalGuard &) = delete;
    PortalGuard &operator=(const PortalGuard &) = delete;
};

// Recursive so a locked helper may call another locked helper without
// self-deadlocking (the ownership API is not reentrant by design, but this
// removes an entire class of accidental recursive-lock bugs). Non-copyable so a
// guard can never be duplicated and double-released.
struct OwnGuard {
    OwnGuard() { if (s_own_lock) xSemaphoreTakeRecursive(s_own_lock, portMAX_DELAY); }
    ~OwnGuard() { if (s_own_lock) xSemaphoreGiveRecursive(s_own_lock); }
    OwnGuard(const OwnGuard &) = delete;
    OwnGuard &operator=(const OwnGuard &) = delete;
};

// ---- Locked ownership accessors -------------------------------------------
// Each of these takes the lock, touches ownership state, and returns. Nothing
// else may read or mutate `s_own` directly.
bool own_station_steady() {
    OwnGuard guard;
    return s_own.station_steady();
}
bool own_candidate_owns() {
    OwnGuard guard;
    return s_own.owner() == wearable::ConnectionOwner::Candidate;
}
wearable::ConnectionAttempt own_station_begin(const char *ssid) {
    OwnGuard guard;
    return s_own.station_begin(ssid);
}
wearable::AttemptOutcome own_station_resolve(const wearable::ConnectionAttempt &attempt, bool got_ip,
                                             const char *actual_ssid) {
    OwnGuard guard;
    return s_own.station_resolve(attempt, got_ip, actual_ssid);
}
void own_station_disconnected() {
    OwnGuard guard;
    s_own.station_disconnected();
}
void own_station_idle() {
    OwnGuard guard;
    s_own.station_idle();
}
wearable::ConnectionAttempt own_take_candidate(const char *ssid) {
    OwnGuard guard;
    return s_own.take_candidate(ssid);
}
wearable::AttemptOutcome own_candidate_resolve(const wearable::ConnectionAttempt &attempt, bool got_ip,
                                               const char *actual_ssid,
                                               wearable::AttemptDirective &directive) {
    OwnGuard guard;
    return s_own.candidate_resolve(attempt, got_ip, actual_ssid, directive);
}
void own_forget_current() {
    OwnGuard guard;
    s_own.forget_current();
}

bool own_is_current(const wearable::ConnectionAttempt &attempt) {
    OwnGuard guard;
    return s_own.is_current(attempt);
}

// Atomically cancel an in-flight candidate and report whether the station still
// owns the interface. Used by the Human Gate preemption so the check and the
// cancel cannot interleave with the candidate task's own transition.
bool own_cancel_candidate_if_any() {
    OwnGuard guard;
    return s_own.cancel_candidate();
}

// Bumped by the event handler on every GOT_IP. A waiter snapshots it before
// connecting and only accepts a wake-up that moved it, so a stale EventGroup bit
// from a previous owner cannot satisfy the current attempt.
std::atomic<uint32_t> s_event_generation{0};
char s_provision_ssid[40] = {};
char s_provision_qr[160] = {};
uint64_t s_provision_started_ms = 0;

struct Guard {
    Guard() { if (s_lock) xSemaphoreTake(s_lock, portMAX_DELAY); }
    ~Guard() { if (s_lock) xSemaphoreGive(s_lock); }
};

// ---- Disconnect reason tracking ------------------------------------------
// The last disconnect reason feeds candidate classification without leaking a
// numeric code to the user.
std::atomic<int> s_last_reason{0};

void set_state(wearable::WifiLinkState state) {
    s_state.store(state, std::memory_order_relaxed);
}

// UI callbacks acquire the display mutex. Publish a coherent snapshot after
// releasing every Wi-Fi lock, since the UI calls back into the manager too.
void wifi_status_task(void *) {
    wearable::WifiLinkState reported_state = wearable::WifiLinkState::Offline;
    char reported_ssid[wearable::WIFI_SSID_MAX + 1] = {};
    bool reported = false;
    for (;;) {
        wearable::WifiLinkState state;
        char ssid[wearable::WIFI_SSID_MAX + 1] = {};
        {
            TransitionGuard transition;
            state = s_state.load();
            std::strcpy(ssid, s_current_ssid);
        }
        if (!reported || state != reported_state || std::strcmp(ssid, reported_ssid)) {
            board_set_wifi_status(state == wearable::WifiLinkState::Connected, ssid);
            reported_state = state;
            std::strcpy(reported_ssid, ssid);
            reported = true;
        }
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

// Fill a driver STA config from a credential pair without truncation. See
// `exact_copy_field` in wifi_profiles.h: the fixed arrays do not require a NUL
// terminator, so a 32-byte SSID or 64-char PSK must survive exactly.
bool populate_sta_config(wifi_config_t &config, const char *ssid, const char *password) {
    config = wifi_config_t{};
    const auto ssid_copy = wearable::exact_copy_field(
        reinterpret_cast<char *>(config.sta.ssid), sizeof(config.sta.ssid), ssid);
    const auto password_copy = wearable::exact_copy_field(
        reinterpret_cast<char *>(config.sta.password), sizeof(config.sta.password), password);
    if (!ssid_copy.ok || !password_copy.ok) return false;
    const bool open = password == nullptr || password[0] == '\0';
    config.sta.threshold.authmode = open ? WIFI_AUTH_OPEN : WIFI_AUTH_WPA2_PSK;
    config.sta.pmf_cfg.capable = true;
    config.sta.pmf_cfg.required = false;
    return true;
}

// The SSID the driver is actually associated with, or "" when not associated.
// `esp_wifi_sta_get_ap_info` reports the associated AP; comparing its SSID to
// the expected one is the last line of defence against persisting a candidate
// that never really connected (B5).
bool current_ap_ssid(char *out, size_t out_size) {
    if (!out || out_size == 0) return false;
    out[0] = '\0';
    wifi_ap_record_t ap = {};
    if (esp_wifi_sta_get_ap_info(&ap) != ESP_OK) return false;
    size_t length = 0;
    while (length < sizeof(ap.ssid) && ap.ssid[length] != 0) ++length;
    if (length == 0 || length >= out_size) return false;
    std::memcpy(out, ap.ssid, length);
    out[length] = '\0';
    return true;
}

// Caller holds TransitionGuard; only acts on the supplied live reservation.
wearable::CandidateResult fail_candidate(const wearable::ConnectionAttempt &attempt) {
    if (!own_is_current(attempt)) return wearable::CandidateResult::Failed;
    esp_wifi_disconnect();
    own_cancel_candidate_if_any();
    s_current_ssid[0] = '\0';
    s_current_profile = -1;
    set_state(wearable::WifiLinkState::Offline);
    return wearable::classify_candidate_failure(s_last_reason.load(), true);
}

// Commit a verified connection: adopt the SSID, advance the recency sequence
// (seeded across reboots) and persist exactly one mark_success.
void apply_success(const char *ssid) {
    std::strncpy(s_current_ssid, ssid, sizeof(s_current_ssid) - 1);
    s_current_ssid[sizeof(s_current_ssid) - 1] = '\0';
    {
        Guard guard;
        const uint32_t sequence =
            wearable::next_success_sequence(s_success_sequence.load());
        s_success_sequence.store(sequence);
        s_store.mark_success(ssid, sequence);
        for (size_t p = 0; p < s_store.count(); ++p)
            if (s_store.at(p).same_ssid(ssid)) s_current_profile = int(p);
    }
}

bool connect_profile(const wearable::WiFiProfile &profile) {
    wifi_config_t config = {};
    if (!populate_sta_config(config, profile.ssid, profile.password)) {
        // A credential longer than the driver field is a programming error, not
        // a runtime condition; report failure rather than truncating silently.
        ESP_LOGW(TAG, "[WIFI] profile rejected: credential exceeds driver field");
        return false;
    }
    if (esp_wifi_set_config(WIFI_IF_STA, &config) != ESP_OK) return false;
    // Log only the SSID; never the credential.
    ESP_LOGI(TAG, "[WIFI] connecting ssid=%s", profile.ssid);
    return esp_wifi_connect() == ESP_OK;
}

// Boot the station and try every known network in selection order. The device
// never starts the SoftAP here.
//
// Ownership rules (B3/B4/B6):
//   * While the station is CONNECTED and still owns the interface, the loop does
//     nothing at all: no set_config, no connect, no mark_success. A successful
//     link is left alone until a real disconnect, a candidate takeover, a
//     profile removal or an explicit transition happens.
//   * While a candidate owns the interface the loop only waits; it never touches
//     the driver and never consumes the candidate's GOT_IP.
//   * Only a genuine OFFLINE/CONNECTING -> verified GOT_IP transition writes
//     mark_success, exactly once.
void station_task(void *) {
    for (;;) {
        std::array<wearable::WiFiProfile, wearable::WIFI_MAX_PROFILES> profiles{};
        size_t count = 0;
        bool paused = false;
        bool steady = false;
        {
            TransitionGuard transition;
            paused = own_candidate_owns();
            steady = own_station_steady();
            if (!paused && !steady) {
                // Copy the values before releasing the store lock. Forget can
                // otherwise shift selection_order's pointers to another SSID.
                {
                    Guard guard;
                    std::array<const wearable::WiFiProfile *, wearable::WIFI_MAX_PROFILES> order{};
                    count = s_store.selection_order(order);
                    for (size_t i = 0; i < count; ++i) profiles[i] = *order[i];
                }
                if (count == 0) {
                    own_station_idle();
                    set_state(wearable::WifiLinkState::NoKnownWifi);
                }
            }
        }
        if (paused || steady || count == 0) {
            vTaskDelay(pdMS_TO_TICKS(paused ? 200 : steady ? 1000 : 5000));
            continue;
        }
        bool connected = false;
        bool superseded = false;
        for (size_t i = 0; i < count; ++i) {
            const auto &profile = profiles[i];
            wearable::ConnectionAttempt attempt;
            uint32_t wait_generation = 0;
            {
                TransitionGuard transition;
                if (own_candidate_owns() || own_station_steady()) { superseded = true; break; }
                bool exists = false;
                {
                    Guard guard;
                    for (size_t p = 0; p < s_store.count(); ++p)
                        if (s_store.at(p).same_ssid(profile.ssid)) exists = true;
                }
                if (!exists) continue;
                attempt = own_station_begin(profile.ssid);
                if (!attempt.valid) { superseded = true; break; }
                set_state(wearable::WifiLinkState::Connecting);
                // Capture BEFORE connect: a fast GOT_IP must not be missed.
                wait_generation = s_event_generation.load();
                if (!connect_profile(profile)) {
                    esp_wifi_disconnect();
                    own_station_idle();
                    set_state(wearable::WifiLinkState::Offline);
                    continue;
                }
            }
            bool got_ip = false;
            const uint64_t deadline = uint64_t(esp_timer_get_time() / 1000) + kConnectTimeoutMs;
            while (uint64_t(esp_timer_get_time() / 1000) < deadline) {
                if (!own_is_current(attempt)) break;
                if (s_event_generation.load() != wait_generation) { got_ip = true; break; }
                vTaskDelay(pdMS_TO_TICKS(50));
            }
            {
                TransitionGuard transition;
                if (!own_is_current(attempt)) { superseded = true; break; }
                char actual[wearable::WIFI_SSID_MAX + 1] = {};
                if (got_ip) current_ap_ssid(actual, sizeof(actual));
                const auto outcome = own_station_resolve(attempt, got_ip, actual);
                if (outcome == wearable::AttemptOutcome::Accepted) {
                    // Resolve and commit are one transaction relative to a
                    // candidate takeover, disconnect, or Forget.
                    apply_success(profile.ssid);
                    set_state(wearable::WifiLinkState::Connected);
                    ESP_LOGI(TAG, "[WIFI] connection success ssid=%s", profile.ssid);
                    connected = true;
                    break;
                }
                if (outcome == wearable::AttemptOutcome::Stale) { superseded = true; break; }
                esp_wifi_disconnect();
                own_station_idle();
                set_state(wearable::WifiLinkState::Offline);
            }
        }
        // In particular, never clear ownership/state after a stale waiter.
        if (!connected && !superseded) vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

void handle_wifi_event(void *, esp_event_base_t base, int32_t id, void *data) {
    if (base == WIFI_EVENT && id == WIFI_EVENT_STA_DISCONNECTED) {
        TransitionGuard transition;
        // A queued disconnect from the previous association must not clear a
        // newer verified link that the driver already reports as associated.
        char actual[wearable::WIFI_SSID_MAX + 1] = {};
        if (current_ap_ssid(actual, sizeof(actual))) return;
        const auto *event = static_cast<wifi_event_sta_disconnected_t *>(data);
        s_last_reason.store(event ? int(event->reason) : 0, std::memory_order_relaxed);
        xEventGroupClearBits(s_events, WIFI_CONNECTED_BIT);
        // The ownership machine decides who, if anyone, this disconnect belongs
        // to. A candidate's own disconnect cycle must not be reported as a
        // station link loss.
        own_station_disconnected();
        if (s_state.load() == wearable::WifiLinkState::Connected) {
            s_current_ssid[0] = '\0';
            s_current_profile = -1;
            set_state(wearable::WifiLinkState::Connecting);
        }
        return;
    }
    if (base == IP_EVENT && id == IP_EVENT_STA_GOT_IP) {
        auto *event = static_cast<ip_event_got_ip_t *>(data);
        char ip[16] = {};
        if (event) std::snprintf(ip, sizeof(ip), IPSTR, IP2STR(&event->ip_info.ip));
        ESP_LOGI(TAG, "[WIFI] got ip %s", ip);
        if (event) {
            // Bump the generation-tagged signal the current owner is waiting on.
            // The bit is also set for the candidate path, but a stale waiter
            // still re-checks ownership before accepting the result.
            s_event_generation.fetch_add(1);
            xEventGroupSetBits(s_events, WIFI_CONNECTED_BIT);
        }
    }
}

// ---- Portal ---------------------------------------------------------------

const char *kPortalIndex = R"HTML(<!doctype html><html><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>AgentPager Wi-Fi Setup</title><style>
body{font-family:system-ui,sans-serif;margin:0;padding:24px;background:#101418;color:#eee}
h1{font-size:20px;margin:0 0 4px}p.note{color:#9aa;margin:0 0 16px;font-size:13px}
ul{list-style:none;padding:0;margin:0 0 16px}li{margin:0 0 6px}
button,input{font:inherit;padding:10px;border-radius:8px;border:1px solid #345;background:#182028;color:#eee}
button{background:#1f6feb;border-color:#1f6feb;width:100%%;margin-top:12px}
label{display:block;margin:12px 0 4px;font-size:13px;color:#9aa}
input{width:100%%;box-sizing:border-box}
.status{margin-top:16px;padding:10px;border-radius:8px;background:#182028;font-size:14px}
</style></head><body>
<h1>AgentPager Wi-Fi Setup</h1>
<p class="note">This device supports 2.4 GHz Wi-Fi only.</p>
<form method="post" action="/connect" id="f">
<ul id="nets">%s</ul>
<label for="ssid">Network name (SSID)</label>
<input id="ssid" name="ssid" maxlength="32" autocomplete="off" required>
<label for="password">Password</label>
<input id="password" name="password" type="password" maxlength="64" autocomplete="off">
<button type="submit">Connect</button>
</form>
<div class="status">%s</div>
<script>
document.querySelectorAll('#nets button').forEach(function(b){b.addEventListener('click',function(e){
e.preventDefault();document.getElementById('ssid').value=b.dataset.ssid;});});
</script></body></html>)HTML";

std::string portal_network_list() {
    wifi_ap_record_t records[kMaxScanResults] = {};
    uint16_t count = kMaxScanResults;
    std::string html;
    if (esp_wifi_scan_start(nullptr, true) == ESP_OK && esp_wifi_scan_get_ap_records(&count, records) == ESP_OK) {
        for (uint16_t i = 0; i < count; ++i) {
            // The SSID is untrusted external input. Escape it separately for the
            // attribute and the element text so a hostile SSID cannot break out
            // of the button or inject markup. The value is escaped, not dropped,
            // so the network stays selectable.
            const std::string raw(reinterpret_cast<const char *>(records[i].ssid));
            const std::string attr = wearable::portal_attr_escape(raw);
            const std::string text = wearable::portal_html_escape(raw);
            char item[320];
            // Only the SSID and RSSI are exposed; never a credential.
            std::snprintf(item, sizeof(item),
                          "<li><button data-ssid=\"%s\" type=\"button\">%s &nbsp; %d dBm</button></li>",
                          attr.c_str(), text.c_str(), int(records[i].rssi));
            html += item;
        }
    }
    if (html.empty()) html = "<li>No compatible Wi-Fi found</li>";
    return html;
}

std::string portal_status() {
    TransitionGuard transition;
    switch (s_last_candidate.load()) {
    case wearable::CandidateResult::Success: return "Setup complete. The device is connecting.";
    case wearable::CandidateResult::Failed: return "Connection failed. Check SSID/password and try again.";
    case wearable::CandidateResult::NotFound: return "Network not found.";
    case wearable::CandidateResult::NoCompatible: return "No compatible Wi-Fi found.";
    case wearable::CandidateResult::StorageError: return "Connected; not saved.";
    default: return "Select a network, enter the password and press Connect.";
    }
}

esp_err_t portal_root(httpd_req_t *request) {
    const std::string html = [] {
        std::string page = kPortalIndex;
        // Two %s placeholders: network list and status.
        const std::string nets = portal_network_list();
        const std::string status = portal_status();
        const size_t first = page.find("%s");
        page.replace(first, 2, nets);
        const size_t second = page.find("%s");
        page.replace(second, 2, status);
        return page;
    }();
    httpd_resp_set_type(request, "text/html");
    httpd_resp_send(request, html.c_str(), html.size());
    return ESP_OK;
}

esp_err_t portal_connect(httpd_req_t *request) {
    // Reject an oversized body before allocating anything. The bound is tiny
    // and documented; a provisioning client must not choose the allocation.
    if (!wearable::portal_body_within_limit(size_t(request->content_len))) {
        ESP_LOGW(TAG, "[WIFI] portal body rejected len=%u", unsigned(request->content_len));
        httpd_resp_send_err(request, HTTPD_413_CONTENT_TOO_LARGE, "body too large");
        return ESP_FAIL;
    }
    std::string body;
    body.resize(size_t(request->content_len));
    size_t received = 0;
    while (received < body.size()) {
        const int chunk = httpd_req_recv(request, body.data() + received, body.size() - received);
        if (chunk <= 0) {
            httpd_resp_send_err(request, HTTPD_400_BAD_REQUEST, "bad request");
            return ESP_FAIL;
        }
        received += size_t(chunk);
    }
    const std::string ssid = wearable::portal_form_value(body, "ssid");
    const std::string password = wearable::portal_form_value(body, "password");
    // Never log the password; only the SSID is recorded.
    ESP_LOGI(TAG, "[WIFI] portal candidate ssid=%s", ssid.c_str());
    if (ssid.empty()) {
        const char *message = "SSID is required.";
        httpd_resp_set_type(request, "text/html");
        httpd_resp_send(request, message, std::strlen(message));
        return ESP_OK;
    }
    wifi_manager_test_candidate(ssid.c_str(), password.c_str());
    // Give the candidate attempt a bounded window to resolve, then report.
    for (int i = 0; i < 200; ++i) {
        const auto result = s_last_candidate.load();
        if (result != wearable::CandidateResult::Pending) break;
        vTaskDelay(pdMS_TO_TICKS(100));
    }
    const std::string message = portal_status();
    httpd_resp_set_type(request, "text/html");
    std::string page = "<!doctype html><meta charset=\"utf-8\"><body style=\"font-family:system-ui;padding:24px;background:#101418;color:#eee\"><p>";
    page += message;
    page += "</p><p><a style=\"color:#5af\" href=\"/\">Back</a></p></body>";
    httpd_resp_send(request, page.c_str(), page.size());
    return ESP_OK;
}

// Answer the OS captive-portal probes and redirect everything else to the
// Portal root. Auto-popup is a convenience, not a correctness requirement;
// http://192.168.4.1 always works.
esp_err_t portal_redirect(httpd_req_t *request) {
    httpd_resp_set_status(request, "302 Found");
    httpd_resp_set_hdr(request, "Location", "http://192.168.4.1/");
    httpd_resp_send(request, nullptr, 0);
    return ESP_OK;
}

esp_err_t portal_connect_handler(httpd_req_t *request) { return portal_connect(request); }
esp_err_t portal_root_handler(httpd_req_t *request) { return portal_root(request); }
esp_err_t portal_redirect_handler(httpd_req_t *request) { return portal_redirect(request); }

httpd_handle_t start_portal() {
    httpd_config_t config = HTTPD_DEFAULT_CONFIG();
    config.lru_purge_enable = true;
    config.max_uri_handlers = 8;
    config.stack_size = 8192;
    httpd_handle_t server = nullptr;
    if (httpd_start(&server, &config) != ESP_OK) {
        ESP_LOGW(TAG, "[WIFI] portal start failed");
        return nullptr;
    }
    const httpd_uri_t root = {"/", HTTP_GET, portal_root_handler, nullptr};
    const httpd_uri_t connect = {"/connect", HTTP_POST, portal_connect_handler, nullptr};
    httpd_register_uri_handler(server, &root);
    httpd_register_uri_handler(server, &connect);
    // Catch-all so OS captive-portal probes are redirected to the Portal. This
    // is convenience only; http://192.168.4.1 always works regardless.
    const httpd_uri_t redirect = {"/*", HTTP_GET, portal_redirect_handler, nullptr};
    httpd_register_uri_handler(server, &redirect);
    return server;
}

void stop_portal() {
    if (s_portal) {
        httpd_stop(s_portal);
        s_portal = nullptr;
    }
}

// ---- Access point ---------------------------------------------------------

esp_err_t start_access_point() {
    if (!s_ap_netif) s_ap_netif = esp_netif_create_default_wifi_ap();
    wifi_config_t config = {};
    std::strncpy(reinterpret_cast<char *>(config.ap.ssid), s_provision_ssid, sizeof(config.ap.ssid) - 1);
    config.ap.ssid_len = std::strlen(s_provision_ssid);
    std::strncpy(reinterpret_cast<char *>(config.ap.password), wearable::PROVISIONING_AP_PASSWORD,
                 sizeof(config.ap.password) - 1);
    config.ap.max_connection = 4;
    config.ap.authmode = WIFI_AUTH_WPA2_PSK;
    config.ap.channel = 1;
    if (esp_wifi_set_mode(WIFI_MODE_APSTA) != ESP_OK) return ESP_FAIL;
    if (esp_wifi_set_config(WIFI_IF_AP, &config) != ESP_OK) return ESP_FAIL;
    s_ap_running.store(true);
    ESP_LOGI(TAG, "[WIFI] provisioning AP started ssid=%s", s_provision_ssid);
    return ESP_OK;
}

void stop_access_point() {
    if (!s_ap_running.load()) return;
    stop_portal();
    // Return to pure station mode; never leave a background AP running.
    TransitionGuard transition;
    esp_wifi_set_mode(WIFI_MODE_STA);
    s_ap_running.store(false);
    ESP_LOGI(TAG, "[WIFI] provisioning AP stopped");
}

// Candidate-first validation, shared by the Portal and the USB recovery path.
//
// The candidate is tested with a real station connection and only persisted on
// success, so a bad credential never overwrites a working profile. This is the
// single implementation of the rule; neither front-end re-implements it.
//
// Portal workers publish their verdict only while their reservation is current.
// Wait until the station reports it is no longer connected, bounded so a stuck
// driver cannot hang validation. Returns true when the link is down.
bool wait_station_disconnected(uint32_t timeout_ms) {
    const uint64_t deadline = esp_timer_get_time() / 1000 + timeout_ms;
    for (;;) {
        wifi_ap_record_t ap = {};
        if (esp_wifi_sta_get_ap_info(&ap) != ESP_OK) return true;
        if (esp_timer_get_time() / 1000 >= deadline) return false;
        vTaskDelay(pdMS_TO_TICKS(20));
    }
}

wearable::CandidateResult validate_candidate(const char *ssid, const char *password,
                                             const wearable::ConnectionAttempt &attempt) {
    {
        TransitionGuard transition;
        if (!own_is_current(attempt)) return wearable::CandidateResult::Failed;
        ESP_LOGI(TAG, "[WIFI] candidate test: disconnecting current ssid=%s", s_current_ssid);
        if (esp_wifi_disconnect() != ESP_OK) return fail_candidate(attempt);
        set_state(wearable::WifiLinkState::Connecting);
        s_current_ssid[0] = '\0';
        s_current_profile = -1;
    }
    // No lock is held during either bounded wait; Approval can invalidate the
    // reservation immediately. Every subsequent side effect rechecks it.
    const bool disconnected = wait_station_disconnected(kDisconnectSettleMs);
    uint32_t wait_generation = 0;
    {
        TransitionGuard transition;
        if (!own_is_current(attempt)) return wearable::CandidateResult::Failed;
        if (!disconnected) return fail_candidate(attempt);
        wifi_config_t config = {};
        if (!populate_sta_config(config, ssid, password)) return fail_candidate(attempt);
        if (esp_wifi_set_config(WIFI_IF_STA, &config) != ESP_OK) return fail_candidate(attempt);
        wait_generation = s_event_generation.load();
        if (esp_wifi_connect() != ESP_OK) return fail_candidate(attempt);
    }
    bool got_ip = false;
    const uint64_t deadline = uint64_t(esp_timer_get_time() / 1000) + kConnectTimeoutMs;
    while (uint64_t(esp_timer_get_time() / 1000) < deadline) {
        if (!own_is_current(attempt)) return wearable::CandidateResult::Failed;
        if (s_event_generation.load() != wait_generation) { got_ip = true; break; }
        vTaskDelay(pdMS_TO_TICKS(50));
    }
    TransitionGuard transition;
    if (!own_is_current(attempt)) return wearable::CandidateResult::Failed;
    char actual[wearable::WIFI_SSID_MAX + 1] = {};
    if (got_ip) current_ap_ssid(actual, sizeof(actual));
    wearable::AttemptDirective directive;
    const auto outcome = own_candidate_resolve(attempt, got_ip, actual, directive);
    if (outcome == wearable::AttemptOutcome::Stale) return wearable::CandidateResult::Failed;
    if (!directive.persist) {
        esp_wifi_disconnect();
        set_state(wearable::WifiLinkState::Offline);
        ESP_LOGW(TAG, "[WIFI] candidate failed ssid=%s", ssid);
        return wearable::classify_candidate_failure(s_last_reason.load(), true);
    }
    wearable::WiFiStoreResult result;
    {
        Guard guard;
        result = s_store.add_or_replace(ssid, password ? password : "");
    }
    if (result != wearable::WiFiStoreResult::Ok) {
        own_forget_current();
        esp_wifi_disconnect();
        set_state(wearable::WifiLinkState::Offline);
        return wearable::CandidateResult::StorageError;
    }
    apply_success(ssid);
    set_state(wearable::WifiLinkState::Connected);
    ESP_LOGI(TAG, "[WIFI] profile add ssid=%s", ssid);
    return wearable::CandidateResult::Success;
}

void cancel_candidate_request() {
    // Caller holds TransitionGuard. Invalidate the reservation before releasing
    // the pending slot, so an old worker cannot affect a newer request.
    if (own_cancel_candidate_if_any()) {
        esp_wifi_disconnect();
        s_current_ssid[0] = '\0';
        s_current_profile = -1;
        set_state(wearable::WifiLinkState::Offline);
    }
    s_candidate_generation = 0;
    s_candidate_pending.store(false);
}

void stop_provisioning_generation(uint32_t expected_generation) {
    PortalGuard portal;
    {
        TransitionGuard transition;
        if (expected_generation && expected_generation != s_provision_generation) return;
        if (s_provisioning.load() == wearable::ProvisioningMode::Normal) return;
        ++s_provision_generation;
        s_portal_stopping = true;
        s_provisioning.store(wearable::ProvisioningMode::Normal);
        cancel_candidate_request();
    }
    // httpd_stop waits for its handler task: never hold TransitionGuard here,
    // since a final handler may itself be waiting to reserve a candidate.
    stop_access_point();
    {
        TransitionGuard transition;
        s_portal_stopping = false;
        ESP_LOGI(TAG, "[WIFI] provisioning exited, current ssid=%s", s_current_ssid);
    }
}

// Portal-driven candidate validation. Runs off the HTTP handler so the read
// loop is not blocked, and closes the AP on success.
void candidate_task(void *context) {
    auto *request = static_cast<CandidateRequest *>(context);
    const auto result = validate_candidate(request->ssid, request->password, request->attempt);
    const auto provision_generation = request->provision_generation;
    bool close_portal = false;
    {
        TransitionGuard transition;
        if (s_candidate_pending.load() && s_candidate_generation == request->attempt.generation) {
            s_last_candidate.store(result);
            s_candidate_pending.store(false);
            s_candidate_generation = 0;
            close_portal = result == wearable::CandidateResult::Success;
        }
    }
    delete request;
    if (close_portal) {
        vTaskDelay(pdMS_TO_TICKS(1500));
        stop_provisioning_generation(provision_generation);
    }
    vTaskDelete(nullptr);
}

// Leaves provisioning if it has been idle too long, so the AP never runs
// forever without the user knowing.
void provisioning_watchdog_task(void *) {
    for (;;) {
        vTaskDelay(pdMS_TO_TICKS(10000));
        uint32_t expired_generation = 0;
        {
            TransitionGuard transition;
            const uint64_t now = esp_timer_get_time() / 1000;
            if (s_provisioning.load() == wearable::ProvisioningMode::Provisioning &&
                now - s_provision_started_ms >= kIdleProvisioningTimeoutMs)
                expired_generation = s_provision_generation;
        }
        if (expired_generation) {
            ESP_LOGW(TAG, "[WIFI] provisioning idle timeout");
            board_show_message("WIFI SETUP", "Setup expired");
            stop_provisioning_generation(expired_generation);
        }
    }
}

// USB provisioning recovery path over the existing USB Serial/JTAG console.
// One line in, one framed response out. Passwords are accepted but never
// echoed; the protocol framing is documented in wifi_manager.h.
void usb_provision_task(void *) {
    char line[256];
    size_t used = 0;
    bool overflow = false;
    static char reply[1024];
    for (;;) {
        const int ch = getchar();
        if (ch == EOF) {
            vTaskDelay(pdMS_TO_TICKS(20));
            continue;
        }
        if (ch == '\r' || ch == '\n') {
            if (overflow) {
                std::snprintf(reply, sizeof(reply), "WFP/1 ERROR line-too-long\nWFP/1 END\n");
                std::fputs(reply, stdout);
                std::fflush(stdout);
                overflow = false;
                used = 0;
                continue;
            }
            if (used == 0) continue;
            line[used] = '\0';
            // The command line itself may contain a password; never log it.
            wifi_manager_handle_usb_line(line, reply, sizeof(reply));
            std::fputs(reply, stdout);
            std::fflush(stdout);
            used = 0;
            continue;
        }
        if (used + 1 < sizeof(line)) {
            line[used++] = char(ch);
        } else {
            overflow = true;
        }
    }
}

}  // namespace

// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------

esp_err_t wifi_manager_start(void) {
    s_lock = xSemaphoreCreateMutex();
    // Recursive so the locked ownership helpers can nest without deadlock.
    s_own_lock = xSemaphoreCreateRecursiveMutex();
    s_transition_lock = xSemaphoreCreateRecursiveMutex();
    s_portal_lock = xSemaphoreCreateMutex();
    s_events = xEventGroupCreate();
    if (!s_lock || !s_own_lock || !s_transition_lock || !s_portal_lock || !s_events) return ESP_ERR_NO_MEM;

    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    s_sta_netif = esp_netif_create_default_wifi_sta();

    wifi_init_config_t init = WIFI_INIT_CONFIG_DEFAULT();
    // Profiles live in our own namespace; the driver must not keep a second
    // copy that could drift from WiFiProfileStore.
    init.nvs_enable = false;
    ESP_ERROR_CHECK(esp_wifi_init(&init));
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &handle_wifi_event,
                                                        nullptr, nullptr));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &handle_wifi_event,
                                                        nullptr, nullptr));

    s_store.load();
    // Seed the recency counter from the highest persisted last_success so a
    // network connected after a reboot always becomes the newest. Without this
    // the boot-local counter restarts at 0 and a previously-newest profile keeps
    // looking newer after reboot (item 10).
    s_success_sequence.store(s_store.latest_success_sequence());
    ESP_LOGI(TAG, "[WIFI] success sequence seeded=%u", unsigned(s_success_sequence.load()));
    // Build-time migration is a ONE-TIME first-run convenience, not a fallback.
    // The decision is the host-tested `should_migrate_build_wifi` policy: only a
    // genuinely Missing store may adopt the build-time credential. A valid store
    // (even empty after Forget All) or a Corrupt/unreadable store must not.
    const bool config_present = std::strlen(CONFIG_DEVICE_LAB_WIFI_SSID) > 0;
    const bool migrate = wearable::should_migrate_build_wifi(
        s_store.load_state(), s_store.initialized(), s_store.store_was_corrupt(), config_present);
    if (s_store.store_was_corrupt()) {
        // A damaged store is never repaired by re-importing a development
        // network. Report it and stay offline; the user can re-provision.
        ESP_LOGW(TAG, "[WIFI] store present but invalid; not migrating (no automatic SoftAP)");
    }
    if (migrate) {
        if (s_store.add_or_replace(CONFIG_DEVICE_LAB_WIFI_SSID, CONFIG_DEVICE_LAB_WIFI_PASSWORD) ==
            wearable::WiFiStoreResult::Ok) {
            ESP_LOGI(TAG, "[WIFI] migrated build-time profile ssid=%s", CONFIG_DEVICE_LAB_WIFI_SSID);
        } else {
            // The credential could not be written; still mark the store
            // initialized so a failed migration is not retried on every boot.
            s_store.mark_initialized();
        }
    }
    if (s_store.count() == 0) {
        ESP_LOGW(TAG, "[WIFI] no known networks initialized=%d corrupt=%d; staying offline (no automatic SoftAP)",
                 int(s_store.initialized()), int(s_store.store_was_corrupt()));
        set_state(wearable::WifiLinkState::NoKnownWifi);
    }

    // Deterministic provisioning SSID from the stable Wi-Fi MAC suffix.
    uint8_t mac[6] = {};
    esp_read_mac(mac, ESP_MAC_WIFI_STA);
    char suffix[8];
    std::snprintf(suffix, sizeof(suffix), "%02x%02x", mac[4], mac[5]);
    wearable::provisioning_ssid(suffix, s_provision_ssid, sizeof(s_provision_ssid));
    wearable::provisioning_qr_payload(s_provision_ssid, wearable::PROVISIONING_AP_PASSWORD, s_provision_qr,
                                      sizeof(s_provision_qr));

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_start());

    xTaskCreate(station_task, "wifi_station", 6144, nullptr, 4, nullptr);
    xTaskCreate(wifi_status_task, "wifi_status", 4096, nullptr, 2, nullptr);
    xTaskCreate(provisioning_watchdog_task, "wifi_ap_watch", 3072, nullptr, 2, nullptr);
    xTaskCreate(usb_provision_task, "wifi_usb", 4096, nullptr, 2, nullptr);
    ESP_LOGI(TAG, "[WIFI] station started profiles=%u provisioning_ssid=%s", unsigned(s_store.count()),
             s_provision_ssid);
    return ESP_OK;
}

EventGroupHandle_t wifi_manager_event_group(void) { return s_events; }

wearable::WifiLinkState wifi_manager_state(void) {
    if (s_provisioning.load() == wearable::ProvisioningMode::Provisioning)
        return wearable::WifiLinkState::Offline;
    return s_state.load();
}

const char *wifi_manager_current_ssid(void) {
    thread_local char snapshot[wearable::WIFI_SSID_MAX + 1];
    TransitionGuard transition;
    std::strcpy(snapshot, s_current_ssid);
    return snapshot;
}

size_t wifi_manager_profile_count(void) {
    Guard guard;
    return s_store.count();
}

const char *wifi_manager_profile_ssid(size_t index) {
    thread_local char snapshot[wearable::WIFI_SSID_MAX + 1];
    Guard guard;
    std::strcpy(snapshot, index < s_store.count() ? s_store.at(index).ssid : "");
    return snapshot;
}

int wifi_manager_current_profile_index(void) {
    TransitionGuard transition;
    return s_current_profile;
}

esp_err_t wifi_manager_forget(const char *ssid) {
    if (!ssid || !*ssid) return ESP_ERR_INVALID_ARG;
    TransitionGuard transition;
    bool was_current = false;
    {
        Guard guard;
        was_current = std::strcmp(s_current_ssid, ssid) == 0;
        if (s_store.remove(ssid) != wearable::WiFiStoreResult::Ok) return ESP_ERR_NOT_FOUND;
    }
    ESP_LOGI(TAG, "[WIFI] profile remove ssid=%s", ssid);
    if (was_current) {
        // Disconnect and let the station task try the remaining profiles;
        // still never auto-start the AP.
        own_forget_current();
        s_current_ssid[0] = '\0';
        s_current_profile = -1;
        esp_wifi_disconnect();
    }
    return ESP_OK;
}

esp_err_t wifi_manager_forget_all(void) {
    TransitionGuard transition;
    cancel_candidate_request();
    {
        Guard guard;
        if (s_store.clear() != wearable::WiFiStoreResult::Ok) return ESP_FAIL;
    }
    ESP_LOGI(TAG, "[WIFI] profiles cleared");
    own_forget_current();
    s_current_ssid[0] = '\0';
    s_current_profile = -1;
    esp_wifi_disconnect();
    return ESP_OK;
}

bool wifi_manager_setup_blocked(void) { return board_approval_pending(); }

esp_err_t wifi_manager_start_provisioning(void) {
    PortalGuard portal;
    {
        TransitionGuard transition;
        if (wifi_manager_setup_blocked() || s_candidate_pending.load() || own_candidate_owns())
            return ESP_ERR_INVALID_STATE;
        if (s_provisioning.load() != wearable::ProvisioningMode::Normal) return ESP_ERR_INVALID_STATE;
        s_provisioning.store(wearable::ProvisioningMode::StartingAccessPoint);
        ++s_provision_generation;
        s_last_candidate.store(wearable::CandidateResult::Pending);
        s_provision_started_ms = esp_timer_get_time() / 1000;
        if (start_access_point() != ESP_OK) {
            esp_wifi_set_mode(WIFI_MODE_STA);
            s_ap_running.store(false);
            s_provisioning.store(wearable::ProvisioningMode::Normal);
            return ESP_FAIL;
        }
    }
    s_portal = start_portal();
    if (!s_portal) {
        stop_access_point();
        s_provisioning.store(wearable::ProvisioningMode::Normal);
        return ESP_FAIL;
    }
    s_provisioning.store(wearable::ProvisioningMode::Provisioning);
    return ESP_OK;
}

void wifi_manager_stop_provisioning(void) {
    stop_provisioning_generation(0);
}

bool wifi_manager_preempt_for_approval(void) {
    bool had_candidate = false;
    bool was_provisioning = false;
    {
        TransitionGuard transition;
        had_candidate = s_candidate_pending.load() || own_candidate_owns();
        was_provisioning = s_provisioning.load() != wearable::ProvisioningMode::Normal;
        cancel_candidate_request();
    }
    if (was_provisioning) {
        wifi_manager_stop_provisioning();
        ESP_LOGI(TAG, "[WIFI] approval preempted provisioning (AP stopped)");
    }
    return was_provisioning || had_candidate;
}

wearable::ProvisioningMode wifi_manager_provisioning_mode(void) { return s_provisioning.load(); }
const char *wifi_manager_provisioning_ssid(void) { return s_provision_ssid; }
const char *wifi_manager_provisioning_password(void) { return wearable::PROVISIONING_AP_PASSWORD; }
const char *wifi_manager_provisioning_address(void) { return wearable::PROVISIONING_AP_ADDRESS; }
const char *wifi_manager_provisioning_qr(void) { return s_provision_qr; }
wearable::CandidateResult wifi_manager_last_candidate(void) { return s_last_candidate.load(); }
const char *wifi_manager_last_candidate_ssid(void) {
    thread_local char snapshot[wearable::WIFI_SSID_MAX + 1];
    TransitionGuard transition;
    std::strcpy(snapshot, s_candidate_ssid);
    return snapshot;
}

esp_err_t wifi_manager_test_candidate(const char *ssid, const char *password) {
    if (!wearable::wifi_ssid_valid(ssid) || !wearable::wifi_password_valid(password))
        return ESP_ERR_INVALID_ARG;
    TransitionGuard transition;
    if (s_provisioning.load() != wearable::ProvisioningMode::Provisioning ||
        s_candidate_pending.load() || wifi_manager_setup_blocked()) return ESP_ERR_INVALID_STATE;
    auto *request = new (std::nothrow) CandidateRequest;
    if (!request) return ESP_ERR_NO_MEM;
    std::strcpy(request->ssid, ssid);
    std::strcpy(request->password, password ? password : "");
    request->provision_generation = ++s_provision_generation;
    // Reserve BEFORE xTaskCreate: preemption invalidates even an unstarted task.
    request->attempt = own_take_candidate(ssid);
    s_candidate_generation = request->attempt.generation;
    std::strcpy(s_candidate_ssid, request->ssid);
    s_last_candidate.store(wearable::CandidateResult::Pending);
    s_candidate_pending.store(true);
    if (xTaskCreate(candidate_task, "wifi_candidate", 6144, request, 4, nullptr) != pdPASS) {
        cancel_candidate_request();
        delete request;
        return ESP_ERR_NO_MEM;
    }
    return ESP_OK;
}

// ---------------------------------------------------------------------------
// USB provisioning protocol
//
// Parsing and formatting live in the pure wifi_usb_protocol.h; this section
// binds the verbs to the real profile store. A password is accepted but never
// echoed in any response line.
// ---------------------------------------------------------------------------
namespace {
// A stable address the USB dispatcher can hold as its `context`. The dispatcher
// treats a null context as "no service bound" and refuses every verb, so
// production MUST pass a real, stable pointer. This object lives for the whole
// program and carries no credentials: the callbacks reach the real store and
// station manager through the file-scope state above.
//
// It is deliberately not a fake sentinel (e.g. reinterpret_cast<void*>(1)):
// the callbacks are the real production ones, so a host/production binding test
// can prove `wifi.status`/`wifi.list`/`wifi.add` reach the live store.
struct WiFiManagerContext {
    // Identifies the binding in the reply-building path; also gives the
    // compiler a concrete, non-null object address.
    uint32_t magic = 0x57494631;  // 'WIF1'
};
WiFiManagerContext s_usb_context;

const char *usb_state_label(void *) { return wearable::wifi_link_label(wifi_manager_state()); }
const char *usb_current_ssid(void *) { return wifi_manager_current_ssid(); }
size_t usb_profile_count(void *) {
    Guard guard;
    return s_store.count();
}
const char *usb_profile_ssid(void *, size_t index) {
    return wifi_manager_profile_ssid(index);
}
bool usb_profile_proven(void *, size_t index) {
    Guard guard;
    return index < s_store.count() && s_store.at(index).last_success != 0;
}
// USB add is candidate-first, exactly like the Portal: the credential is
// tested with a real station connection and only persisted on success. The USB
// console task runs this synchronously so the response can report the verdict.
//
// If the station cannot be tested safely (a Portal candidate test is already
// running, or provisioning owns the interface), the verb reports UNAVAILABLE
// instead of pretending the credential was verified.
wearable::UsbAddOutcome usb_add(void *, const char *ssid, const char *password) {
    if (!wearable::wifi_ssid_valid(ssid) || !wearable::wifi_password_valid(password))
        return wearable::UsbAddOutcome::Invalid;
    wearable::ConnectionAttempt attempt;
    {
        TransitionGuard transition;
        if (s_candidate_pending.load() || s_portal_stopping ||
            s_provisioning.load() != wearable::ProvisioningMode::Normal ||
            own_candidate_owns() || wifi_manager_setup_blocked())
            return wearable::UsbAddOutcome::Unavailable;
        attempt = own_take_candidate(ssid);
        s_candidate_generation = attempt.generation;
        s_candidate_pending.store(true);
    }
    ESP_LOGI(TAG, "[WIFI] usb candidate test ssid=%s", ssid);
    const auto result = validate_candidate(ssid, password ? password : "", attempt);
    {
        TransitionGuard transition;
        if (s_candidate_generation == attempt.generation) {
            s_candidate_pending.store(false);
            s_candidate_generation = 0;
        }
    }
    switch (result) {
    case wearable::CandidateResult::Success: return wearable::UsbAddOutcome::Verified;
    case wearable::CandidateResult::StorageError: return wearable::UsbAddOutcome::StorageError;
    case wearable::CandidateResult::NotFound:
    case wearable::CandidateResult::NoCompatible:
    case wearable::CandidateResult::Failed: return wearable::UsbAddOutcome::Rejected;
    default: return wearable::UsbAddOutcome::Unavailable;
    }
}
bool usb_remove(void *, const char *ssid) { return wifi_manager_forget(ssid) == ESP_OK; }
bool usb_clear(void *) { return wifi_manager_forget_all() == ESP_OK; }
}

bool wifi_usb_line_has_password(const char *line) { return wearable::usb_line_contains_password(line); }

void wifi_manager_handle_usb_line(const char *line, char *reply, size_t reply_size) {
    wifi_manager_dispatch(line, reply, reply_size);
}

size_t wifi_manager_dispatch(const char *line, char *reply, size_t reply_size) {
    if (!reply || reply_size == 0) return 0;
    reply[0] = '\0';
    wearable::UsbWiFiRequest request;
    const bool framed = wearable::usb_parse_line(line, request);
    if (!framed) {
        std::snprintf(reply, reply_size, "WFP/1 ERROR version\n");
        return std::strlen(reply);
    }
    const wearable::UsbWiFiOps ops{usb_state_label, usb_current_ssid, usb_profile_count,
                                   usb_profile_ssid, usb_profile_proven, usb_add, usb_remove,
                                   usb_clear, &s_usb_context};
    const std::string response = wearable::usb_handle_request(request, ops);
    const size_t copy = response.size() < reply_size - 1 ? response.size() : reply_size - 1;
    std::memcpy(reply, response.data(), copy);
    reply[copy] = '\0';
    return copy;
}

// ---------------------------------------------------------------------------
// Board-facing network interface (boards/common/include/board_network.h)
//
// These are the only functions board.cpp may call. They expose SSIDs, counts
// and state labels; a password never crosses this boundary.
// ---------------------------------------------------------------------------

board_wifi_state_t board_network_state(void) {
    switch (wifi_manager_state()) {
    case wearable::WifiLinkState::Connected: return BOARD_WIFI_CONNECTED;
    case wearable::WifiLinkState::Connecting: return BOARD_WIFI_CONNECTING;
    case wearable::WifiLinkState::NoKnownWifi: return BOARD_WIFI_NO_KNOWN;
    default: return BOARD_WIFI_OFFLINE;
    }
}

const char *board_network_state_label(void) {
    return wearable::wifi_link_label(wifi_manager_state());
}

const char *board_network_current_ssid(void) { return wifi_manager_current_ssid(); }

size_t board_network_profile_count(void) { return wifi_manager_profile_count(); }

const char *board_network_profile_ssid(size_t index) { return wifi_manager_profile_ssid(index); }

esp_err_t board_network_forget(const char *ssid) { return wifi_manager_forget(ssid); }

esp_err_t board_network_forget_all(void) { return wifi_manager_forget_all(); }

esp_err_t board_network_start_setup(void) { return wifi_manager_start_provisioning(); }

void board_network_stop_setup(void) { wifi_manager_stop_provisioning(); }

bool board_network_setup_running(void) {
    return wearable::access_point_should_run(wifi_manager_provisioning_mode());
}

bool board_network_candidate_active(void) {
    return s_candidate_pending.load() || own_candidate_owns();
}

void board_network_preempt_for_approval(void) { wifi_manager_preempt_for_approval(); }

bool board_network_setup_blocked(void) { return wifi_manager_setup_blocked(); }

const char *board_network_setup_ssid(void) { return wifi_manager_provisioning_ssid(); }

const char *board_network_setup_password(void) { return wifi_manager_provisioning_password(); }

const char *board_network_setup_address(void) { return wifi_manager_provisioning_address(); }

const char *board_network_setup_status(void) {
    return wearable::candidate_reason(wifi_manager_last_candidate());
}
