#include "portal.h"
#include "config.h"
#include "led.h"
#include "api.h"
#include "store.h"
#include <WiFi.h>
#include <WebServer.h>
#include <DNSServer.h>

namespace {
  WebServer server(80);
  DNSServer dns;
  String networksHtml;
  String lastError;

  const char PAGE_HEAD[] PROGMEM = R"(<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>SMASHR setup</title><style>
body{font-family:-apple-system,system-ui,sans-serif;background:#e8eae9;color:#141516;margin:0;padding:24px}
h1{font-weight:300;font-size:34px;letter-spacing:-.02em;margin:0 0 8px}p{color:#3a3d3f}
label{display:block;font-size:13px;margin:16px 0 6px}input,select{width:100%;box-sizing:border-box;height:44px;padding:0 12px;border:1px solid #c3c8c7;background:#f3f4f3;font-size:16px;border-radius:0}
.code{font-family:ui-monospace,monospace;letter-spacing:.2em;text-transform:uppercase}
button{margin-top:24px;width:100%;height:48px;background:#e8381b;color:#fff;border:1px solid #141516;box-shadow:0 2px 0 #141516;font-size:16px}
.err{color:#e8381b}</style></head><body>)";

  String esc(const String& s) { String o; for (char c : s) { if (c == '<') o += "&lt;"; else if (c == '>') o += "&gt;"; else if (c == '&') o += "&amp;"; else if (c == '"') o += "&quot;"; else o += c; } return o; }

  void scan() {
    int n = WiFi.scanNetworks();
    networksHtml = "";
    for (int i = 0; i < n && i < 20; i++) {
      String ssid = WiFi.SSID(i);
      if (!ssid.length() || networksHtml.indexOf(">" + esc(ssid) + "<") >= 0) continue;
      networksHtml += "<option>" + esc(ssid) + "</option>";
    }
  }

  void handleRoot() {
    String h = FPSTR(PAGE_HEAD);
    h += "<h1>Set up your button</h1><p>Pick your home Wi-Fi (2.4 GHz) and type the 6 character code from the SMASHR dashboard.</p>";
    if (lastError.length()) h += "<p class=err>" + esc(lastError) + "</p>";
    h += "<form method=post action=/save><label>Network</label><select name=ssid>" + networksHtml + "</select>"
         "<label>Or type it</label><input name=ssid2 autocomplete=off>"
         "<label>Password</label><input name=pass type=password>"
         "<label>Pairing code</label><input name=code class=code maxlength=6 required autocomplete=off>"
         "<button type=submit>Connect and pair</button></form></body></html>";
    server.send(200, "text/html", h);
  }

  void handleSave() {
    String ssid = server.arg("ssid2").length() ? server.arg("ssid2") : server.arg("ssid");
    String pass = server.arg("pass");
    String code = server.arg("code");
    code.trim(); code.toUpperCase();
    led::set(LED_CONNECTING);
    WiFi.begin(ssid.c_str(), pass.c_str());
    uint32_t t0 = millis();
    while (WiFi.status() != WL_CONNECTED && millis() - t0 < WIFI_CONNECT_MS) { delay(100); dns.processNextRequest(); }
    if (WiFi.status() != WL_CONNECTED) {
      lastError = "Could not join " + ssid + ". Check the password, and that it is a 2.4 GHz network.";
      led::set(LED_PORTAL);
      server.sendHeader("Location", "/"); server.send(303);
      return;
    }
    configTime(0, 0, "pool.ntp.org", "time.google.com");
    String id, secret, err;
    if (!api::pair(code, id, secret, err)) {
      lastError = err;
      led::set(LED_PORTAL);
      server.sendHeader("Location", "/"); server.send(303);
      return;
    }
    store::saveWifi(ssid, pass);
    store::saveIdentity(id, secret);
    String h = FPSTR(PAGE_HEAD);
    h += "<h1>Paired.</h1><p>This is " + esc(id) + ". The ring turns green, then red. Go press it.</p></body></html>";
    server.send(200, "text/html", h);
    led::settle(LED_OK, 2400, LED_IDLE);
    delay(2500);
    ESP.restart();
  }

  void redirect() { server.sendHeader("Location", "http://192.168.4.1/"); server.send(302); }
}

namespace portal {
  void run() {
    String mac = WiFi.macAddress(); mac.replace(":", "");
    String apName = String(AP_PREFIX) + mac.substring(8);
    WiFi.mode(WIFI_AP_STA);
    WiFi.softAP(apName.c_str());
    delay(200);
    scan();
    dns.start(53, "*", WiFi.softAPIP());
    server.on("/", HTTP_GET, handleRoot);
    server.on("/save", HTTP_POST, handleSave);
    // OS captive-portal probes
    server.on("/generate_204", redirect);
    server.on("/hotspot-detect.html", redirect);
    server.on("/connecttest.txt", redirect);
    server.onNotFound(redirect);
    server.begin();
    led::set(LED_PORTAL);
    Serial.printf("[portal] join %s\n", apName.c_str());
    uint32_t t0 = millis();
    while (millis() - t0 < PORTAL_TIMEOUT_MS) {
      dns.processNextRequest();
      server.handleClient();
      delay(2);
    }
    ESP.restart();
  }
}
