import paho.mqtt.client as mqtt
from flask import Flask, render_template_string, jsonify, request
import threading
from collections import deque
import datetime
import urllib.request
import json
import ftplib
import io
import sqlite3
import os

# ── Base de données SQLite ─────────────────────────────────────────
DB_PATH = "/home/pimane/berceau/berceau.db"

def init_db():
    conn = sqlite3.connect(DB_PATH)
    c = conn.cursor()
    c.execute("""
        CREATE TABLE IF NOT EXISTS mesures (
            id          INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp   TEXT    DEFAULT (datetime('now','localtime')),
            heart_rate  REAL,
            breath_rate REAL,
            distance    REAL,
            bpm_optique REAL,
            spo2        REAL
        )
    """)
    c.execute("""
        CREATE TABLE IF NOT EXISTS alertes (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp  TEXT    DEFAULT (datetime('now','localtime')),
            type       TEXT,
            parametre  TEXT,
            valeur     REAL,
            message    TEXT
        )
    """)
    c.execute("""
        CREATE TABLE IF NOT EXISTS reconnaissances (
            id         INTEGER PRIMARY KEY AUTOINCREMENT,
            timestamp  TEXT    DEFAULT (datetime('now','localtime')),
            nom        TEXT,
            nb_visages INTEGER,
            alerte     INTEGER
        )
    """)
    conn.commit()
    conn.close()

init_db()

def db_save_mesure(d):
    try:
        conn = sqlite3.connect(DB_PATH)
        conn.execute("""
            INSERT INTO mesures (heart_rate, breath_rate, distance, bpm_optique, spo2)
            VALUES (?,?,?,?,?)
        """, (d["heart_rate"], d["breath_rate"], d["distance"], d["bpm_optique"], d["spo2"]))
        conn.commit()
        conn.close()
    except Exception as e:
        print("DB mesure error:", e)

def db_save_alerte(type_alerte, parametre, valeur, message):
    try:
        conn = sqlite3.connect(DB_PATH)
        conn.execute("""
            INSERT INTO alertes (type, parametre, valeur, message)
            VALUES (?,?,?,?)
        """, (type_alerte, parametre, valeur, message))
        conn.commit()
        conn.close()
    except Exception as e:
        print("DB alerte error:", e)

def db_save_reconnaissance(nom, nb_visages, alerte):
    try:
        conn = sqlite3.connect(DB_PATH)
        conn.execute("""
            INSERT INTO reconnaissances (nom, nb_visages, alerte)
            VALUES (?,?,?)
        """, (nom, nb_visages, 1 if alerte else 0))
        conn.commit()
        conn.close()
    except Exception as e:
        print("DB reconnaissance error:", e)

def check_and_log_alerts(d):
    """Vérifie les seuils et enregistre les alertes en base de données."""
    spo2 = d["spo2"]
    bpm  = d["bpm_optique"]
    hr   = d["heart_rate"]
    br   = d["breath_rate"]

    if spo2 > 0:
        if spo2 < 90:
            db_save_alerte("critique", "spo2", spo2, f"SpO2 critique : {spo2:.1f}%")
        elif spo2 < 94:
            db_save_alerte("warning",  "spo2", spo2, f"SpO2 basse : {spo2:.1f}%")

    if bpm > 0:
        if bpm < 60 or bpm > 180:
            db_save_alerte("critique", "bpm_optique", bpm, f"BPM optique critique : {bpm:.0f}")
        elif bpm < 80 or bpm > 160:
            db_save_alerte("warning",  "bpm_optique", bpm, f"BPM optique anormal : {bpm:.0f}")

    if hr > 0:
        if hr < 60 or hr > 180:
            db_save_alerte("critique", "heart_rate", hr, f"BPM radar critique : {hr:.0f}")
        elif hr < 80 or hr > 160:
            db_save_alerte("warning",  "heart_rate", hr, f"BPM radar anormal : {hr:.0f}")

    if br > 0:
        if br < 15 or br > 70:
            db_save_alerte("critique", "breath_rate", br, f"Respiration critique : {br:.1f}")
        elif br < 20 or br > 60:
            db_save_alerte("warning",  "breath_rate", br, f"Respiration anormale : {br:.1f}")

# ── MQTT Configuration ─────────────────────────────────────────
MQTT_BROKER    = "broker.hivemq.com"
MQTT_PORT      = 1883

TOPIC_HEART    = "berceau/135701/cardiaque"
TOPIC_BREATH   = "berceau/135701/respiration"
TOPIC_DIST     = "berceau/135701/distance"
TOPIC_BPM_OPT  = "berceau/135701/bpm_optique"
TOPIC_SPO2     = "berceau/135701/spo2"
TOPIC_LUMIERE  = "berceau/135701/lumiere"

# ── Données temps réel ─────────────────────────────────────────
data = {
    "heart_rate":  0.0,
    "breath_rate": 0.0,
    "distance":    0.0,
    "bpm_optique": 0.0,
    "spo2":        0.0,
    "lumiere":     1.0,
    "connected":   False
}

# ── Historique (30 derniers points) ───────────────────────────
MAX_HIST = 30
history = {
    "timestamps": deque(maxlen=MAX_HIST),
    "heart_rate": deque(maxlen=MAX_HIST),
    "breath_rate": deque(maxlen=MAX_HIST),
    "bpm_optique": deque(maxlen=MAX_HIST),
    "spo2":        deque(maxlen=MAX_HIST),
}

def record_history():
    now = datetime.datetime.now().strftime("%H:%M:%S")
    history["timestamps"].append(now)
    history["heart_rate"].append(round(data["heart_rate"], 1))
    history["breath_rate"].append(round(data["breath_rate"], 1))
    history["bpm_optique"].append(round(data["bpm_optique"], 1))
    history["spo2"].append(round(data["spo2"], 1))
    # Sauvegarde en base de données SQLite
    db_save_mesure(data)
    check_and_log_alerts(data)

# ── MQTT Callbacks ─────────────────────────────────────────────
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        data["connected"] = True
        client.subscribe(TOPIC_HEART)
        client.subscribe(TOPIC_BREATH)
        client.subscribe(TOPIC_DIST)
        client.subscribe(TOPIC_BPM_OPT)
        client.subscribe(TOPIC_SPO2)
        client.subscribe(TOPIC_LUMIERE)

def on_message(client, userdata, msg):
    try:
        value = float(msg.payload.decode())
        if msg.topic == TOPIC_HEART:
            data["heart_rate"] = value
            record_history()
        elif msg.topic == TOPIC_BREATH:
            data["breath_rate"] = value
        elif msg.topic == TOPIC_DIST:
            data["distance"] = value
        elif msg.topic == TOPIC_BPM_OPT:
            data["bpm_optique"] = value
        elif msg.topic == TOPIC_SPO2:
            data["spo2"] = value
        elif msg.topic == TOPIC_LUMIERE:
            data["lumiere"] = value
    except:
        pass

