#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>
#include <driver/touch_pad.h>

preferences prefs;
webserver server(80);

// touch pad used as boot override / setup trigger
const touch_pad_t touch_pin = TOUCH_PAD_NUM4; // gpio13
const int touch_threshold = 500;

// softap fallback
const char* ap_ssid = "baby-monitor-setup";
const char* ap_password = "setup1234";

// saved / editable config
String wifi_ssid = "";
String wifi_password = "";
String dest_ip_str = "192.168.1.190";
uint16_t udp_port = 5004;

bool config_mode = false;
const unsigned long wifi_connect_timeout_ms = 15000;

String html_escape(const String& s) {
  String out = s;
  out.replace("&", "&amp;");
  out.replace("<", "&lt;");
  out.replace(">", "&gt;");
  out.replace("\"", "&quot;");
  return out;
}

void load_config() {
  prefs.begin("bmcfg", true);
  wifi_ssid = prefs.getString("ssid", "");
  wifi_password = prefs.getString("pwd", "");
  dest_ip_str = prefs.getString("destip", "192.168.1.190");
  udp_port = prefs.getUShort("port", 5004);
  prefs.end();
}

void save_config(const String& ssid, const String& pwd, const String& dest_ip, uint16_t port) {
  prefs.begin("bmcfg", false);
  prefs.putString("ssid", ssid);
  prefs.putString("pwd", pwd);
  prefs.putString("destip", dest_ip);
  prefs.putUShort("port", port);
  prefs.end();
}

void clear_config() {
  prefs.begin("bmcfg", false);
  prefs.clear();
  prefs.end();
}

bool force_setup_requested() {
  // give touch pad a moment to settle
  delay(100);

  uint16_t touch_val = 0;
  touch_pad_read(touch_pin, &touch_val);

  Serial.printf("touch boot check: %u\n", touch_val);
  return touch_val < touch_threshold;
}

void handle_root() {
  String page =
    "<!doctype html>"
    "<html><head>"
    "<meta name='viewport' content='width=device-width,initial-scale=1'>"
    "<title>baby monitor setup</title>"
    "<style>"
    "body{font-family:sans-serif;max-width:520px;margin:2rem auto;padding:0 1rem;}"
    "input{width:100%;padding:.6rem;margin:.2rem 0 1rem 0;box-sizing:border-box;}"
    "button{padding:.7rem 1rem;margin-right:.5rem;}"
    "code{background:#eee;padding:.1rem .3rem;}"
    "</style>"
    "</head><body>"
    "<h2>baby monitor setup</h2>"
    "<p>connect to ap <code>" + String(ap_ssid) + "</code> then browse to <code>192.168.4.1</code>.</p>"
    "<form method='post' action='/save'>"
    "<label>wifi ssid</label>"
    "<input name='ssid' value='" + html_escape(wifi_ssid) + "'>"
    "<label>wifi password</label>"
    "<input name='pwd' type='password' value='" + html_escape(wifi_password) + "'>"
    "<label>destination ip</label>"
    "<input name='destip' value='" + html_escape(dest_ip_str) + "'>"
    "<label>udp port</label>"
    "<input name='port' value='" + String(udp_port) + "'>"
    "<button type='submit'>save and reboot</button>"
    "</form>"
    "<form method='post' action='/clear' onsubmit='return confirm(\"clear saved config?\");'>"
    "<button type='submit'>clear saved config</button>"
    "</form>"
    "<p><a href='/status'>status json</a></p>"
    "</body></html>";

  server.send(200, "text/html", page);
}

void handle_status() {
  String json = "{";
  json += "\"config_mode\":" + String(config_mode ? "true" : "false") + ",";
  json += "\"ap_ssid\":\"" + String(ap_ssid) + "\",";
  json += "\"saved_ssid\":\"" + html_escape(wifi_ssid) + "\",";
  json += "\"dest_ip\":\"" + html_escape(dest_ip_str) + "\",";
  json += "\"udp_port\":" + String(udp_port);
  json += "}";

  server.send(200, "application/json", json);
}

