/*
 * ============================================================
 * KAIHATSU LAB — TÉLÉMÈTRE / SURVEILLANCE
 * ESP32 30 broches + HC-SR04 + SSD1306 + serveur web + Gmail
 * ============================================================
 *
 * OLED SSD1306 I2C :
 *   SDA -> GPIO 21
 *   SCL -> GPIO 22
 *   VCC -> 3.3V
 *   GND -> GND
 *
 * HC-SR04 :
 *   TRIG -> GPIO 4
 *   ECHO -> GPIO 5
 *
 * IMPORTANT :
 * Le HC-SR04 classique est généralement alimenté en 5 V et son
 * signal ECHO peut monter à 5 V. L'entrée ESP32 n'est pas 5 V
 * tolérante : utiliser un pont diviseur 5 V -> 3.3 V sur ECHO.
 *
 * Gmail :
 *   smtp.gmail.com / port 465
 *   Utiliser un mot de passe d'application Google.
 *   Ne jamais mettre le mot de passe normal du compte Google.
 *
 * Bibliothèques :
 *   Adafruit GFX
 *   Adafruit SSD1306
 *   ESP Mail Client
 * ============================================================
 */

#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ESP_Mail_Client.h>

// ============================================================
// WIFI
// ============================================================

const char* WIFI_SSID = "TON_WIFI";
const char* WIFI_PASSWORD = "TON_MOT_DE_PASSE_WIFI";

// ============================================================
// GMAIL
// ============================================================

#define SMTP_HOST "smtp.gmail.com"
#define SMTP_PORT 465

#define AUTHOR_EMAIL "tonadresse@gmail.com"
#define AUTHOR_PASSWORD "MOT_DE_PASSE_APPLICATION"
#define RECIPIENT_EMAIL "tonadresse@gmail.com"

// ============================================================
// GPIO — ESP32 30 BROCHES
// ============================================================

#define OLED_SDA 21
#define OLED_SCL 22

#define TRIG_PIN 4
#define ECHO_PIN 5

// ============================================================
// OLED
// ============================================================

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// ============================================================
// SERVEUR WEB
// ============================================================

WebServer server(80);

// ============================================================
// SMTP
// ============================================================

SMTPSession smtp;
Session_Config smtpConfig;
SMTP_Message message;

// ============================================================
// ÉTAT DU SYSTÈME
// ============================================================

float currentDistance = -1.0;
float referenceDistance = -1.0;
float surveillanceThreshold = 5.0;

bool surveillanceEnabled = false;
bool alarmTriggered = false;

unsigned long measurementCount = 0;
unsigned long lastMeasurement = 0;
unsigned long lastAlert = 0;

const unsigned long alertCooldown = 60000UL;
const int REQUIRED_CONFIRMATIONS = 3;

int movementConfirmations = 0;

// ============================================================
// MESURE HC-SR04
// ============================================================

float readDistanceCM() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);

  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);

  digitalWrite(TRIG_PIN, LOW);

  long duration = pulseIn(ECHO_PIN, HIGH, 30000);

  if (duration == 0) return -1.0;

  float distance = duration * 0.0343 / 2.0;

  if (distance < 2.0 || distance > 450.0) return -1.0;

  return distance;
}

// ============================================================
// OLED
// ============================================================

void updateOLED() {
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);

  display.setTextSize(1);
  display.setCursor(0, 0);

  if (surveillanceEnabled) {
    display.print("SURVEILLANCE");
    if (alarmTriggered) display.print(" !");
  } else {
    display.print("TELEMETRE");
  }

  display.setTextSize(2);
  display.setCursor(0, 15);

  if (currentDistance < 0) {
    display.println("HORS");
    display.setCursor(0, 35);
    display.println("PORTEE");
  } else {
    display.print(currentDistance, 1);
    display.println(" cm");
  }

  // Barre graphique : 0 à 200 cm
  const int barX = 0;
  const int barY = 56;
  const int barW = 128;
  const int barH = 7;

  display.drawRect(barX, barY, barW, barH, SSD1306_WHITE);

  if (currentDistance >= 0) {
    float ratio = currentDistance / 200.0;
    if (ratio > 1.0) ratio = 1.0;

    int fillWidth = (int)((barW - 2) * ratio);

    display.fillRect(
      barX + 1,
      barY + 1,
      fillWidth,
      barH - 2,
      SSD1306_WHITE
    );
  }

  display.display();
}