def on_disconnect(client, userdata, rc):
    data["connected"] = False

def start_mqtt():
    client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1)
    client.on_connect    = on_connect
    client.on_message    = on_message
    client.on_disconnect = on_disconnect
    client.connect(MQTT_BROKER, MQTT_PORT)
    client.loop_forever()

threading.Thread(target=start_mqtt, daemon=True).start()

# ── HTML Dashboard ─────────────────────────────────────────────
HTML = """<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Berceau Connecte - Creche Lyra</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
  <style>
    /* ── Variables thème ── */
    :root {
      --bg:         #0f1923;
      --bg2:        #162032;
      --card:       #1a2740;
      --border:     #243452;
      --text:       #e8f0fe;
      --muted:      #7a90b4;
      --accent:     #4f9cf9;
      --accent2:    #34d399;
      --header-bg:  #0d1520;
      --shadow:     rgba(0,0,0,0.4);
      --chart-grid: rgba(255,255,255,0.05);
    }
    body.jour {
      --bg:         #f0f5ff;
      --bg2:        #e4ecfc;
      --card:       #ffffff;
      --border:     #c8d8f0;
      --text:       #1a2740;
      --muted:      #5a7094;
      --accent:     #2563eb;
      --accent2:    #059669;
      --header-bg:  #1e3a6e;
      --shadow:     rgba(0,0,0,0.1);
      --chart-grid: rgba(0,0,0,0.05);
    }
    * { box-sizing: border-box; margin: 0; padding: 0; transition: background 0.4s, color 0.3s, border-color 0.3s, box-shadow 0.3s; }
    body { font-family: 'Segoe UI', Arial, sans-serif; background: var(--bg); color: var(--text); min-height: 100vh; }

    /* ── Header portrait ── */
    header {
      background: var(--header-bg);
      padding: 12px 20px;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 10px;
      box-shadow: 0 2px 12px var(--shadow);
    }
    .header-top { display: flex; align-items: center; gap: 14px; width: 100%; justify-content: center; }
    .logo-circle {
      width: 46px; height: 46px; border-radius: 50%;
      background: linear-gradient(135deg, #4f9cf9, #34d399);
      display: flex; align-items: center; justify-content: center;
      font-size: 1.4em; font-weight: 900; color: white;
      box-shadow: 0 0 16px rgba(79,156,249,0.4);
      flex-shrink: 0;
    }
    .header-titles { color: white; }
    .header-titles h1 { font-size: 1.15em; font-weight: 700; letter-spacing: 0.5px; }
    .header-titles p  { font-size: 0.75em; opacity: 0.7; margin-top: 2px; }
    .header-bottom { display: flex; align-items: center; gap: 20px; justify-content: center; width: 100%; }
    .clock { color: rgba(255,255,255,0.85); font-size: 1em; font-variant-numeric: tabular-nums; font-weight: 600; letter-spacing: 1px; }

    /* ── Toggle jour/nuit ── */
    .toggle-wrap { display: flex; align-items: center; gap: 8px; }
    .toggle-label { font-size: 0.78em; color: rgba(255,255,255,0.6); }
    .toggle { position: relative; width: 64px; height: 30px; cursor: pointer; }
    .toggle input { display: none; }
    .slider {
      position: absolute; inset: 0; border-radius: 30px;
      background: #1f3060;
      display: flex; align-items: center;
      padding: 0 6px; justify-content: space-between;
      border: 1px solid rgba(255,255,255,0.1);
    }
    .slider .icon-moon, .slider .icon-sun { font-size: 0.85em; }
    .slider::after {
      content: ''; position: absolute;
      width: 22px; height: 22px; border-radius: 50%;
      background: white; top: 3px; left: 4px;
      transition: transform 0.35s cubic-bezier(0.4,0,0.2,1);
      box-shadow: 0 2px 6px rgba(0,0,0,0.3);
    }
    .toggle input:checked + .slider { background: #2563eb; }
    .toggle input:checked + .slider::after { transform: translateX(33px); }
    .auto-badge {
      font-size: 0.7em; background: rgba(79,156,249,0.25);
      color: #4f9cf9; border: 1px solid #4f9cf9;
      border-radius: 10px; padding: 2px 7px;
    }

    /* ── Status bar ── */
    .status-bar {
      display: flex; align-items: center; justify-content: center;
      gap: 8px; padding: 7px; font-size: 0.8em; color: var(--muted);
      background: var(--bg2); border-bottom: 1px solid var(--border);
    }
    .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
    .dot.on  { background: #34d399; box-shadow: 0 0 6px #34d399; }
    .dot.off { background: #f87171; box-shadow: 0 0 6px #f87171; }

    /* ── Main content portrait ── */
    main { padding: 14px 16px; max-width: 100%; }

    /* ── Section titles ── */
    .section-title {
      font-size: 0.72em; font-weight: 700; letter-spacing: 1.5px;
      text-transform: uppercase; color: var(--muted);
      margin-bottom: 12px; margin-top: 22px;
      border-left: 3px solid var(--accent);
      padding-left: 8px;
    }

    /* ── Cards portrait : 2 colonnes ── */
    .cards-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 12px;
    }
    .card {
      background: var(--card); border: 1px solid var(--border);
      border-radius: 14px; padding: 16px 12px;
      text-align: center;
      box-shadow: 0 4px 16px var(--shadow);
      position: relative; overflow: hidden;
    }
    .card::before {
      content: ''; position: absolute;
      top: 0; left: 0; right: 0; height: 3px;
      background: var(--accent);
      border-radius: 14px 14px 0 0;
    }
    .card.warning::before { background: #f59e0b; }
    .card.danger::before  { background: #f87171; }
    .card .icon  { font-size: 1.5em; margin-bottom: 6px; }
    .card .label { color: var(--muted); font-size: 0.75em; margin-bottom: 5px; font-weight: 500; line-height: 1.3; }
    .card .value { font-size: 2.2em; font-weight: 700; color: var(--accent); line-height: 1; }
    .card.warning .value { color: #f59e0b; }
    .card.danger  .value { color: #f87171; }
    .card .unit  { color: var(--muted); font-size: 0.75em; margin-top: 4px; }
    .card .alert-badge {
      display: none; margin-top: 6px;
      font-size: 0.68em; font-weight: 700;
      padding: 3px 8px; border-radius: 20px;
    }
    .card.warning .alert-badge { display: inline-block; background: rgba(245,158,11,0.15); color: #f59e0b; border: 1px solid #f59e0b; }
    .card.danger  .alert-badge { display: inline-block; background: rgba(248,113,113,0.15); color: #f87171; border: 1px solid #f87171; }

    /* ── Graphiques portrait : 2 colonnes ── */
    .charts-grid {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      gap: 12px;
    }
    .chart-card {
      background: var(--card); border: 1px solid var(--border);
      border-radius: 14px; padding: 14px;
      box-shadow: 0 4px 16px var(--shadow);
    }
    .chart-title {
      font-size: 0.75em; font-weight: 600; color: var(--muted);
      margin-bottom: 10px; display: flex; align-items: center; gap: 6px;
    }
    .chart-title span { font-size: 1em; }
    .chart-wrap { position: relative; height: 140px; }

    /* ── Video portrait ── */
    .video-wrap { margin-bottom: 8px; }
    .video-card {
      background: var(--card); border: 1px solid var(--border);
      border-radius: 14px; overflow: hidden;
      box-shadow: 0 4px 16px var(--shadow);
    }
    .video-header {
      background: #0d1520; color: #e8f0fe;
      padding: 9px 14px; font-size: 0.78em;
      display: flex; align-items: center; gap: 8px;
      border-bottom: 1px solid var(--border);
    }
    .rec-dot {
      width: 8px; height: 8px; border-radius: 50%;
      background: #f87171;
      box-shadow: 0 0 6px #f87171;
      animation: blink 1.2s infinite;
      display: inline-block;
    }
    @keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.2} }
    .video-container {
      position: relative; width: 100%;
      background: #000; line-height: 0;
    }
    .video-container img {
      width: 100%; max-height: 420px;
      object-fit: contain; display: block;
    }
    .video-offline {
      display: none; position: absolute; inset: 0;
      background: #0d1520; color: var(--muted);
      flex-direction: column; align-items: center;
      justify-content: center; gap: 8px;
      font-size: 1em; min-height: 180px;
    }

    /* ── Footer ── */
    footer {
      text-align: center; padding: 16px;
      font-size: 0.72em; color: var(--muted);
      border-top: 1px solid var(--border);
      margin-top: 24px;
    }
  </style>
</head>
<body>

  <!-- ── Header portrait ── -->
  <header>
    <div class="header-top">
      <div class="logo-circle">L</div>
      <div class="header-titles">
        <h1>Creche Lyra &mdash; Schaerbeek</h1>
        <p>Berceau Connecte &bull; Surveillance intelligente</p>
      </div>
    </div>
    <div class="header-bottom">
      <div class="clock" id="clock">--:--:--</div>
      <a href="/historique" style="color:rgba(255,255,255,0.75);font-size:0.8em;text-decoration:none;background:rgba(79,156,249,0.15);border:1px solid rgba(79,156,249,0.4);padding:4px 12px;border-radius:20px;">&#128200; Historique DB</a>
      <div class="toggle-wrap">
        <span class="toggle-label">Nuit</span>
        <label class="toggle">
          <input type="checkbox" id="modeToggle" onchange="onManualToggle()">
          <div class="slider">
            <span class="icon-moon">&#9790;</span>
            <span class="icon-sun">&#9728;</span>
          </div>
        </label>
        <span class="toggle-label">Jour</span>
        <span class="auto-badge" id="autoBadge">AUTO</span>
      </div>
    </div>
  </header>

  <!-- ── Status ── -->
  <div class="status-bar">
    <span class="dot" id="dot"></span>
    <span id="status-text">Connexion MQTT...</span>
  </div>

  <main>

    <!-- ── Capteurs temps réel ── -->
    <div class="section-title">Surveillance en temps reel</div>
    <div class="cards-grid">
      <div class="card" id="card-breath">
        <div class="icon">&#129754;</div>
        <div class="label">Respiration (radar)</div>
        <div class="value" id="breath">--</div>
        <div class="unit">cycles / min</div>
        <div class="alert-badge" id="badge-breath">ALERTE</div>
      </div>
      <div class="card" id="card-heart">
        <div class="icon">&#10084;</div>
        <div class="label">Freq. cardiaque (radar)</div>
        <div class="value" id="heart">--</div>
        <div class="unit">BPM</div>
        <div class="alert-badge" id="badge-heart">ALERTE</div>
      </div>
      <div class="card" id="card-distance">
        <div class="icon">&#128207;</div>
        <div class="label">Distance</div>
        <div class="value" id="distance">--</div>
        <div class="unit">cm</div>
        <div class="alert-badge" id="badge-distance"></div>
      </div>
      <div class="card" id="card-bpm">
        <div class="icon">&#128147;</div>
        <div class="label">BPM optique (MAX30102)</div>
        <div class="value" id="bpm_opt">--</div>
        <div class="unit">BPM</div>
        <div class="alert-badge" id="badge-bpm">ALERTE</div>
      </div>
      <div class="card" id="card-spo2">
        <div class="icon">&#129978;</div>
        <div class="label">SpO2 (MAX30102)</div>
        <div class="value" id="spo2">--</div>
        <div class="unit">%</div>
        <div class="alert-badge" id="badge-spo2">ALERTE</div>
      </div>
    </div>

    <!-- ── Reconnaissance faciale ── -->
    <div class="section-title">Reconnaissance faciale</div>
    <div class="cards-grid" style="margin-bottom:16px;">
      <div class="card" id="card-person" style="grid-column: 1 / -1;">
        <div class="label">Personne detectee</div>
        <div class="value" id="person-nom" style="font-size:1.6em;">--</div>
        <div style="margin-top:10px;display:flex;gap:12px;justify-content:center;flex-wrap:wrap;">
          <span style="font-size:0.8em;color:var(--muted);">Visages : <strong id="person-nb">0</strong></span>
          <span style="font-size:0.8em;color:var(--muted);">Derniere detection : <strong id="person-ts">--</strong></span>
        </div>
        <div class="alert-badge" id="badge-person">ACCES NON AUTORISE</div>
      </div>
    </div>

    <!-- ── Camera ── -->
    <div class="section-title">Surveillance video - Reconnaissance faciale</div>
    <div class="video-wrap">
      <div class="video-card">
        <div class="video-header">
          <span class="rec-dot"></span> LIVE &nbsp;&bull;&nbsp; Camera NoIR &nbsp;&bull;&nbsp; Pi 2 (192.168.68.123)
        </div>
        <div class="video-container">
          <img src="http://192.168.68.123:8080/video_feed"
               id="videoFeed"
               alt="Flux camera"
               onerror="this.src='';document.getElementById('videoStatus').style.display='flex'">
          <div class="video-offline" id="videoStatus">
            <div>&#128247; Camera hors ligne</div>
            <small>Verifier Pi 2 (192.168.68.123:8080)</small>
          </div>
        </div>
      </div>
    </div>

    <!-- ── Graphiques historique ── -->
    <div class="section-title">Historique des mesures</div>
    <div class="charts-grid">
      <div class="chart-card">
        <div class="chart-title"><span>&#129754;</span> Respiration (cycles/min)</div>
        <div class="chart-wrap"><canvas id="chartBreath"></canvas></div>
      </div>
      <div class="chart-card">
        <div class="chart-title"><span>&#10084;</span> Freq. cardiaque radar (BPM)</div>
        <div class="chart-wrap"><canvas id="chartHeart"></canvas></div>
      </div>
      <div class="chart-card">
        <div class="chart-title"><span>&#128147;</span> BPM optique (BPM)</div>
        <div class="chart-wrap"><canvas id="chartBpm"></canvas></div>
      </div>
      <div class="chart-card">
        <div class="chart-title"><span>&#129978;</span> SpO2 (%)</div>
        <div class="chart-wrap"><canvas id="chartSpo2"></canvas></div>
      </div>
    </div>

  </main>

  <footer>
    Creche Lyra &bull; Schaerbeek, Bruxelles &bull; Berceau Connecte v2.0
  </footer>

  <script>
    let autoMode = true;

    // ── Horloge ──
    function updateClock() {
      const now = new Date();
      const h = String(now.getHours()).padStart(2,'0');
      const m = String(now.getMinutes()).padStart(2,'0');
      const s = String(now.getSeconds()).padStart(2,'0');
      document.getElementById('clock').textContent = h+':'+m+':'+s;
    }
    setInterval(updateClock, 1000);
    updateClock();

    // ── Theme ──
    function setTheme(isJour) {
      document.getElementById('modeToggle').checked = isJour;
      document.body.classList.toggle('jour', isJour);
      updateChartTheme();
    }
    function onManualToggle() {
      autoMode = false;
      document.getElementById('autoBadge').style.opacity = '0.3';
      setTheme(document.getElementById('modeToggle').checked);
    }

    // ── Alertes couleur cartes ──
    function setCardStatus(cardId, badgeId, value, warnLow, warnHigh, dangerLow, dangerHigh, badgeText) {
      const card  = document.getElementById(cardId);
      const badge = document.getElementById(badgeId);
      card.classList.remove('warning','danger');
      if (value > 0) {
        if (value < dangerLow || value > dangerHigh) {
          card.classList.add('danger');
          if (badge) badge.textContent = 'CRITIQUE';
        } else if (value < warnLow || value > warnHigh) {
          card.classList.add('warning');
          if (badge) badge.textContent = 'ATTENTION';
        }
      }
    }

    // ── Charts ──
    const chartDefaults = {
      type: 'line',
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: { duration: 400 },
        plugins: { legend: { display: false } },
        scales: {
          x: { ticks: { color: '#7a90b4', font: { size: 10 }, maxTicksLimit: 6 },
               grid: { color: 'rgba(255,255,255,0.05)' } },
          y: { ticks: { color: '#7a90b4', font: { size: 10 } },
               grid: { color: 'rgba(255,255,255,0.05)' } }
        },
        elements: { point: { radius: 2 }, line: { tension: 0.4, borderWidth: 2 } }
      }
    };

    function makeChart(canvasId, color, refLines) {
      const ctx = document.getElementById(canvasId).getContext('2d');
      const cfg = JSON.parse(JSON.stringify(chartDefaults));
      cfg.data = {
        labels: [],
        datasets: [{
          data: [],
          borderColor: color,
          backgroundColor: color.replace('rgb', 'rgba').replace(')', ',0.08)'),
          fill: true
        }]
      };
      if (refLines) {
        cfg.options.plugins.annotation = refLines;
      }
      return new Chart(ctx, cfg);
    }

    const charts = {
      breath: makeChart('chartBreath', 'rgb(52,211,153)'),
      heart:  makeChart('chartHeart',  'rgb(248,113,113)'),
      bpm:    makeChart('chartBpm',    'rgb(251,191,36)'),
      spo2:   makeChart('chartSpo2',   'rgb(79,156,249)'),
    };

    function updateChartTheme() {
      const isJour = document.body.classList.contains('jour');
      const gridColor = isJour ? 'rgba(0,0,0,0.06)' : 'rgba(255,255,255,0.05)';
      const tickColor = isJour ? '#5a7094' : '#7a90b4';
      Object.values(charts).forEach(c => {
        c.options.scales.x.grid.color = gridColor;
        c.options.scales.y.grid.color = gridColor;
        c.options.scales.x.ticks.color = tickColor;
        c.options.scales.y.ticks.color = tickColor;
        c.update('none');
      });
    }

    function pushChart(chart, label, value, maxPoints) {
      chart.data.labels.push(label);
      chart.data.datasets[0].data.push(value);
      if (chart.data.labels.length > maxPoints) {
        chart.data.labels.shift();
        chart.data.datasets[0].data.shift();
      }
      chart.update('none');
    }

    // ── Mise à jour principale ──
    function update() {
      fetch('/api/data').then(r => r.json()).then(d => {

        // Valeurs
        document.getElementById('breath').textContent   = d.breath_rate.toFixed(1);
        document.getElementById('heart').textContent    = d.heart_rate.toFixed(0);
        document.getElementById('distance').textContent = d.distance.toFixed(0);
        document.getElementById('bpm_opt').textContent  = d.bpm_optique.toFixed(0);
        document.getElementById('spo2').textContent     = d.spo2.toFixed(1);

        // Status MQTT
        const dot = document.getElementById('dot');
        const txt = document.getElementById('status-text');
        dot.className = 'dot ' + (d.connected ? 'on' : 'off');
        txt.textContent = d.connected ? 'MQTT connecte - broker.hivemq.com' : 'MQTT deconnecte';

        // Alertes cartes
        // Respiration bebe: normal 30-60, warning 20-30 ou 60-70, danger <20 ou >70
        setCardStatus('card-breath',  'badge-breath',  d.breath_rate,  20, 60,  15, 70);
        // BPM radar: normal 80-160, warning 60-80 ou 160-180, danger <60 ou >180
        setCardStatus('card-heart',   'badge-heart',   d.heart_rate,   80, 160, 60, 180);
        // BPM optique: meme seuils
        setCardStatus('card-bpm',     'badge-bpm',     d.bpm_optique,  80, 160, 60, 180);
        // SpO2: normal >=95, warning 90-94, danger <90
        setCardStatus('card-spo2',    'badge-spo2',    d.spo2,         94, 101, 90, 101);

        // Theme auto LM393
        if (autoMode) {
          setTheme(d.lumiere < 0.5);
        }
      });
    }

    // ── Historique ──
    function updateHistory() {
      fetch('/api/history').then(r => r.json()).then(h => {
        if (!h.timestamps || h.timestamps.length === 0) return;
        // Recharge complet des graphiques
        const labels = h.timestamps;
        charts.breath.data.labels = [...labels];
        charts.breath.data.datasets[0].data = [...h.breath_rate];
        charts.heart.data.labels  = [...labels];
        charts.heart.data.datasets[0].data  = [...h.heart_rate];
        charts.bpm.data.labels    = [...labels];
        charts.bpm.data.datasets[0].data    = [...h.bpm_optique];
        charts.spo2.data.labels   = [...labels];
        charts.spo2.data.datasets[0].data   = [...h.spo2];
        Object.values(charts).forEach(c => c.update('none'));
      });
    }

    // ── Reconnaissance faciale ──
    function updatePerson() {
      fetch('/api/person').then(r => r.json()).then(p => {
        const nom   = document.getElementById('person-nom');
        const badge = document.getElementById('badge-person');
        const card  = document.getElementById('card-person');

        nom.textContent = p.nom || '--';
        document.getElementById('person-nb').textContent = p.nb_visages || 0;
        document.getElementById('person-ts').textContent = p.timestamp  || '--';

        card.classList.remove('warning', 'danger');
        badge.style.display = 'none';

        if (p.alerte && p.nb_visages > 0) {
          card.classList.add('danger');
          badge.style.display  = 'inline-block';
          badge.textContent    = 'ACCES NON AUTORISE';
          nom.style.color      = '#f87171';
        } else if (p.nb_visages > 0) {
          card.classList.remove('danger');
          nom.style.color = '#34d399';
          badge.textContent = '';
        } else {
          nom.style.color = 'var(--accent)';
        }
      }).catch(() => {
        document.getElementById('person-nom').textContent = 'Camera hors ligne';
      });
    }

    update();
    updateHistory();
    updatePerson();
    setInterval(update,        3000);
    setInterval(updateHistory, 5000);
    setInterval(updatePerson,  2000);
  </script>
</body>
</html>"""

