String fmtUptime() {
  unsigned long sec = millis() / 1000;
  unsigned int days = sec / 86400; sec %= 86400;
  unsigned int hours = sec / 3600; sec %= 3600;
  unsigned int mins = sec / 60;
  String s;
  if (days) { s += String(days); s += "d "; }
  if (hours < 10) s += "0"; s += String(hours); s += ":";
  if (mins < 10) s += "0"; s += String(mins);
  return s;
}


String currentLang() {
  String l = String(cfg.language);
  l.toUpperCase();
  if (l == "EN") l = "GB";
  if (l == "DE") l = "D";
  return l;
}

String txt(const char* key) {
  String l = currentLang();
  String k = String(key);

  if (l == "GB") {
    if (k=="status") return "Status"; if (k=="sensor") return "Sensor"; if (k=="wifi") return "WiFi"; if (k=="mqtt") return "MQTT"; if (k=="system") return "System"; if (k=="firmware") return "Firmware"; if (k=="info") return "Info";
    if (k=="temperature") return "Temperature"; if (k=="humidity") return "Humidity"; if (k=="led") return "LED"; if (k=="on") return "On"; if (k=="off") return "Off"; if (k=="turnOn") return "Turn on"; if (k=="turnOff") return "Turn off";
    if (k=="latest") return "Latest measurement"; if (k=="connected") return "Connected"; if (k=="notConnected") return "Not connected"; if (k=="ok") return "OK"; if (k=="notOk") return "Not OK";
    if (k=="connections") return "Connections"; if (k=="location") return "Location"; if (k=="ip") return "IP address"; if (k=="freeheap") return "Free RAM / heap"; if (k=="sendInterval") return "Send interval"; if (k=="seconds") return "seconds";
    if (k=="quickActions") return "Quick actions"; if (k=="updateStatus") return "Update status"; if (k=="saveConfig") return "Save configuration"; if (k=="restart") return "Restart"; if (k=="resetConfig") return "Reset configuration";
    if (k=="save") return "Save"; if (k=="saveRestart") return "Save and restart"; if (k=="password") return "Password"; if (k=="user") return "User"; if (k=="brokerHost") return "Broker host"; if (k=="useTls") return "Use TLS / secure";
    if (k=="topicTemp") return "Temperature topic"; if (k=="topicHum") return "Humidity topic"; if (k=="topicLed") return "LED topic"; if (k=="testMqtt") return "Test MQTT connection"; if (k=="latestTest") return "Latest test"; if (k=="mqttConnected") return "MQTT is connected"; if (k=="mqttNotConnected") return "MQTT is not connected";
    if (k=="locationName") return "Location name"; if (k=="sensorType") return "Sensor type"; if (k=="dhtPin") return "DHT data pin"; if (k=="blueSensor") return "DHT11 - blue sensor"; if (k=="whiteSensor") return "DHT22 / AM2302 - white sensor"; if (k=="normalPin") return "Normally your files use D5 / GPIO14.";
    if (k=="languageCode") return "Language code"; if (k=="retained") return "Retained MQTT messages"; if (k=="saved") return "Saved."; if (k=="sensorSaved") return "Sensor saved."; if (k=="mqttSaved") return "MQTT saved.";
    if (k=="systemHelp") return "Hostname, language, send interval and retained MQTT. Location is changed under Sensor, and topics under MQTT.";
    if (k=="firmwareUpload") return "Firmware upload"; if (k=="firmwareHelp") return "Select a compiled .bin file from Arduino IDE and upload it directly to NodeMCU."; if (k=="about") return "Temperature and humidity sensor with MQTT, OLED and LED control.";
  }
  if (l == "D") {
    if (k=="status") return "Status"; if (k=="sensor") return "Sensor"; if (k=="wifi") return "WiFi"; if (k=="mqtt") return "MQTT"; if (k=="system") return "System"; if (k=="firmware") return "Firmware"; if (k=="info") return "Info";
    if (k=="temperature") return "Temperatur"; if (k=="humidity") return "Feuchtigkeit"; if (k=="led") return "LED"; if (k=="on") return "Ein"; if (k=="off") return "Aus"; if (k=="turnOn") return "Einschalten"; if (k=="turnOff") return "Ausschalten";
    if (k=="latest") return "Letzte Messung"; if (k=="connected") return "Verbunden"; if (k=="notConnected") return "Nicht verbunden"; if (k=="ok") return "OK"; if (k=="notOk") return "Nicht OK";
    if (k=="connections") return "Verbindungen"; if (k=="location") return "Standort"; if (k=="ip") return "IP-Adresse"; if (k=="freeheap") return "Freier RAM / Heap"; if (k=="sendInterval") return "Sendeintervall"; if (k=="seconds") return "Sekunden";
    if (k=="quickActions") return "Schnellaktionen"; if (k=="updateStatus") return "Status aktualisieren"; if (k=="saveConfig") return "Konfiguration speichern"; if (k=="restart") return "Neustart"; if (k=="resetConfig") return "Konfiguration zurücksetzen";
    if (k=="save") return "Speichern"; if (k=="saveRestart") return "Speichern und neu starten"; if (k=="password") return "Passwort"; if (k=="user") return "Benutzer"; if (k=="brokerHost") return "Broker Host"; if (k=="useTls") return "TLS / secure verwenden";
    if (k=="topicTemp") return "Topic Temperatur"; if (k=="topicHum") return "Topic Feuchtigkeit"; if (k=="topicLed") return "Topic LED"; if (k=="testMqtt") return "MQTT-Verbindung testen"; if (k=="latestTest") return "Letzter Test"; if (k=="mqttConnected") return "MQTT ist verbunden"; if (k=="mqttNotConnected") return "MQTT ist nicht verbunden";
    if (k=="locationName") return "Name des Standorts"; if (k=="sensorType") return "Sensortyp"; if (k=="dhtPin") return "DHT Daten-Pin"; if (k=="blueSensor") return "DHT11 - blauer Sensor"; if (k=="whiteSensor") return "DHT22 / AM2302 - weißer Sensor"; if (k=="normalPin") return "Normalerweise verwenden deine Dateien D5 / GPIO14.";
    if (k=="languageCode") return "Sprachcode"; if (k=="retained") return "Retained MQTT-Nachrichten"; if (k=="saved") return "Gespeichert."; if (k=="sensorSaved") return "Sensor gespeichert."; if (k=="mqttSaved") return "MQTT gespeichert.";
    if (k=="systemHelp") return "Hostname, Sprache, Sendeintervall und retained MQTT. Der Standort wird unter Sensor geändert, Topics unter MQTT.";
    if (k=="firmwareUpload") return "Firmware hochladen"; if (k=="firmwareHelp") return "Wähle eine kompilierte .bin-Datei aus der Arduino IDE und lade sie direkt auf den NodeMCU."; if (k=="about") return "Temperatur- und Feuchtigkeitssensor mit MQTT, OLED und LED-Steuerung.";
  }
  // DK standard
  if (k=="status") return "Status"; if (k=="sensor") return "Sensor"; if (k=="wifi") return "WiFi"; if (k=="mqtt") return "MQTT"; if (k=="system") return "System"; if (k=="firmware") return "Firmware"; if (k=="info") return "Info";
  if (k=="temperature") return "Temperatur"; if (k=="humidity") return "Fugtighed"; if (k=="led") return "LED"; if (k=="on") return "Tændt"; if (k=="off") return "Slukket"; if (k=="turnOn") return "Tænd"; if (k=="turnOff") return "Sluk";
  if (k=="latest") return "Seneste måling"; if (k=="connected") return "Tilsluttet"; if (k=="notConnected") return "Ikke forbundet"; if (k=="ok") return "OK"; if (k=="notOk") return "Ikke OK";
  if (k=="connections") return "Forbindelser"; if (k=="location") return "Placering"; if (k=="ip") return "IP-adresse"; if (k=="freeheap") return "Fri RAM / heap"; if (k=="sendInterval") return "Send interval"; if (k=="seconds") return "sekunder";
  if (k=="quickActions") return "Hurtige handlinger"; if (k=="updateStatus") return "Opdater status"; if (k=="saveConfig") return "Gem konfiguration"; if (k=="restart") return "Genstart"; if (k=="resetConfig") return "Nulstil konfiguration";
  if (k=="save") return "Gem"; if (k=="saveRestart") return "Gem og genstart"; if (k=="password") return "Password"; if (k=="user") return "Bruger"; if (k=="brokerHost") return "Broker host"; if (k=="useTls") return "Brug TLS / secure";
  if (k=="topicTemp") return "Topic temperatur"; if (k=="topicHum") return "Topic fugtighed"; if (k=="topicLed") return "Topic LED"; if (k=="testMqtt") return "Test MQTT-forbindelse"; if (k=="latestTest") return "Seneste test"; if (k=="mqttConnected") return "MQTT er forbundet"; if (k=="mqttNotConnected") return "MQTT er ikke forbundet";
  if (k=="locationName") return "Navn på placering"; if (k=="sensorType") return "Sensortype"; if (k=="dhtPin") return "DHT data pin"; if (k=="blueSensor") return "DHT11 - blå sensor"; if (k=="whiteSensor") return "DHT22 / AM2302 - hvid sensor"; if (k=="normalPin") return "Normalt bruger dine filer D5 / GPIO14.";
  if (k=="languageCode") return "Sprogkode"; if (k=="retained") return "Retained MQTT beskeder"; if (k=="saved") return "Gemt."; if (k=="sensorSaved") return "Sensor er gemt."; if (k=="mqttSaved") return "MQTT er gemt.";
  if (k=="systemHelp") return "Hostname, sprog, sendeinterval og retained MQTT. Placering ændres under Sensor, og topics ændres under MQTT.";
  if (k=="firmwareUpload") return "Firmware upload"; if (k=="firmwareHelp") return "Vælg en kompileret .bin-fil fra Arduino IDE og upload den direkte til NodeMCU."; if (k=="about") return "Temperatur- og fugtighedsmåler med MQTT, OLED og LED-styring.";
  return String(key);
}