bool valid_ipv4(const String& s) {
  ipaddress ip;
  return ip.fromString(s);
}

void handle_save() {
  String new_ssid = server.arg("ssid");
  String new_pwd = server.arg("pwd");
  String new_dest = server.arg("destip");
  uint16_t new_port = (uint16_t)server.arg("port").toInt();

  if (new_ssid.length() == 0) {
    server.send(400, "text/plain", "ssid required");
    return;
  }

  if (!valid_ipv4(new_dest)) {
    server.send(400, "text/plain", "invalid destination ip");
    return;
  }

  if (new_port == 0) {
    server.send(400, "text/plain", "invalid udp port");
    return;
  }

  save_config(new_ssid, new_pwd, new_dest, new_port);

  server.send(200, "text/html",
    "<html><body><h3>saved. rebooting...</h3></body></html>");
  delay(1000);
  esp_restart();
}

void handle_clear() {
  clear_config();
  server.send(200, "text/html",
    "<html><body><h3>config cleared. rebooting...</h3></body></html>");
  delay(1000);
  esp_restart();
}

void start_config_ap() {
  config_mode = true;

  WiFi.disconnect(true, true);
  delay(250);

  WiFi.mode(WIFI_AP);
  WiFi.softAP(ap_ssid, ap_password);

  IPAddress ip = WiFi.softAPIP();
  Serial.printf("config ap active\n");
  Serial.printf("  ssid: %s\n", ap_ssid);
  Serial.printf("  pass: %s\n", ap_password);
  Serial.printf("  ip:   %s\n", ip.toString().c_str());

  server.on("/", HTTP_GET, handle_root);
  server.on("/save", HTTP_POST, handle_save);
  server.on("/clear", HTTP_POST, handle_clear);
  server.on("/status", HTTP_GET, handle_status);
  server.begin();
}

bool try_connect_wifi() {
  if (wifi_ssid.length() == 0) {
    Serial.println("no saved wifi ssid");
    return false;
  }

  WiFi.disconnect(true, true);
  delay(250);

  WiFi.mode(WIFI_STA);
  WiFi.begin(wifi_ssid.c_str(), wifi_password.c_str());

  Serial.printf("connecting to wifi: %s\n", wifi_ssid.c_str());

  unsigned long start = millis();
  while (WiFi.status() != WL_CONNECTED &&
         millis() - start < wifi_connect_timeout_ms) {
    delay(500);
    Serial.print(".");
  }
  Serial.println();

  if (WiFi.status() == WL_CONNECTED) {
    Serial.printf("connected\n");
    Serial.printf("  ip: %s\n", WiFi.localIP().toString().c_str());
    Serial.printf("  dest ip: %s\n", dest_ip_str.c_str());
    Serial.printf("  udp port: %u\n", udp_port);
    return true;
  }

  Serial.println("wifi connect failed");
  return false;
}

void setup() {
  Serial.begin(115200);
  delay(200);

  touch_pad_init();
  touch_pad_set_voltage(TOUCH_HVOLT_2V7, TOUCH_LVOLT_0V5, TOUCH_HVOLT_ATTEN_1V);
  touch_pad_config(touch_pin, 0);

  load_config();

  if (force_setup_requested()) {
    Serial.println("force setup mode requested");
    start_config_ap();
    return;
  }

  if (!try_connect_wifi()) {
    start_config_ap();
  }
}

void loop() {
  if (config_mode) {
    server.handleClient();
    delay(2);
    return;
  }

  // placeholder for later normal-mode behavior
  static unsigned long last_print = 0;
  if (millis() - last_print > 5000) {
    Serial.printf("normal mode alive. wifi=%s ip=%s dest=%s:%u\n",
      wifi_ssid.c_str(),
      WiFi.localIP().toString().c_str(),
      dest_ip_str.c_str(),
      udp_port);
    last_print = millis();
  }

  delay(10);
}