# ── FTP Configuration ─────────────────────────────────────────
FTP_HOST = "192.168.68.123"
FTP_PORT = 21
FTP_USER = "younes"
FTP_PASS = "younes"

def upload_ftp(filename, image_data):
    try:
        ftp = ftplib.FTP()
        ftp.connect(FTP_HOST, FTP_PORT, timeout=10)
        ftp.login(FTP_USER, FTP_PASS)
        ftp.storbinary("STOR " + filename, io.BytesIO(image_data))
        ftp.quit()
        return True
    except Exception as e:
        print("Erreur FTP: " + str(e))
        return False

# ── Page Capture ───────────────────────────────────────────────
CAPTURE_HTML = """<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
  <title>Ajouter un visage - Creche Lyra</title>
  <style>
    * { box-sizing: border-box; margin: 0; padding: 0; }
    body { font-family: Arial, sans-serif; background: #0f1923; color: #e8f0fe; min-height: 100vh; display: flex; flex-direction: column; }
    header { background: #0d1520; padding: 16px 20px; display: flex; align-items: center; gap: 12px; box-shadow: 0 2px 8px rgba(0,0,0,0.4); }
    .logo { width: 42px; height: 42px; border-radius: 50%; background: linear-gradient(135deg, #4f9cf9, #34d399); display: flex; align-items: center; justify-content: center; font-weight: 900; font-size: 1.2em; color: white; flex-shrink: 0; }
    header h1 { font-size: 1.05em; color: white; }
    header p  { font-size: 0.75em; color: rgba(255,255,255,0.55); margin-top: 2px; }
    main { flex: 1; padding: 24px 20px; max-width: 480px; margin: 0 auto; width: 100%; }
    .card { background: #1a2740; border: 1px solid #243452; border-radius: 14px; padding: 22px; margin-bottom: 16px; }
    .card-title { font-size: 0.78em; font-weight: 700; letter-spacing: 1px; text-transform: uppercase; color: #7a90b4; margin-bottom: 14px; }
    label { display: block; font-size: 0.85em; color: #7a90b4; margin-bottom: 8px; }
    input[type=text] { width: 100%; padding: 12px 16px; background: #0f1923; border: 1px solid #243452; border-radius: 10px; color: #e8f0fe; font-size: 1em; outline: none; }
    input[type=text]:focus { border-color: #4f9cf9; }
    .btn-camera { display: flex; align-items: center; justify-content: center; gap: 10px; width: 100%; padding: 18px; background: rgba(79,156,249,0.08); border: 2px dashed #4f9cf9; border-radius: 12px; color: #4f9cf9; font-size: 1em; font-weight: 600; cursor: pointer; margin-top: 4px; transition: background 0.2s; }
    .btn-camera:hover { background: rgba(79,156,249,0.15); }
    #preview-wrap { display: none; margin-top: 16px; }
    #preview { width: 100%; max-height: 280px; object-fit: cover; border-radius: 10px; border: 2px solid #34d399; display: block; margin-bottom: 12px; }
    .preview-ok { font-size: 0.82em; color: #34d399; text-align: center; margin-bottom: 14px; }
    .btn-send { width: 100%; padding: 16px; background: linear-gradient(135deg, #059669, #34d399); border: none; border-radius: 12px; color: white; font-size: 1.05em; font-weight: 700; cursor: pointer; }
    .btn-send:disabled { opacity: 0.45; cursor: not-allowed; }
    .btn-retry { width: 100%; padding: 11px; background: transparent; border: 1px solid #4f9cf9; border-radius: 10px; color: #4f9cf9; font-size: 0.9em; cursor: pointer; margin-top: 8px; }
    .alert { padding: 14px 16px; border-radius: 10px; font-size: 0.88em; text-align: center; margin-top: 16px; display: none; }
    .alert.success { background: rgba(52,211,153,0.12); border: 1px solid #34d399; color: #34d399; }
    .alert.error   { background: rgba(248,113,113,0.12); border: 1px solid #f87171; color: #f87171; }
    #file-input { display: none; }
    .back-link { display: inline-flex; align-items: center; gap: 6px; color: #4f9cf9; font-size: 0.85em; text-decoration: none; margin-top: 8px; }
  </style>
</head>
<body>
  <header>
    <div class="logo">L</div>
    <div>
      <h1>Creche Lyra &mdash; Schaerbeek</h1>
      <p>Ajouter un visage connu au systeme</p>
    </div>
  </header>
  <main>
    <div class="card">
      <div class="card-title">Nom de l'enfant</div>
      <label>Prenom (sans accent, en minuscules)</label>
      <input type="text" id="nom" placeholder="ex: emma, lucas, sofia..." autocomplete="off" inputmode="text">
    </div>

    <div class="card">
      <div class="card-title">Photo du visage</div>
      <div class="btn-camera" onclick="document.getElementById('file-input').click()">
        &#128247; &nbsp;<span id="cam-label">Prendre une photo</span>
      </div>
      <input type="file" id="file-input" accept="image/*" capture="user" onchange="previewPhoto(this)">

      <div id="preview-wrap">
        <img id="preview" src="" alt="Apercu">
        <div class="preview-ok">&#10003; Photo prete a etre envoyee</div>
        <button class="btn-send" id="btn-send" onclick="sendPhoto()">
          &#128228; &nbsp;Envoyer sur le serveur
        </button>
        <button class="btn-retry" onclick="document.getElementById('file-input').click()">
          &#8635; Reprendre la photo
        </button>
      </div>
    </div>

    <div class="alert" id="alert-box"></div>
    <a class="back-link" href="/">&#8592; Retour au dashboard</a>
  </main>
  <script>
    function previewPhoto(input) {
      if (!input.files || !input.files[0]) return;
      const reader = new FileReader();
      reader.onload = e => {
        document.getElementById('preview').src = e.target.result;
        document.getElementById('preview-wrap').style.display = 'block';
        document.getElementById('cam-label').textContent = 'Changer la photo';
      };
      reader.readAsDataURL(input.files[0]);
    }
    function showAlert(msg, type) {
      const box = document.getElementById('alert-box');
      box.textContent = msg;
      box.className = 'alert ' + type;
      box.style.display = 'block';
      setTimeout(() => box.style.display = 'none', 6000);
    }
    function sendPhoto() {
      const nom  = document.getElementById('nom').value.trim().toLowerCase();
      const file = document.getElementById('file-input').files[0];
      if (!nom)  { showAlert('Entrez le prenom de l\\'enfant !', 'error');  return; }
      if (!file) { showAlert('Prenez une photo d\\'abord !', 'error'); return; }
      const btn = document.getElementById('btn-send');
      btn.disabled = true;
      btn.textContent = 'Envoi en cours...';
      const formData = new FormData();
      formData.append('photo', file);
      formData.append('nom', nom);
      fetch('/upload_photo', { method: 'POST', body: formData })
        .then(r => r.json())
        .then(d => {
          if (d.success) {
            showAlert('\\u2713 ' + d.message, 'success');
            document.getElementById('preview-wrap').style.display = 'none';
            document.getElementById('nom').value = '';
            document.getElementById('file-input').value = '';
            document.getElementById('cam-label').textContent = 'Prendre une photo';
          } else {
            showAlert('\\u2717 ' + d.message, 'error');
          }
        })
        .catch(() => showAlert('Erreur reseau', 'error'))
        .finally(() => {
          btn.disabled = false;
          btn.textContent = '\\u{1F4E8} Envoyer sur le serveur';
        });
    }
  </script>
</body>
</html>"""