String statusText(bool ok) {
  return ok ? String("<span class='ok'>OK</span>") : String("<span class='bad'>Ikke OK</span>");
}

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

void strlcpySafe(char* dst, const String& src, size_t len) {
  src.substring(0, len - 1).toCharArray(dst, len);
}

void setDefaults() {
  memset(&cfg, 0, sizeof(cfg));
  cfg.magic = CONFIG_MAGIC;
  strlcpy(cfg.hostname, "nodemcu-tempmqtt", sizeof(cfg.hostname));
  strlcpy(cfg.language, "DK", sizeof(cfg.language));
  strlcpy(cfg.sensorName, "Farum Stue", sizeof(cfg.sensorName));
  cfg.dhtPin = DEFAULT_DHT_PIN;
  cfg.dhtType = 22;
  cfg.mqttPort = 8883;
  cfg.mqttTls = true;
  cfg.publishIntervalMs = 5000;
  cfg.retained = true;
  strlcpy(cfg.topicTemp, "WDP/Custom/Temperature/Home/LivingRoom/temperature", sizeof(cfg.topicTemp));
  strlcpy(cfg.topicHum,  "WDP/Custom/Temperature/Home/LivingRoom/humidity", sizeof(cfg.topicHum));
  strlcpy(cfg.topicLed,  "WDP/Custom/Temperature/Home/LivingRoom/led", sizeof(cfg.topicLed));
}

