#ifdef ESP_PLATFORM
#include <sdkconfig.h>
#endif

#if defined(CONFIG_SOC_WIFI_SUPPORTED) && !defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED)

#include <Tactility/service/espnow/EspNowBackend.h>
#include <Tactility/service/wifi/Wifi.h>

#include <esp_now.h>
#include <esp_wifi.h>

#include <tactility/log.h>

namespace tt::service::espnow::backend {

constexpr auto* TAG = "EspNowBackendNative";
static bool wifiStartedByEspNow = false;
static bool longRangeProtocolApplied = false;
static wifi_interface_t longRangeInterface = WIFI_IF_STA;
static uint8_t savedProtocolBitmap = 0;

static bool deinitWifi();

static bool initWifi(const EspNowConfig& config) {
    auto wifi_state = wifi::getRadioState();
    bool wifi_already_running = (wifi_state != wifi::RadioState::Off && wifi_state != wifi::RadioState::OffPending);

    wifi_mode_t mode;
    if (config.mode == Mode::Station) {
        mode = wifi_mode_t::WIFI_MODE_STA;
    } else {
        mode = wifi_mode_t::WIFI_MODE_AP;
    }

    // Only initialize WiFi if it's not already running; ESP-NOW coexists with STA mode
    wifiStartedByEspNow = !wifi_already_running;
    if (wifiStartedByEspNow) {
        wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
        if (esp_wifi_init(&cfg) != ESP_OK) {
            LOG_E(TAG,"esp_wifi_init() failed");
            wifiStartedByEspNow = false;
            return false;
        }

        if (esp_wifi_set_storage(WIFI_STORAGE_RAM) != ESP_OK) {
            LOG_E(TAG,"esp_wifi_set_storage() failed");
            esp_wifi_deinit();
            wifiStartedByEspNow = false;
            return false;
        }

        if (esp_wifi_set_mode(mode) != ESP_OK) {
            LOG_E(TAG,"esp_wifi_set_mode() failed");
            esp_wifi_deinit();
            wifiStartedByEspNow = false;
            return false;
        }

        if (esp_wifi_start() != ESP_OK) {
            LOG_E(TAG,"esp_wifi_start() failed");
            esp_wifi_deinit();
            wifiStartedByEspNow = false;
            return false;
        }

        if (config.channel != 0 && esp_wifi_set_channel(config.channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) {
            LOG_E(TAG,"esp_wifi_set_channel() failed");
            deinitWifi();
            return false;
        }
    } else if (config.channel != 0 &&
            wifi_state != wifi::RadioState::ConnectionActive && wifi_state != wifi::RadioState::ConnectionPending) {
        // WifiService already owns the radio but isn't associated to an AP: an unassociated STA's
        // operating channel is otherwise left undefined/wherever it last scanned to, which silently
        // breaks ESP-NOW (esp_now_send() reports success but nothing reaches a peer sitting on a
        // different channel) - only skip this when actually connected/connecting, where the STA is
        // already locked to the AP's channel and forcing a different one would disrupt that link.
        if (esp_wifi_set_channel(config.channel, WIFI_SECOND_CHAN_NONE) != ESP_OK) {
            LOG_W(TAG, "esp_wifi_set_channel() failed on externally managed radio - ESP-NOW may not reach peers");
        }
    }

    if (config.longRange) {
        wifi_interface_t wifi_interface = (config.mode == Mode::Station) ? WIFI_IF_STA : WIFI_IF_AP;

        // Preserve whatever protocol mask the interface already had (e.g. set by WifiService if
        // it owns this interface) so it can be restored in deinitWifi() - esp_wifi_set_protocol()
        // below otherwise permanently stomps it, even when ESP-NOW doesn't own the radio. Skip
        // applying long-range at all if the snapshot fails: without a saved mask to restore,
        // changing the protocol would permanently alter an externally-managed interface with no
        // way to undo it later.
        uint8_t previousBitmap = 0;
        bool hadPreviousBitmap = esp_wifi_get_protocol(wifi_interface, &previousBitmap) == ESP_OK;

        if (!hadPreviousBitmap) {
            LOG_W(TAG, "esp_wifi_get_protocol() failed - skipping long-range (can't safely restore protocol mask later)");
        } else if (esp_wifi_set_protocol(wifi_interface, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G | WIFI_PROTOCOL_11N | WIFI_PROTOCOL_LR) != ESP_OK) {
            LOG_W(TAG,"esp_wifi_set_protocol() for long range failed");
        } else {
            longRangeProtocolApplied = true;
            longRangeInterface = wifi_interface;
            savedProtocolBitmap = previousBitmap;
        }
    }

    LOG_I(TAG, "WiFi initialized for ESP-NOW (wifi already running: %s)", wifi_already_running ? "yes" : "no");
    return true;
}

static bool deinitWifi() {
    // Restore the interface's protocol mask before either stopping WiFi or handing the radio
    // back to WifiService - covers both paths, since an externally managed radio (wifiStartedByEspNow
    // == false) is the case that actually needs its prior protocol bits put back.
    if (longRangeProtocolApplied) {
        if (esp_wifi_set_protocol(longRangeInterface, savedProtocolBitmap) != ESP_OK) {
            LOG_W(TAG, "Failed to restore prior WiFi protocol bitmap");
        }
        longRangeProtocolApplied = false;
    }

    if (wifiStartedByEspNow) {
        esp_wifi_stop();
        esp_wifi_deinit();
        wifiStartedByEspNow = false;
        LOG_I(TAG, "WiFi stopped (was started by ESP-NOW)");
    } else {
        LOG_I(TAG, "WiFi left running (managed by WiFi service)");
    }
    return true;
}

static uint32_t espnowVersion = 0;

bool init(const EspNowConfig& config, esp_now_recv_cb_t recvCallback) {
    if (!initWifi(config)) {
        LOG_E(TAG, "initWifi() failed");
        return false;
    }

    if (esp_now_init() != ESP_OK) {
        LOG_E(TAG, "esp_now_init() failed");
        deinitWifi();
        return false;
    }

    if (esp_now_register_recv_cb(recvCallback) != ESP_OK) {
        LOG_E(TAG, "esp_now_register_recv_cb() failed");
        esp_now_deinit();
        deinitWifi();
        return false;
    }

    if (esp_now_set_pmk(config.masterKey) != ESP_OK) {
        LOG_E(TAG, "esp_now_set_pmk() failed");
        esp_now_deinit();
        deinitWifi();
        return false;
    }

    espnowVersion = 0;
    if (esp_now_get_version(&espnowVersion) == ESP_OK) {
        LOG_I(TAG, "ESP-NOW version: %u.0", (unsigned)espnowVersion);
    } else {
        LOG_W(TAG, "Failed to get ESP-NOW version");
    }

    return true;
}

bool deinit() {
    bool success = true;

    if (esp_now_deinit() != ESP_OK) {
        LOG_E(TAG, "esp_now_deinit() failed");
        success = false;
    }

    if (!deinitWifi()) {
        LOG_E(TAG, "deinitWifi() failed");
        success = false;
    }

    espnowVersion = 0;
    return success;
}

bool addPeer(const esp_now_peer_info_t& peer) {
    if (esp_now_add_peer(&peer) != ESP_OK) {
        LOG_E(TAG, "Failed to add peer");
        return false;
    } else {
        LOG_I(TAG, "Peer added");
        return true;
    }
}

bool send(const uint8_t* address, const uint8_t* buffer, size_t bufferLength) {
    return esp_now_send(address, buffer, bufferLength) == ESP_OK;
}

uint32_t getVersion() {
    return espnowVersion;
}

}

#endif // CONFIG_SOC_WIFI_SUPPORTED && !CONFIG_SLAVE_SOC_WIFI_SUPPORTED