# ── Flask App ──────────────────────────────────────────────────
app = Flask(__name__)

@app.route('/')
def index():
    return render_template_string(HTML)

@app.route('/api/data')
def api_data():
    return jsonify(data)

@app.route('/api/history')
def api_history():
    return jsonify({k: list(v) for k, v in history.items()})

_last_reco_nom = {"nom": None}  # Évite de logguer la même personne en boucle

@app.route('/api/person')
def api_person():
    try:
        url = 'http://192.168.68.123:8080/api/person'
        with urllib.request.urlopen(url, timeout=2) as r:
            p = json.loads(r.read().decode())
        # Sauvegarde en base seulement si le nom change
        nom = p.get("nom", "")
        if nom and nom != _last_reco_nom["nom"] and nom not in ("Camera indisponible", "Inconnu", ""):
            db_save_reconnaissance(nom, p.get("nb_visages", 0), p.get("alerte", False))
            _last_reco_nom["nom"] = nom
        return jsonify(p)
    except:
        return jsonify({"nom": "Camera indisponible", "alerte": False, "nb_visages": 0, "timestamp": ""})

@app.route('/capture')
def capture_page():
    return render_template_string(CAPTURE_HTML)

@app.route('/upload_photo', methods=['POST'])
def upload_photo():
    if 'photo' not in request.files:
        return jsonify({"success": False, "message": "Pas de photo recue"})
    file = request.files['photo']
    nom  = request.form.get('nom', '').strip().lower()
    if not nom:
        return jsonify({"success": False, "message": "Nom vide"})
    filename   = nom + '.jpg'
    image_data = file.read()
    if upload_ftp(filename, image_data):
        return jsonify({"success": True,  "message": "Photo de " + nom + " envoyee ! Le systeme va la reconnaitre dans quelques secondes."})
    else:
        return jsonify({"success": False, "message": "Erreur FTP - verifier la connexion avec Pi 2"})