void loadConfig() {
  EEPROM.begin(EEPROM_SIZE);
  EEPROM.get(0, cfg);
  if (cfg.magic != CONFIG_MAGIC || cfg.publishIntervalMs < 1000 || cfg.publishIntervalMs > 3600000) {
    setDefaults();
    EEPROM.put(0, cfg);
    EEPROM.commit();
  }
}

void saveConfig() {
  cfg.magic = CONFIG_MAGIC;
  EEPROM.put(0, cfg);
  EEPROM.commit();
}

void applyPreset(const String& p) {
  if (p == "living") {
    strlcpy(cfg.hostname, "temp-stue", sizeof(cfg.hostname));
    strlcpy(cfg.sensorName, "Farum Stue", sizeof(cfg.sensorName));
    cfg.dhtPin = D5; cfg.dhtType = 22;
    strlcpy(cfg.topicTemp, "WDP/Custom/Temperature/Home/LivingRoom/temperature", sizeof(cfg.topicTemp));
    strlcpy(cfg.topicHum,  "WDP/Custom/Temperature/Home/LivingRoom/humidity", sizeof(cfg.topicHum));
    strlcpy(cfg.topicLed,  "WDP/Custom/Temperature/Home/LivingRoom/led", sizeof(cfg.topicLed));
  } else if (p == "cellar") {
    strlcpy(cfg.hostname, "temp-kaelder", sizeof(cfg.hostname));
    strlcpy(cfg.sensorName, "Farum Kaelder", sizeof(cfg.sensorName));
    cfg.dhtPin = D5; cfg.dhtType = 22;
    strlcpy(cfg.topicTemp, "WDP/Custom/Temperature/Home/Cellar/temperature", sizeof(cfg.topicTemp));
    strlcpy(cfg.topicHum,  "WDP/Custom/Temperature/Home/Cellar/humidity", sizeof(cfg.topicHum));
    strlcpy(cfg.topicLed,  "WDP/Custom/Temperature/Home/Cellar/led", sizeof(cfg.topicLed));
  } else if (p == "shus") {
    strlcpy(cfg.hostname, "temp-sommerhus", sizeof(cfg.hostname));
    strlcpy(cfg.sensorName, "Sommerhus", sizeof(cfg.sensorName));
    cfg.dhtPin = D5; cfg.dhtType = 22;
    strlcpy(cfg.topicTemp, "WDP/Custom/Temperature/S-hus/temperature", sizeof(cfg.topicTemp));
    strlcpy(cfg.topicHum,  "WDP/Custom/Temperature/S-hus/humidity", sizeof(cfg.topicHum));
    strlcpy(cfg.topicLed,  "WDP/Custom/Temperature/S-hus/led", sizeof(cfg.topicLed));
  }
}