// ============================================================
// GMAIL
// ============================================================

void smtpCallback(SMTP_Status status) {
  Serial.println(status.info());

  if (status.success()) {
    Serial.println("Email envoyé avec succès.");
  }
}

void setupSMTP() {
  smtpConfig.server.host_name = SMTP_HOST;
  smtpConfig.server.port = SMTP_PORT;

  smtpConfig.login.email = AUTHOR_EMAIL;
  smtpConfig.login.password = AUTHOR_PASSWORD;
  smtpConfig.login.user_domain = "";

  smtp.callback(smtpCallback);
}

bool sendEmail(String subject, String body) {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi non connecté : email impossible.");
    return false;
  }

  smtpConfig.time.ntp_server = "pool.ntp.org,time.nist.gov";
  smtpConfig.time.gmt_offset = 1;
  smtpConfig.time.day_light_offset = 1;

  if (!smtp.connect(&smtpConfig)) {
    Serial.println("Impossible de se connecter à Gmail.");
    return false;
  }

  message.sender.name = "Kaihatsu Lab";
  message.sender.email = AUTHOR_EMAIL;
  message.subject = subject.c_str();

  message.addRecipient("Destinataire", RECIPIENT_EMAIL);

  message.text.content = body.c_str();
  message.text.charSet = "utf-8";
  message.text.transfer_encoding = "base64";

  bool success = MailClient.sendMail(&smtp, &message);

  message.clearRecipients();
  smtp.sendingResult.clear();
  smtp.closeSession();

  if (success) {
    Serial.println("EMAIL ENVOYÉ !");
  } else {
    Serial.print("ERREUR EMAIL : ");
    Serial.println(smtp.errorReason());
  }

  return success;
}

// ============================================================
// SURVEILLANCE
// ============================================================

void triggerAlarm() {
  if (!surveillanceEnabled) return;
  if (referenceDistance < 0) return;
  if (alarmTriggered) return;

  unsigned long now = millis();

  if (lastAlert != 0 && now - lastAlert < alertCooldown) {
    Serial.println("Cooldown alerte actif.");
    return;
  }

  alarmTriggered = true;
  lastAlert = now;

  Serial.println("!!! MOUVEMENT DETECTE !!!");

  String body =
    "Une variation a été détectée par le télémètre Kaihatsu Lab.

"
    "Distance de référence : " + String(referenceDistance, 1) + " cm
" +
    "Distance actuelle : " + String(currentDistance, 1) + " cm
" +
    "Écart : " + String(abs(currentDistance - referenceDistance), 1) + " cm

" +
    "ESP32 + HC-SR04 + SSD1306";

  sendEmail(
    "Kaihatsu Lab - Alerte surveillance",
    body
  );
}

void updateSurveillance() {
  if (!surveillanceEnabled) {
    movementConfirmations = 0;
    alarmTriggered = false;
    return;
  }

  if (referenceDistance < 0 || currentDistance < 0) {
    movementConfirmations = 0;
    return;
  }

  float difference = abs(currentDistance - referenceDistance);

  if (difference >= surveillanceThreshold) {
    movementConfirmations++;

    if (movementConfirmations >= REQUIRED_CONFIRMATIONS) {
      triggerAlarm();
    }
  } else {
    movementConfirmations = 0;

    if (alarmTriggered) {
      Serial.println("Retour à la normale.");
      alarmTriggered = false;
    }
  }
}

// ============================================================
// INTERFACE WEB
// ============================================================