@app.route('/api/historique')
def api_historique():
    periode = request.args.get('periode', '24h')
    mapping = {'1h': 1, '6h': 6, '24h': 24, '7j': 168}
    heures  = mapping.get(periode, 24)
    try:
        conn = sqlite3.connect(DB_PATH)
        conn.row_factory = sqlite3.Row
        rows = conn.execute("""
            SELECT timestamp, heart_rate, breath_rate, bpm_optique, spo2
            FROM mesures
            WHERE timestamp >= datetime('now','localtime', ? || ' hours')
            ORDER BY timestamp ASC
        """, (f"-{heures}",)).fetchall()
        conn.close()
        return jsonify({
            "timestamps":   [r["timestamp"][-8:] for r in rows],
            "heart_rate":   [r["heart_rate"]  for r in rows],
            "breath_rate":  [r["breath_rate"] for r in rows],
            "bpm_optique":  [r["bpm_optique"] for r in rows],
            "spo2":         [r["spo2"]        for r in rows],
        })
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/api/alertes')
def api_alertes():
    limite = int(request.args.get('limite', 50))
    try:
        conn = sqlite3.connect(DB_PATH)
        conn.row_factory = sqlite3.Row
        rows = conn.execute("""
            SELECT timestamp, type, parametre, valeur, message
            FROM alertes ORDER BY id DESC LIMIT ?
        """, (limite,)).fetchall()
        conn.close()
        return jsonify([dict(r) for r in rows])
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/api/reconnaissances')
def api_reconnaissances():
    limite = int(request.args.get('limite', 30))
    try:
        conn = sqlite3.connect(DB_PATH)
        conn.row_factory = sqlite3.Row
        rows = conn.execute("""
            SELECT timestamp, nom, nb_visages, alerte
            FROM reconnaissances ORDER BY id DESC LIMIT ?
        """, (limite,)).fetchall()
        conn.close()
        return jsonify([dict(r) for r in rows])
    except Exception as e:
        return jsonify({"error": str(e)}), 500

