#include "wifi_manager.h"
#include "config.h"
#include <WiFi.h>
#include <esp_wifi.h>

static Preferences prefs;

static bool wifi_log_save_failure(const char *key, const char *reason) {
  if (SERIAL_DEBUG_MODE) {
    Serial.printf("wifi_cfg: save failed key=%s reason=%s\n", key, reason);
  }
  return false;
}

static bool wifi_write_string_pref(const char *key, const String &value) {
  size_t written = prefs.putString(key, value);
  if (written != value.length()) {
    return wifi_log_save_failure(key, "write_short");
  }
  if (!prefs.isKey(key)) {
    return wifi_log_save_failure(key, "missing_key");
  }
  String readback = prefs.getString(key, "");
  if (readback != value) {
    return wifi_log_save_failure(key, "verify_mismatch");
  }
  return true;
}

static bool wifi_write_bool_pref(const char *key, bool value) {
  size_t written = prefs.putBool(key, value);
  if (written != sizeof(uint8_t)) {
    return wifi_log_save_failure(key, "write_short");
  }
  if (!prefs.isKey(key)) {
    return wifi_log_save_failure(key, "missing_key");
  }
  bool readback = prefs.getBool(key, !value);
  if (readback != value) {
    return wifi_log_save_failure(key, "verify_mismatch");
  }
  return true;
}

static bool wifi_write_ushort_pref(const char *key, uint16_t value) {
  size_t written = prefs.putUShort(key, value);
  if (written != sizeof(uint16_t)) {
    return wifi_log_save_failure(key, "write_short");
  }
  if (!prefs.isKey(key)) {
    return wifi_log_save_failure(key, "missing_key");
  }
  uint16_t fallback = value == static_cast<uint16_t>(0xFFFF) ? 0 : static_cast<uint16_t>(0xFFFF);
  uint16_t readback = prefs.getUShort(key, fallback);
  if (readback != value) {
    return wifi_log_save_failure(key, "verify_mismatch");
  }
  return true;
}

void wifi_manager_init() {
  prefs.begin("dd3cfg", false);
}

bool wifi_load_config(WifiMqttConfig &config) {
  config.valid = prefs.getBool("valid", false);
  config.ssid = prefs.getString("ssid", "");
  config.password = prefs.getString("pass", "");
  config.mqtt_host = prefs.getString("mqhost", "");
  config.mqtt_port = prefs.getUShort("mqport", 1883);
  config.mqtt_user = prefs.getString("mquser", "");
  config.mqtt_pass = prefs.getString("mqpass", "");
  config.ntp_server_1 = prefs.getString("ntp1", "pool.ntp.org");
  config.ntp_server_2 = prefs.getString("ntp2", "time.nist.gov");
  config.web_user = prefs.getString("webuser", WEB_AUTH_DEFAULT_USER);
  config.web_pass = prefs.getString("webpass", WEB_AUTH_DEFAULT_PASS);
  if (!config.valid) {
    return false;
  }
  return config.ssid.length() > 0 && config.mqtt_host.length() > 0;
}

bool wifi_save_config(const WifiMqttConfig &config) {
  if (!wifi_write_bool_pref("valid", true)) {
    return false;
  }
  if (!wifi_write_string_pref("ssid", config.ssid)) {
    return false;
  }
  if (!wifi_write_string_pref("pass", config.password)) {
    return false;
  }
  if (!wifi_write_string_pref("mqhost", config.mqtt_host)) {
    return false;
  }
  if (!wifi_write_ushort_pref("mqport", config.mqtt_port)) {
    return false;
  }
  if (!wifi_write_string_pref("mquser", config.mqtt_user)) {
    return false;
  }
  if (!wifi_write_string_pref("mqpass", config.mqtt_pass)) {
    return false;
  }
  if (!wifi_write_string_pref("ntp1", config.ntp_server_1)) {
    return false;
  }
  if (!wifi_write_string_pref("ntp2", config.ntp_server_2)) {
    return false;
  }
  if (!wifi_write_string_pref("webuser", config.web_user)) {
    return false;
  }
  if (!wifi_write_string_pref("webpass", config.web_pass)) {
    return false;
  }
  return true;
}

bool wifi_connect_sta(const WifiMqttConfig &config, uint32_t timeout_ms) {
  WiFi.mode(WIFI_STA);
  WiFi.begin(config.ssid.c_str(), config.password.c_str());
  uint32_t start = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - start < timeout_ms) {
    delay(200);
  }
  bool connected = WiFi.status() == WL_CONNECTED;
  if (connected) {
    esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
  }
  return connected;
}

void wifi_start_ap(const char *ap_ssid, const char *ap_pass) {
  WiFi.mode(WIFI_AP);
  WiFi.softAP(ap_ssid, ap_pass);
}

bool wifi_is_connected() {
  return WiFi.status() == WL_CONNECTED;
}

String wifi_get_ssid() {
  return WiFi.SSID();
}

// Try to reconnect to WiFi with a shorter timeout (for periodic reconnection attempts)
// Called when device is stuck in AP mode and we want to try switching back to STA
bool wifi_try_reconnect_sta(const WifiMqttConfig &config, uint32_t timeout_ms) {
  // Only attempt if not already connected and config is valid
  if (WiFi.status() == WL_CONNECTED) {
    return true;
  }
  
  // Check if config is valid
  if (config.ssid.length() == 0 || config.mqtt_host.length() == 0) {
    return false;
  }
  
  // Switch to STA mode and attempt connection with shorter timeout
  WiFi.mode(WIFI_STA);
  WiFi.begin(config.ssid.c_str(), config.password.c_str());
  
  uint32_t start = millis();
  while (WiFi.status() != WL_CONNECTED && millis() - start < timeout_ms) {
    delay(200);
  }
  
  bool connected = WiFi.status() == WL_CONNECTED;
  if (connected) {
    esp_wifi_set_ps(WIFI_PS_MIN_MODEM);
    if (SERIAL_DEBUG_MODE) {
      Serial.printf("wifi_reconnect: success, connected to %s\n", config.ssid.c_str());
    }
  } else {
    if (SERIAL_DEBUG_MODE) {
      Serial.printf("wifi_reconnect: failed, remaining in STA mode\n");
    }
  }
  
  return connected;
}

// Helper function to restore AP mode when reconnection attempt has failed
void wifi_restore_ap_mode(const char *ap_ssid, const char *ap_pass) {
  if (WiFi.status() != WL_CONNECTED) {
    // We're not connected to WiFi, restore AP mode
    WiFi.mode(WIFI_AP);
    WiFi.softAP(ap_ssid, ap_pass);
    if (SERIAL_DEBUG_MODE) {
      Serial.printf("wifi_restore_ap: AP mode restored\n");
    }
  }
}