String htmlPage() {
  return R"rawliteral(
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Kaihatsu Lab — Télémètre</title>
<style>
*{box-sizing:border-box}
body{margin:0;font-family:Arial,sans-serif;background:#0b0b0b;color:#fff}
header{padding:20px;text-align:center;border-bottom:1px solid #333}
h1{margin:0;font-size:24px}
.container{max-width:700px;margin:auto;padding:20px}
.card{background:#151515;border:1px solid #303030;border-radius:14px;padding:20px;margin-bottom:15px}
.distance{font-size:52px;font-weight:bold;text-align:center;margin:15px 0}
.bar{height:22px;background:#282828;border-radius:12px;overflow:hidden}
.barFill{height:100%;width:0%;background:#fff;transition:width .3s}
button{width:100%;padding:14px;border:0;border-radius:10px;margin-top:10px;font-size:16px;cursor:pointer}
input{width:100%;padding:12px;margin-top:6px;margin-bottom:12px;border-radius:8px;border:1px solid #444;background:#202020;color:#fff}
.status{padding:12px;border-radius:8px;background:#202020;margin-top:10px}
.on{border:1px solid #fff}
.alarm{background:#fff;color:#000}
.small{color:#aaa;font-size:13px}
</style>
</head>
<body>
<header>
<h1>KAIHATSU LAB</h1>
<div class="small">Télémètre ultrasonique ESP32</div>
</header>
<div class="container">

<div class="card">
<h2>📏 Distance</h2>
<div class="distance" id="distance">-- cm</div>
<div class="bar"><div class="barFill" id="bar"></div></div>
</div>

<div class="card">
<h2>🛡️ Surveillance</h2>
<div class="status" id="surveillance">Désactivée</div>

<label>Distance de référence</label>
<input id="reference" type="number" step="0.1">

<label>Seuil de déclenchement (cm)</label>
<input id="threshold" type="number" step="0.1" value="5">

<button onclick="setReference()">🎯 Prendre la distance actuelle comme référence</button>
<button onclick="setThreshold()">⚙️ Enregistrer le seuil</button>
<button onclick="toggleSurveillance()">🛡️ Activer / désactiver la surveillance</button>
</div>

<div class="card">
<h2>📧 Gmail</h2>
<button onclick="testEmail()">📨 Envoyer un mail de test</button>
<div class="status" id="emailStatus">Prêt</div>
</div>

<div class="card">
<h2>📡 Système</h2>
<div class="status" id="system">Chargement...</div>
</div>

</div>
<script>
async function update(){
  try{
    const r=await fetch('/api/status');
    const d=await r.json();

    document.getElementById('distance').innerText =
      d.distance<0 ? 'Hors portée' : d.distance.toFixed(1)+' cm';

    let percent=d.distance<0?0:Math.min(100,(d.distance/200)*100);
    document.getElementById('bar').style.width=percent+'%';

    document.getElementById('reference').value =
      d.reference>=0 ? d.reference : '';

    document.getElementById('threshold').value=d.threshold;

    const surveillance=document.getElementById('surveillance');

    if(d.surveillance){
      surveillance.innerText=d.alarm?'⚠️ MOUVEMENT DÉTECTÉ':'🟢 Surveillance active';
      surveillance.className=d.alarm?'status alarm':'status on';
    }else{
      surveillance.innerText='⚪ Surveillance désactivée';
      surveillance.className='status';
    }

    document.getElementById('system').innerHTML =
      'Wi-Fi : '+(d.wifi?'connecté':'déconnecté')+'<br>'+
      'IP : '+d.ip+'<br>'+
      'Mesures : '+d.measurements+'<br>'+
      'RSSI : '+d.rssi+' dBm';
  }catch(e){console.log(e)}
}

async function setReference(){
  const r=await fetch('/api/reference',{method:'POST'});
  alert(await r.text()); update();
}

async function setThreshold(){
  const value=document.getElementById('threshold').value;
  const r=await fetch('/api/threshold?value='+encodeURIComponent(value),{method:'POST'});
  alert(await r.text()); update();
}

async function toggleSurveillance(){
  const r=await fetch('/api/surveillance',{method:'POST'});
  alert(await r.text()); update();
}

async function testEmail(){
  const status=document.getElementById('emailStatus');
  status.innerText='Envoi en cours...';
  const r=await fetch('/api/test-email',{method:'POST'});
  status.innerText=await r.text();
}

update();
setInterval(update,1000);
</script>
</body>
</html>
)rawliteral";
}

// ============================================================
// API
// ============================================================

void handleStatus() {
  String json = "{";

  json += ""distance":" + String(currentDistance, 1) + ",";
  json += ""reference":" + String(referenceDistance, 1) + ",";
  json += ""threshold":" + String(surveillanceThreshold, 1) + ",";
  json += ""surveillance":" + String(surveillanceEnabled ? "true" : "false") + ",";
  json += ""alarm":" + String(alarmTriggered ? "true" : "false") + ",";
  json += ""measurements":" + String(measurementCount) + ",";
  json += ""wifi":" + String(WiFi.status() == WL_CONNECTED ? "true" : "false") + ",";
  json += ""ip":"" + WiFi.localIP().toString() + "",";
  json += ""rssi":" + String(WiFi.RSSI());

  json += "}";

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

void handleReference() {
  if (currentDistance < 0) {
    server.send(400, "text/plain", "Distance invalide.");
    return;
  }

  referenceDistance = currentDistance;
  alarmTriggered = false;
  movementConfirmations = 0;

  server.send(
    200,
    "text/plain",
    "Référence définie à " + String(referenceDistance, 1) + " cm."
  );
}

void handleThreshold() {
  if (!server.hasArg("value")) {
    server.send(400, "text/plain", "Valeur manquante.");
    return;
  }

  float value = server.arg("value").toFloat();

  if (value <= 0) {
    server.send(400, "text/plain", "Seuil invalide.");
    return;
  }

  surveillanceThreshold = value;

  server.send(
    200,
    "text/plain",
    "Seuil : " + String(surveillanceThreshold, 1) + " cm."
  );
}

void handleSurveillance() {
  surveillanceEnabled = !surveillanceEnabled;
  movementConfirmations = 0;
  alarmTriggered = false;

  if (surveillanceEnabled && referenceDistance < 0) {
    referenceDistance = currentDistance;
  }

  server.send(
    200,
    "text/plain",
    surveillanceEnabled
      ? "Surveillance active."
      : "Surveillance désactivée."
  );
}

void handleTestEmail() {
  bool result = sendEmail(
    "Kaihatsu Lab - Test",
    "Ceci est un email de test envoyé par l'ESP32.

"
    "Le système Gmail fonctionne."
  );

  server.send(
    result ? 200 : 500,
    "text/plain",
    result ? "Email de test envoyé !" : "Échec de l'envoi."
  );
}

void handleNotFound() {
  server.send(404, "text/plain", "404 - Page inexistante");
}

// ============================================================
// WIFI / SERVEUR
// ============================================================

void connectWiFi() {
  Serial.print("Connexion WiFi à ");
  Serial.println(WIFI_SSID);

  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  int attempts = 0;

  while (WiFi.status() != WL_CONNECTED && attempts < 40) {
    delay(500);
    Serial.print(".");
    attempts++;
  }

  Serial.println();

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println("WiFi CONNECTÉ");
    Serial.print("Adresse IP : ");
    Serial.println(WiFi.localIP());
    Serial.print("RSSI : ");
    Serial.print(WiFi.RSSI());
    Serial.println(" dBm");
  } else {
    Serial.println("ÉCHEC WIFI");
  }
}

void setupWebServer() {
  server.on("/", HTTP_GET, [](){
    server.send(200, "text/html", htmlPage());
  });

  server.on("/api/status", HTTP_GET, handleStatus);
  server.on("/api/reference", HTTP_POST, handleReference);
  server.on("/api/threshold", HTTP_POST, handleThreshold);
  server.on("/api/surveillance", HTTP_POST, handleSurveillance);
  server.on("/api/test-email", HTTP_POST, handleTestEmail);

  server.on("/favicon.ico", HTTP_GET, [](){
    server.send(204);
  });

  server.onNotFound(handleNotFound);
  server.begin();

  Serial.println("Serveur Web démarré.");
}

// ============================================================
// SETUP
// ============================================================

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

  Serial.println();
  Serial.println("================================");
  Serial.println(" KAIHATSU LAB");
  Serial.println(" ESP32 TÉLÉMÈTRE / SURVEILLANCE");
  Serial.println("================================");

  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  digitalWrite(TRIG_PIN, LOW);

  Wire.begin(OLED_SDA, OLED_SCL);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("ERREUR OLED !");
    while (true) delay(1000);
  }

  Serial.println("OLED OK");

  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(10, 10);
  display.println("KAIHATSU");
  display.setTextSize(1);
  display.setCursor(30, 40);
  display.println("Telemetre");
  display.display();

  delay(1500);

  connectWiFi();
  setupSMTP();
  setupWebServer();

  Serial.println("Système prêt.");

  if (WiFi.status() == WL_CONNECTED) {
    Serial.print("Ouvre dans votre navigateur : http://");
    Serial.println(WiFi.localIP());
  }
}

// ============================================================
// LOOP
// ============================================================

void loop() {
  server.handleClient();

  unsigned long now = millis();

  if (now - lastMeasurement >= 300) {
    lastMeasurement = now;

    currentDistance = readDistanceCM();
    measurementCount++;

    Serial.print("Distance : ");

    if (currentDistance < 0) {
      Serial.println("Hors de portée");
    } else {
      Serial.print(currentDistance, 1);
      Serial.println(" cm");
    }

    updateSurveillance();
    updateOLED();
  }
}