@app.route('/api/stats')
def api_stats():
    try:
        conn = sqlite3.connect(DB_PATH)
        c = conn.cursor()
        total_mesures   = c.execute("SELECT COUNT(*) FROM mesures").fetchone()[0]
        total_alertes   = c.execute("SELECT COUNT(*) FROM alertes").fetchone()[0]
        alertes_crit    = c.execute("SELECT COUNT(*) FROM alertes WHERE type='critique'").fetchone()[0]
        alertes_warn    = c.execute("SELECT COUNT(*) FROM alertes WHERE type='warning'").fetchone()[0]
        spo2_moy        = c.execute("SELECT AVG(spo2) FROM mesures WHERE spo2 > 0").fetchone()[0]
        bpm_moy         = c.execute("SELECT AVG(heart_rate) FROM mesures WHERE heart_rate > 0").fetchone()[0]
        premiere        = c.execute("SELECT MIN(timestamp) FROM mesures").fetchone()[0]
        conn.close()
        return jsonify({
            "total_mesures":  total_mesures,
            "total_alertes":  total_alertes,
            "alertes_critique": alertes_crit,
            "alertes_warning":  alertes_warn,
            "spo2_moyenne":   round(spo2_moy, 1) if spo2_moy else 0,
            "bpm_moyen":      round(bpm_moy, 1)  if bpm_moy  else 0,
            "premiere_mesure": premiere or "--"
        })
    except Exception as e:
        return jsonify({"error": str(e)}), 500

# ── Page Historique long terme ─────────────────────────────────────
HISTORIQUE_HTML = """<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Historique - Creche Lyra</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
  <style>
    :root { --bg:#0f1923;--card:#1a2740;--border:#243452;--text:#e8f0fe;--muted:#7a90b4;--accent:#4f9cf9;--accent2:#34d399;--header:#0d1520; }
    * { box-sizing:border-box; margin:0; padding:0; }
    body { font-family:'Segoe UI',Arial,sans-serif; background:var(--bg); color:var(--text); min-height:100vh; }
    header { background:var(--header); padding:14px 24px; display:flex; align-items:center; justify-content:space-between; box-shadow:0 2px 10px rgba(0,0,0,0.4); }
    .logo { width:40px;height:40px;border-radius:50%;background:linear-gradient(135deg,#4f9cf9,#34d399);display:flex;align-items:center;justify-content:center;font-weight:900;color:white;font-size:1.2em;flex-shrink:0; }
    .header-info h1 { color:white; font-size:1.1em; }
    .header-info p  { color:rgba(255,255,255,0.6); font-size:0.78em; }
    a.back { color:var(--accent); font-size:0.85em; text-decoration:none; }
    main { padding:20px 24px; max-width:1400px; margin:0 auto; }
    .section-title { font-size:0.72em; font-weight:700; letter-spacing:1.5px; text-transform:uppercase; color:var(--muted); margin:24px 0 12px; border-left:3px solid var(--accent); padding-left:8px; }

    /* Stats cards */
    .stats-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(160px,1fr)); gap:12px; margin-bottom:8px; }
    .stat-card { background:var(--card); border:1px solid var(--border); border-radius:12px; padding:16px; text-align:center; }
    .stat-card .sv { font-size:1.8em; font-weight:700; color:var(--accent); }
    .stat-card .sl { font-size:0.75em; color:var(--muted); margin-top:4px; }
    .stat-card.crit .sv { color:#f87171; }
    .stat-card.warn .sv { color:#f59e0b; }
    .stat-card.ok   .sv { color:#34d399; }

    /* Periode */
    .periode-bar { display:flex; gap:8px; margin-bottom:16px; flex-wrap:wrap; }
    .btn-periode { padding:8px 18px; border:1px solid var(--border); border-radius:20px; background:var(--card); color:var(--muted); cursor:pointer; font-size:0.85em; transition:all 0.2s; }
    .btn-periode.active, .btn-periode:hover { background:var(--accent); color:white; border-color:var(--accent); }

    /* Charts */
    .charts-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(480px,1fr)); gap:16px; }
    .chart-card { background:var(--card); border:1px solid var(--border); border-radius:14px; padding:18px; }
    .chart-title { font-size:0.8em; font-weight:600; color:var(--muted); margin-bottom:12px; }
    .chart-wrap { position:relative; height:200px; }

    /* Alertes table */
    .alerts-table { width:100%; border-collapse:collapse; font-size:0.85em; }
    .alerts-table th { background:#162032; color:var(--muted); padding:10px 12px; text-align:left; font-weight:600; border-bottom:1px solid var(--border); }
    .alerts-table td { padding:9px 12px; border-bottom:1px solid rgba(36,52,82,0.5); }
    .alerts-table tr:hover td { background:rgba(79,156,249,0.04); }
    .badge { display:inline-block; padding:2px 8px; border-radius:10px; font-size:0.75em; font-weight:700; }
    .badge.critique { background:rgba(248,113,113,0.15); color:#f87171; border:1px solid #f87171; }
    .badge.warning  { background:rgba(245,158,11,0.15);  color:#f59e0b; border:1px solid #f59e0b; }
    .table-wrap { background:var(--card); border:1px solid var(--border); border-radius:14px; overflow:hidden; }
    .table-scroll { max-height:360px; overflow-y:auto; }

    /* Reconnaissances */
    .reco-grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:10px; }
    .reco-card { background:var(--card); border:1px solid var(--border); border-radius:12px; padding:14px 16px; }
    .reco-nom  { font-size:1.1em; font-weight:700; color:var(--accent2); margin-bottom:4px; }
    .reco-card.alerte .reco-nom { color:#f87171; }
    .reco-ts   { font-size:0.75em; color:var(--muted); }

    footer { text-align:center; padding:20px; font-size:0.72em; color:var(--muted); border-top:1px solid var(--border); margin-top:32px; }
  </style>
</head>
<body>
  <header>
    <div style="display:flex;align-items:center;gap:14px;">
      <div class="logo">L</div>
      <div class="header-info">
        <h1>Creche Lyra &mdash; Historique</h1>
        <p>Base de donnees SQLite &bull; Surveillance long terme</p>
      </div>
    </div>
    <a class="back" href="/">&#8592; Retour au dashboard</a>
  </header>

  <main>

    <!-- Stats globales -->
    <div class="section-title">Statistiques globales</div>
    <div class="stats-grid">
      <div class="stat-card ok">
        <div class="sv" id="st-mesures">--</div>
        <div class="sl">Mesures enregistrees</div>
      </div>
      <div class="stat-card ok">
        <div class="sv" id="st-spo2">--</div>
        <div class="sl">SpO2 moyenne (%)</div>
      </div>
      <div class="stat-card ok">
        <div class="sv" id="st-bpm">--</div>
        <div class="sl">BPM moyen</div>
      </div>
      <div class="stat-card warn">
        <div class="sv" id="st-warn">--</div>
        <div class="sl">Avertissements</div>
      </div>
      <div class="stat-card crit">
        <div class="sv" id="st-crit">--</div>
        <div class="sl">Alertes critiques</div>
      </div>
      <div class="stat-card">
        <div class="sv" id="st-depuis" style="font-size:0.95em;">--</div>
        <div class="sl">Premiere mesure</div>
      </div>
    </div>

    <!-- Graphiques historique -->
    <div class="section-title">Graphiques historiques</div>
    <div class="periode-bar">
      <button class="btn-periode" onclick="loadHistorique('1h',this)">1 heure</button>
      <button class="btn-periode active" onclick="loadHistorique('24h',this)">24 heures</button>
      <button class="btn-periode" onclick="loadHistorique('7j',this)">7 jours</button>
    </div>
    <div class="charts-grid">
      <div class="chart-card">
        <div class="chart-title">&#129754; Respiration (cycles/min)</div>
        <div class="chart-wrap"><canvas id="cBreath"></canvas></div>
      </div>
      <div class="chart-card">
        <div class="chart-title">&#10084; Frequence cardiaque radar (BPM)</div>
        <div class="chart-wrap"><canvas id="cHeart"></canvas></div>
      </div>
      <div class="chart-card">
        <div class="chart-title">&#128147; BPM optique MAX30102</div>
        <div class="chart-wrap"><canvas id="cBpm"></canvas></div>
      </div>
      <div class="chart-card">
        <div class="chart-title">&#129978; SpO2 (%)</div>
        <div class="chart-wrap"><canvas id="cSpo2"></canvas></div>
      </div>
    </div>

    <!-- Alertes -->
    <div class="section-title">Journal des alertes</div>
    <div class="table-wrap">
      <div class="table-scroll">
        <table class="alerts-table">
          <thead><tr><th>Date/Heure</th><th>Type</th><th>Parametre</th><th>Valeur</th><th>Message</th></tr></thead>
          <tbody id="alerts-body"><tr><td colspan="5" style="text-align:center;color:var(--muted);padding:20px;">Chargement...</td></tr></tbody>
        </table>
      </div>
    </div>

    <!-- Reconnaissances -->
    <div class="section-title">Dernières reconnaissances faciales</div>
    <div class="reco-grid" id="reco-grid">
      <div style="color:var(--muted);font-size:0.85em;">Chargement...</div>
    </div>

  </main>
  <footer>Creche Lyra &bull; Schaerbeek, Bruxelles &bull; Base de donnees SQLite</footer>

  <script>
    const chartOpts = {
      responsive:true, maintainAspectRatio:false,
      animation:{duration:300},
      plugins:{legend:{display:false}},
      scales:{
        x:{ticks:{color:'#7a90b4',font:{size:9},maxTicksLimit:8}, grid:{color:'rgba(255,255,255,0.04)'}},
        y:{ticks:{color:'#7a90b4',font:{size:9}},               grid:{color:'rgba(255,255,255,0.04)'}}
      },
      elements:{point:{radius:1},line:{tension:0.4,borderWidth:2}}
    };

    function makeChart(id, color) {
      return new Chart(document.getElementById(id).getContext('2d'), {
        type:'line',
        data:{ labels:[], datasets:[{ data:[], borderColor:color,
          backgroundColor:color.replace('rgb','rgba').replace(')',',0.07)'), fill:true }] },
        options: JSON.parse(JSON.stringify(chartOpts))
      });
    }

    const charts = {
      breath: makeChart('cBreath','rgb(52,211,153)'),
      heart:  makeChart('cHeart', 'rgb(248,113,113)'),
      bpm:    makeChart('cBpm',   'rgb(251,191,36)'),
      spo2:   makeChart('cSpo2',  'rgb(79,156,249)'),
    };

    function loadHistorique(periode, btn) {
      document.querySelectorAll('.btn-periode').forEach(b => b.classList.remove('active'));
      if(btn) btn.classList.add('active');
      fetch('/api/historique?periode='+periode).then(r=>r.json()).then(h => {
        const labels = h.timestamps || [];
        const step   = labels.length > 200 ? Math.ceil(labels.length/200) : 1;
        const filter = (arr) => arr.filter((_,i) => i%step===0);
        charts.breath.data.labels              = filter(labels);
        charts.breath.data.datasets[0].data   = filter(h.breath_rate);
        charts.heart.data.labels               = filter(labels);
        charts.heart.data.datasets[0].data    = filter(h.heart_rate);
        charts.bpm.data.labels                 = filter(labels);
        charts.bpm.data.datasets[0].data      = filter(h.bpm_optique);
        charts.spo2.data.labels                = filter(labels);
        charts.spo2.data.datasets[0].data     = filter(h.spo2);
        Object.values(charts).forEach(c => c.update('none'));
      });
    }

    function loadStats() {
      fetch('/api/stats').then(r=>r.json()).then(s => {
        document.getElementById('st-mesures').textContent = s.total_mesures  || 0;
        document.getElementById('st-spo2').textContent    = s.spo2_moyenne   || '--';
        document.getElementById('st-bpm').textContent     = s.bpm_moyen      || '--';
        document.getElementById('st-warn').textContent    = s.alertes_warning   || 0;
        document.getElementById('st-crit').textContent    = s.alertes_critique  || 0;
        document.getElementById('st-depuis').textContent  = s.premiere_mesure   || '--';
      });
    }

    function loadAlertes() {
      fetch('/api/alertes?limite=50').then(r=>r.json()).then(rows => {
        const tbody = document.getElementById('alerts-body');
        if(!rows.length) {
          tbody.innerHTML = '<tr><td colspan="5" style="text-align:center;color:var(--muted);padding:20px;">Aucune alerte enregistree</td></tr>';
          return;
        }
        tbody.innerHTML = rows.map(r => `
          <tr>
            <td>${r.timestamp}</td>
            <td><span class="badge ${r.type}">${r.type.toUpperCase()}</span></td>
            <td>${r.parametre}</td>
            <td>${r.valeur !== null ? parseFloat(r.valeur).toFixed(1) : '--'}</td>
            <td>${r.message}</td>
          </tr>`).join('');
      });
    }

    function loadReconnaissances() {
      fetch('/api/reconnaissances?limite=30').then(r=>r.json()).then(rows => {
        const grid = document.getElementById('reco-grid');
        if(!rows.length) {
          grid.innerHTML = '<div style="color:var(--muted);font-size:0.85em;">Aucune reconnaissance enregistree</div>';
          return;
        }
        grid.innerHTML = rows.map(r => `
          <div class="reco-card ${r.alerte ? 'alerte' : ''}">
            <div class="reco-nom">${r.nom || 'Inconnu'}</div>
            <div class="reco-ts">${r.timestamp}</div>
            <div style="font-size:0.75em;color:var(--muted);margin-top:4px;">${r.nb_visages} visage(s) detecte(s)</div>
          </div>`).join('');
      });
    }

    // Chargement initial
    loadStats();
    loadHistorique('24h', null);
    loadAlertes();
    loadReconnaissances();

    // Rafraichissement auto
    setInterval(loadStats, 30000);
    setInterval(loadAlertes, 15000);
    setInterval(loadReconnaissances, 10000);
  </script>
</body>
</html>"""

@app.route('/historique')
def historique_page():
    return render_template_string(HISTORIQUE_HTML)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=False)
