"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import dynamic from "next/dynamic";
import type { Pt, Geofence } from "./MapView";
import { useCurrentUser } from "../user-context";

const MapView = dynamic(() => import("./MapView"), {
  ssr: false,
  loading: () => (
    <div className="flex h-[60vh] w-full items-center justify-center rounded-2xl border border-zinc-200 text-sm text-zinc-400 dark:border-zinc-800">
      Chargement de la carte…
    </div>
  ),
});

interface Device {
  id: string;
  name: string;
  lastSeenAt: string | null;
  createdAt: string;
  lastPoint: { lat: number; lng: number; battery: number | null; recordedAt: string } | null;
  geofence: { refLat: number; refLng: number; radiusM: number; enabled: boolean } | null;
}

type Range = "today" | "24h" | "7d";

const RANGES: { key: Range; label: string }[] = [
  { key: "today", label: "Aujourd'hui" },
  { key: "24h", label: "24 h" },
  { key: "7d", label: "7 jours" },
];

function rangeToFrom(r: Range): Date {
  const now = Date.now();
  if (r === "today") {
    const d = new Date();
    d.setHours(0, 0, 0, 0);
    return d;
  }
  if (r === "7d") return new Date(now - 7 * 24 * 3600 * 1000);
  return new Date(now - 24 * 3600 * 1000);
}

const ORIGIN = typeof window !== "undefined" ? window.location.origin : "https://leperelion.fr";
const PAGE_SIZE = 50;
// Icônes proposées pour le message d'alerte (insérables au curseur).
const EMOJIS = ["⚠️", "🚨", "❗", "🔔", "📍", "🏠", "🚗", "🛵", "🚶", "🏃", "✅", "❌", "🕒", "🔋", "🛰️", "📌", "➡️", "👀", "📞", "😀"];

// Insère du texte à la position du curseur d'un textarea, puis replace le curseur.
function insertAtCursor(
  ref: { current: HTMLTextAreaElement | null },
  current: string,
  apply: (next: string) => void,
  value: string
) {
  const ta = ref.current;
  const start = ta?.selectionStart ?? current.length;
  const end = ta?.selectionEnd ?? current.length;
  apply(current.slice(0, start) + value + current.slice(end));
  requestAnimationFrame(() => {
    if (ta) {
      ta.focus();
      const pos = start + value.length;
      ta.setSelectionRange(pos, pos);
    }
  });
}
const todayStr = () => {
  const d = new Date();
  const p = (n: number) => String(n).padStart(2, "0");
  return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}`;
};

const histFmt = new Intl.DateTimeFormat("fr-FR", {
  weekday: "short",
  day: "numeric",
  month: "short",
  hour: "2-digit",
  minute: "2-digit",
});
function osmLink(p: { lat: number; lng: number }) {
  return `https://www.openstreetmap.org/?mlat=${p.lat}&mlon=${p.lng}#map=17/${p.lat}/${p.lng}`;
}

export default function TrackingPage() {
  const me = useCurrentUser();
  const canManage = me.role === "super_admin";
  const [devices, setDevices] = useState<Device[]>([]);
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [points, setPoints] = useState<Pt[]>([]);
  const [range, setRange] = useState<Range>("24h");
  const [day, setDay] = useState<string>(""); // filtre "jour précis" (YYYY-MM-DD), vide = plages
  const [page, setPage] = useState(1);
  const [focus, setFocus] = useState<Pt | null>(null);
  const [gf, setGf] = useState({ refLat: "", refLng: "", radiusM: "50", notifyNumber: "", message: "", enabled: true });
  const [gfPick, setGfPick] = useState(false);
  const [gfMsg, setGfMsg] = useState<string | null>(null);
  const [cmdMsg, setCmdMsg] = useState<string | null>(null);

  async function locateNow() {
    setCmdMsg(null);
    const res = await fetch(`/backend/track/devices/${selectedId}/locate`, { method: "POST", credentials: "include" });
    setCmdMsg(res.ok ? "📍 Demande envoyée — la position arrivera au prochain contact du téléphone." : "Échec de la demande.");
  }
  async function syncWaypoints() {
    const res = await fetch(`/backend/track/devices/${selectedId}/sync-waypoints`, { method: "POST", credentials: "include" });
    setGfMsg(res.ok ? "Zone envoyée au téléphone (appliquée au prochain contact)." : "Échec de l'envoi.");
  }
  const [gfTestMsg, setGfTestMsg] = useState<string | null>(null);
  const [gfSnapshot, setGfSnapshot] = useState<string | null>(null);
  const [showEmoji, setShowEmoji] = useState(false);
  const messageRef = useRef<HTMLTextAreaElement>(null);

  function insertEmoji(emoji: string) {
    insertAtCursor(messageRef, gf.message, (next) => setGf((s) => ({ ...s, message: next })), emoji);
  }

  // ── Alerte batterie faible ──
  const [batt, setBatt] = useState({ threshold: "20", notifyNumber: "", message: "", enabled: true });
  const [battMsg, setBattMsg] = useState<string | null>(null);
  const [battTestMsg, setBattTestMsg] = useState<string | null>(null);
  const [battShowEmoji, setBattShowEmoji] = useState(false);
  const battMsgRef = useRef<HTMLTextAreaElement>(null);

  function insertBattEmoji(emoji: string) {
    insertAtCursor(battMsgRef, batt.message, (next) => setBatt((s) => ({ ...s, message: next })), emoji);
  }

  useEffect(() => {
    if (!canManage || !selectedId) return;
    setBattMsg(null);
    setBattTestMsg(null);
    fetch(`/backend/track/devices/${selectedId}/battery-alert`, { credentials: "include" })
      .then((r) => (r.ok ? r.json() : null))
      .then((d) => {
        const b = d?.batteryAlert;
        const form = b
          ? { threshold: String(b.threshold), notifyNumber: b.notifyNumber, message: b.message ?? "", enabled: b.enabled }
          : { threshold: "20", notifyNumber: "", message: "", enabled: true };
        setBatt(form);
      })
      .catch(() => {});
  }, [canManage, selectedId]);

  async function saveBattery() {
    setBattMsg(null);
    if (!batt.notifyNumber) return setBattMsg("Choisissez une conversation cible.");
    const res = await fetch(`/backend/track/devices/${selectedId}/battery-alert`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify({
        threshold: Number(batt.threshold) || 20,
        notifyNumber: batt.notifyNumber,
        message: batt.message.trim() || undefined,
        enabled: batt.enabled,
      }),
    });
    setBattMsg(res.ok ? "✅ Alerte batterie enregistrée." : "Échec de l'enregistrement.");
  }
  async function testBattery() {
    setBattTestMsg(null);
    if (!batt.notifyNumber) return setBattTestMsg("Choisissez une conversation.");
    const res = await fetch(`/backend/track/devices/${selectedId}/battery-alert/test`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify({ notifyNumber: batt.notifyNumber, message: batt.message.trim() || undefined, threshold: Number(batt.threshold) || 20 }),
    });
    setBattTestMsg(res.ok ? "✅ Test envoyé." : "Échec de l'envoi (WhatsApp connecté ?).");
  }
  async function deleteBattery() {
    if (!window.confirm("Supprimer l'alerte batterie ?")) return;
    await fetch(`/backend/track/devices/${selectedId}/battery-alert`, { method: "DELETE", credentials: "include" });
    setBatt({ threshold: "20", notifyNumber: "", message: "", enabled: true });
    setBattMsg("Alerte supprimée.");
  }
  const [waChats, setWaChats] = useState<{ jid: string; name: string }[]>([]);
  const [loading, setLoading] = useState(true);
  const [loadingPoints, setLoadingPoints] = useState(false);
  const [tokens, setTokens] = useState<Record<string, string>>({});

  const loadDevices = useCallback(async () => {
    const res = await fetch("/backend/track/devices", { credentials: "include" });
    if (!res.ok) return;
    const data = await res.json();
    setDevices(data.devices);
    setSelectedId((cur) => cur ?? data.devices[0]?.id ?? null);
  }, []);

  useEffect(() => {
    loadDevices().finally(() => setLoading(false));
  }, [loadDevices]);

  const loadPoints = useCallback(async () => {
    if (!selectedId) {
      setPoints([]);
      return;
    }
    setLoadingPoints(true);
    try {
      let fromD: Date;
      let toD: Date;
      if (day) {
        fromD = new Date(`${day}T00:00:00`);
        toD = new Date(fromD.getTime() + 24 * 3600 * 1000);
      } else {
        fromD = rangeToFrom(range);
        toD = new Date();
      }
      const res = await fetch(
        `/backend/track/devices/${selectedId}/points?from=${encodeURIComponent(fromD.toISOString())}&to=${encodeURIComponent(toD.toISOString())}&limit=5000`,
        { credentials: "include" }
      );
      if (res.ok) {
        const data = await res.json();
        setPoints(data.points);
      }
    } finally {
      setLoadingPoints(false);
    }
  }, [selectedId, range, day]);

  useEffect(() => {
    loadPoints();
  }, [loadPoints]);

  // Réinitialise la mise en évidence et la pagination quand on change d'appareil / plage / jour.
  useEffect(() => {
    setFocus(null);
    setPage(1);
  }, [selectedId, range, day]);

  // Évite de rester sur une page hors limites après un rafraîchissement.
  useEffect(() => {
    setPage((p) => Math.min(p, Math.max(1, Math.ceil([...points].length / PAGE_SIZE))));
  }, [points]);

  // Rafraîchissement automatique toutes les 30 s
  useEffect(() => {
    const t = setInterval(() => {
      loadPoints();
      loadDevices();
    }, 30000);
    return () => clearInterval(t);
  }, [loadPoints, loadDevices]);

  async function createDevice() {
    const name = window.prompt("Nom de l'appareil (ex. « iPhone de Guillaume ») :");
    if (!name) return;
    const res = await fetch("/backend/track/devices", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify({ name }),
    });
    if (res.ok) {
      const data = await res.json();
      setTokens((t) => ({ ...t, [data.device.id]: data.device.pairingToken }));
      await loadDevices();
      setSelectedId(data.device.id);
    }
  }

  async function showToken(id: string) {
    const res = await fetch(`/backend/track/devices/${id}/token`, { credentials: "include" });
    if (res.ok) {
      const data = await res.json();
      setTokens((t) => ({ ...t, [id]: data.pairingToken }));
    }
  }

  async function regenerate(id: string) {
    if (!window.confirm("Régénérer le jeton ? L'ancienne URL ne fonctionnera plus.")) return;
    const res = await fetch(`/backend/track/devices/${id}/regenerate`, { method: "POST", credentials: "include" });
    if (res.ok) {
      const data = await res.json();
      setTokens((t) => ({ ...t, [id]: data.pairingToken }));
    }
  }

  async function removeDevice(id: string) {
    if (!window.confirm("Supprimer cet appareil et tout son historique ?")) return;
    const res = await fetch(`/backend/track/devices/${id}`, { method: "DELETE", credentials: "include" });
    if (res.ok) {
      if (selectedId === id) setSelectedId(null);
      await loadDevices();
    }
  }

  const selected = useMemo(() => devices.find((d) => d.id === selectedId) ?? null, [devices, selectedId]);

  // Charge la config d'alerte de zone de l'appareil (Super Admin).
  useEffect(() => {
    if (!canManage || !selectedId) return;
    setGfMsg(null);
    setGfPick(false);
    fetch(`/backend/track/devices/${selectedId}/geofence`, { credentials: "include" })
      .then((r) => (r.ok ? r.json() : null))
      .then((d) => {
        const g = d?.geofence;
        const form = g
          ? { refLat: String(g.refLat), refLng: String(g.refLng), radiusM: String(g.radiusM), notifyNumber: g.notifyNumber, message: g.message ?? "", enabled: g.enabled }
          : { refLat: "", refLng: "", radiusM: "50", notifyNumber: "", message: "", enabled: true };
        setGf(form);
        setGfSnapshot(JSON.stringify(form)); // référence "enregistrée" pour détecter les modifications
      })
      .catch(() => {});
  }, [canManage, selectedId]);

  // Conversations WhatsApp (pour choisir la cible de l'alerte).
  useEffect(() => {
    if (!canManage) return;
    fetch("/backend/whatsapp/chats", { credentials: "include" })
      .then((r) => (r.ok ? r.json() : null))
      .then((d) => { if (d?.chats) setWaChats(d.chats); })
      .catch(() => {});
  }, [canManage]);

  const gfForMap: Geofence | null = gf.refLat && gf.refLng ? { lat: Number(gf.refLat), lng: Number(gf.refLng), radiusM: Number(gf.radiusM) || 50 } : null;
  const gfDirty = gfSnapshot !== null && gfSnapshot !== JSON.stringify(gf);
  // Zone affichée sur la carte : le formulaire (live) pour le super_admin ; sinon la zone
  // enregistrée de l'appareil (lecture seule) — visible aussi par les admins.
  const mapGeo: Geofence | null = canManage
    ? gfForMap
    : selected?.geofence
      ? { lat: selected.geofence.refLat, lng: selected.geofence.refLng, radiusM: selected.geofence.radiusM }
      : null;

  async function saveGeofence() {
    setGfMsg(null);
    if (!gf.refLat || !gf.refLng) return setGfMsg("Définissez d'abord un point de référence.");
    if (!gf.notifyNumber.trim()) return setGfMsg("Indiquez un numéro WhatsApp à alerter.");
    const res = await fetch(`/backend/track/devices/${selectedId}/geofence`, {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify({
        refLat: Number(gf.refLat),
        refLng: Number(gf.refLng),
        radiusM: Number(gf.radiusM) || 50,
        notifyNumber: gf.notifyNumber.trim(),
        message: gf.message.trim() || undefined,
        enabled: gf.enabled,
      }),
    });
    if (res.ok) setGfSnapshot(JSON.stringify(gf));
    setGfMsg(res.ok ? "✅ Alerte enregistrée." : "Échec de l'enregistrement.");
  }
  async function testGeofence() {
    setGfTestMsg(null);
    if (!gf.notifyNumber) return setGfTestMsg("Choisissez d'abord une conversation.");
    const res = await fetch(`/backend/track/devices/${selectedId}/geofence/test`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      credentials: "include",
      body: JSON.stringify({ notifyNumber: gf.notifyNumber, message: gf.message.trim() || undefined, radiusM: Number(gf.radiusM) || 50 }),
    });
    setGfTestMsg(res.ok ? "✅ Message de test envoyé." : "Échec de l'envoi (WhatsApp connecté ?).");
  }
  async function deleteGeofence() {
    if (!window.confirm("Supprimer l'alerte de zone ?")) return;
    await fetch(`/backend/track/devices/${selectedId}/geofence`, { method: "DELETE", credentials: "include" });
    setGf({ refLat: "", refLng: "", radiusM: "50", notifyNumber: "", message: "", enabled: true });
    setGfMsg("Alerte supprimée.");
  }

  // Historique : plus récent d'abord, paginé.
  const ordered = useMemo(() => [...points].reverse(), [points]);
  const totalPages = Math.max(1, Math.ceil(ordered.length / PAGE_SIZE));
  const pageItems = useMemo(() => ordered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE), [ordered, page]);

  if (loading) return <div className="text-sm text-zinc-500">Chargement…</div>;

  return (
    <div className="mx-auto w-full max-w-4xl">
      <div className="mb-4 flex flex-wrap items-center justify-between gap-2">
        <h1 className="text-xl font-semibold text-black sm:text-2xl dark:text-zinc-50">Localisation</h1>
        {canManage && (
          <button onClick={createDevice} className="rounded-lg bg-black px-3 py-2 text-sm font-medium text-white hover:bg-zinc-800 dark:bg-white dark:text-black dark:hover:bg-zinc-200">
            + Appareil
          </button>
        )}
      </div>

      {devices.length === 0 ? (
        <div className="rounded-2xl border border-zinc-200 bg-white p-6 text-sm text-zinc-600 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-300">
          <p className="mb-3 font-medium text-black dark:text-zinc-50">Aucun appareil suivi.</p>
          {canManage ? (
            <p>Cliquez sur <strong>+ Appareil</strong> pour en créer un, puis suivez les instructions de
            configuration d&apos;OwnTracks affichées.</p>
          ) : (
            <p>Aucun appareil n&apos;a encore été configuré par l&apos;administrateur.</p>
          )}
        </div>
      ) : (
        <>
          {/* Sélecteur d'appareil */}
          {devices.length > 1 && (
            <div className="mb-3 flex flex-wrap gap-2">
              {devices.map((d) => (
                <button
                  key={d.id}
                  onClick={() => setSelectedId(d.id)}
                  className={
                    "rounded-lg border px-3 py-1.5 text-sm " +
                    (d.id === selectedId
                      ? "border-black bg-black text-white dark:border-white dark:bg-white dark:text-black"
                      : "border-zinc-300 dark:border-zinc-700")
                  }
                >
                  {d.name}
                </button>
              ))}
            </div>
          )}

          {/* Plage temporelle + filtre par jour */}
          <div className="mb-3 flex flex-wrap items-center justify-between gap-2">
            <div className="flex flex-wrap items-center gap-2">
              <div className="inline-flex rounded-lg border border-zinc-300 p-0.5 dark:border-zinc-700">
                {RANGES.map((r) => (
                  <button
                    key={r.key}
                    onClick={() => { setRange(r.key); setDay(""); }}
                    className={
                      "rounded-md px-3 py-1.5 text-sm font-medium " +
                      (!day && range === r.key ? "bg-black text-white dark:bg-white dark:text-black" : "text-zinc-600 dark:text-zinc-400")
                    }
                  >
                    {r.label}
                  </button>
                ))}
              </div>
              <input
                type="date"
                value={day}
                max={todayStr()}
                onChange={(e) => setDay(e.target.value)}
                className={
                  "rounded-lg border px-3 py-1.5 text-sm dark:bg-black " +
                  (day ? "border-black dark:border-white" : "border-zinc-300 dark:border-zinc-700")
                }
                title="Filtrer sur un jour précis"
              />
              {day && (
                <button onClick={() => setDay("")} className="rounded-lg border border-zinc-300 px-2 py-1.5 text-sm hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-900">
                  ✕ jour
                </button>
              )}
            </div>
            <div className="flex items-center gap-2">
              {canManage && (
                <button onClick={locateNow} className="rounded-lg border border-blue-300 px-3 py-1.5 text-sm font-medium text-blue-700 hover:bg-blue-50 dark:border-blue-900 dark:text-blue-400 dark:hover:bg-blue-950">
                  📍 Localiser maintenant
                </button>
              )}
              <button onClick={() => { loadPoints(); loadDevices(); }} className="rounded-lg border border-zinc-300 px-3 py-1.5 text-sm hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-900">
                ↻ Rafraîchir
              </button>
            </div>
          </div>
          {cmdMsg && <p className="mb-3 text-sm text-blue-700 dark:text-blue-400">{cmdMsg}</p>}

          {/* Carte (trace complète + marqueurs de la page courante + zone d'alerte) */}
          {gfPick && (
            <p className="mb-2 rounded-lg bg-green-50 px-3 py-2 text-sm text-green-700 dark:bg-green-950 dark:text-green-300">
              📍 Cliquez sur la carte pour définir le point de référence.
            </p>
          )}
          <MapView
            points={points}
            markers={pageItems}
            focus={focus}
            geofence={mapGeo}
            pickMode={gfPick}
            onPick={(lat, lng) => {
              setGf((s) => ({ ...s, refLat: lat.toFixed(6), refLng: lng.toFixed(6) }));
              setGfPick(false);
            }}
          />

          {/* Infos dernière position */}
          <div className="mt-3 text-sm text-zinc-500">
            {loadingPoints
              ? "Mise à jour…"
              : points.length === 0
                ? "Aucune position reçue sur cette période."
                : `${points.length} position(s) · dernière : ${new Date(points[points.length - 1].recordedAt).toLocaleString("fr-FR")}` +
                  (selected?.lastPoint?.battery != null ? ` · 🔋 ${selected.lastPoint.battery}%` : "")}
          </div>

          {/* Historique des positions (plus récent d'abord) */}
          {points.length > 0 && (
            <details className="mt-4 rounded-2xl border border-zinc-200 bg-white dark:border-zinc-800 dark:bg-zinc-950" open>
              <summary className="cursor-pointer px-4 py-3 text-sm font-medium text-black dark:text-zinc-50">
                Historique des positions ({points.length})
              </summary>

              {/* Pagination */}
              {totalPages > 1 && (
                <div className="flex items-center justify-between gap-2 border-t border-zinc-100 px-4 py-2 text-sm dark:border-zinc-900">
                  <button
                    onClick={() => setPage((p) => Math.max(1, p - 1))}
                    disabled={page <= 1}
                    className="rounded-md border border-zinc-300 px-2 py-1 disabled:opacity-40 dark:border-zinc-700"
                  >
                    ‹ Précédent
                  </button>
                  <span className="text-zinc-500">Page {page} / {totalPages}</span>
                  <button
                    onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
                    disabled={page >= totalPages}
                    className="rounded-md border border-zinc-300 px-2 py-1 disabled:opacity-40 dark:border-zinc-700"
                  >
                    Suivant ›
                  </button>
                </div>
              )}

              <ul className="max-h-80 overflow-auto border-t border-zinc-100 dark:border-zinc-900">
                {pageItems.map((p, i) => {
                  const meta = [
                    p.battery != null ? `🔋 ${p.battery}%` : null,
                    p.accuracy != null ? `±${Math.round(p.accuracy)} m` : null,
                  ]
                    .filter(Boolean)
                    .join(" · ");
                  return (
                  <li
                    key={i}
                    className={
                      "flex items-center justify-between gap-3 border-b border-zinc-100 px-4 py-2 text-sm last:border-0 dark:border-zinc-900 " +
                      (focus === p ? "bg-blue-50 dark:bg-blue-950/40" : "")
                    }
                  >
                    <button onClick={() => setFocus(p)} className="flex min-w-0 flex-1 flex-col text-left">
                      <span className="capitalize text-zinc-700 dark:text-zinc-300">{histFmt.format(new Date(p.recordedAt))}</span>
                      <span className="text-xs text-zinc-400">{meta}</span>
                    </button>
                    <div className="flex shrink-0 gap-1.5 text-xs">
                      <button onClick={() => setFocus(p)} className="rounded-md border border-zinc-300 px-2 py-1 hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-900">
                        Carte
                      </button>
                      <a href={osmLink(p)} target="_blank" rel="noopener noreferrer" className="rounded-md border border-zinc-300 px-2 py-1 hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-900">
                        ↗
                      </a>
                    </div>
                  </li>
                  );
                })}
              </ul>
            </details>
          )}

          {/* Gestion de l'appareil sélectionné — Super Admin uniquement */}
          {selected && canManage && (
            <div className="mt-6 rounded-2xl border border-zinc-200 bg-white p-5 dark:border-zinc-800 dark:bg-zinc-950">
              <div className="mb-3 flex items-center justify-between">
                <h2 className="font-medium text-black dark:text-zinc-50">Configuration — {selected.name}</h2>
                <button onClick={() => removeDevice(selected.id)} className="rounded-md border border-red-300 px-2 py-1 text-xs text-red-600 hover:bg-red-50 dark:border-red-900 dark:hover:bg-red-950">
                  Supprimer
                </button>
              </div>

              <ol className="mb-4 list-decimal space-y-1 pl-5 text-sm text-zinc-600 dark:text-zinc-300">
                <li>Installez l&apos;app <strong>OwnTracks</strong> (Play Store) sur le téléphone.</li>
                <li>Dans OwnTracks : <strong>Preferences → Connection → Mode → Private HTTP</strong>.</li>
                <li>Dans <strong>Host</strong>, collez l&apos;URL ci-dessous (bouton « Afficher l&apos;URL »).</li>
                <li>Laissez les autres champs par défaut, autorisez la localisation « tout le temps ».</li>
              </ol>

              {tokens[selected.id] ? (
                <div className="flex flex-col gap-2">
                  <code className="block break-all rounded-lg bg-zinc-100 px-3 py-2 text-xs dark:bg-zinc-900">
                    {ORIGIN}/backend/track/owntracks/{tokens[selected.id]}
                  </code>
                  <div className="flex flex-wrap gap-2 text-xs">
                    <button
                      onClick={() => navigator.clipboard?.writeText(`${ORIGIN}/backend/track/owntracks/${tokens[selected.id]}`)}
                      className="rounded-md border border-zinc-300 px-2 py-1 hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-900"
                    >
                      Copier l&apos;URL
                    </button>
                    <button onClick={() => regenerate(selected.id)} className="rounded-md border border-zinc-300 px-2 py-1 hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-900">
                      Régénérer le jeton
                    </button>
                  </div>
                  <p className="text-xs text-amber-600 dark:text-amber-400">
                    ⚠️ Cette URL est un secret : quiconque la possède peut envoyer des positions pour cet appareil.
                  </p>
                </div>
              ) : (
                <button onClick={() => showToken(selected.id)} className="rounded-lg border border-zinc-300 px-3 py-2 text-sm hover:bg-zinc-100 dark:border-zinc-700 dark:hover:bg-zinc-900">
                  Afficher l&apos;URL OwnTracks
                </button>
              )}
            </div>
          )}

          {/* Alerte de zone (geofence → WhatsApp) — Super Admin uniquement */}
          {selected && canManage && (
            <div className="mt-4 rounded-2xl border border-zinc-200 bg-white p-5 dark:border-zinc-800 dark:bg-zinc-950">
              <h2 className="font-medium text-black dark:text-zinc-50">Alerte de zone (WhatsApp)</h2>
              <p className="mt-1 text-xs text-zinc-500">
                Envoie un message WhatsApp quand l&apos;appareil sort du rayon autour du point de référence.
                ⚠️ Nécessite que WhatsApp soit connecté.
              </p>

              <div className="mt-4 flex flex-col gap-3">
                <label className="flex items-center gap-2 text-sm">
                  <input type="checkbox" checked={gf.enabled} onChange={(e) => setGf((s) => ({ ...s, enabled: e.target.checked }))} />
                  Alerte activée
                </label>

                <div>
                  <div className="mb-1 text-xs text-zinc-500">Point de référence (cercle vert sur la carte)</div>
                  <div className="flex flex-wrap items-center gap-2">
                    <span className="rounded-md bg-zinc-100 px-2 py-1 text-xs dark:bg-zinc-900">
                      {gf.refLat && gf.refLng ? `${Number(gf.refLat).toFixed(5)}, ${Number(gf.refLng).toFixed(5)}` : "non défini"}
                    </span>
                    <button onClick={() => setGfPick((v) => !v)} className={"rounded-md border px-2 py-1 text-xs " + (gfPick ? "border-green-500 text-green-600" : "border-zinc-300 dark:border-zinc-700")}>
                      {gfPick ? "Cliquez sur la carte…" : "Définir sur la carte"}
                    </button>
                    {selected.lastPoint && (
                      <button
                        onClick={() => selected.lastPoint && setGf((s) => ({ ...s, refLat: selected.lastPoint!.lat.toFixed(6), refLng: selected.lastPoint!.lng.toFixed(6) }))}
                        className="rounded-md border border-zinc-300 px-2 py-1 text-xs dark:border-zinc-700"
                      >
                        Dernière position
                      </button>
                    )}
                    {gfForMap && (
                      <button
                        onClick={() => setFocus({ lat: gfForMap.lat, lng: gfForMap.lng, recordedAt: "", battery: null })}
                        className="rounded-md border border-green-400 px-2 py-1 text-xs text-green-700 dark:border-green-800 dark:text-green-400"
                      >
                        Voir sur la carte
                      </button>
                    )}
                  </div>
                  {/* État enregistré / non enregistré */}
                  {gf.refLat && gf.refLng && (
                    gfDirty ? (
                      <p className="mt-1 text-xs text-amber-600 dark:text-amber-400">● Point modifié — cliquez « Enregistrer l&apos;alerte » pour l&apos;appliquer.</p>
                    ) : (
                      <p className="mt-1 text-xs text-green-600 dark:text-green-400">✓ Point de référence enregistré.</p>
                    )
                  )}
                </div>

                <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
                  <label className="flex flex-col gap-1 text-xs text-zinc-500">
                    Rayon (mètres)
                    <input type="number" min={10} value={gf.radiusM} onChange={(e) => setGf((s) => ({ ...s, radiusM: e.target.value }))} className="rounded-lg border border-zinc-300 px-3 py-2 text-sm dark:border-zinc-700 dark:bg-black" />
                  </label>
                  <label className="flex flex-col gap-1 text-xs text-zinc-500">
                    Conversation WhatsApp à alerter
                    {waChats.length > 0 ? (
                      <select value={gf.notifyNumber} onChange={(e) => setGf((s) => ({ ...s, notifyNumber: e.target.value }))} className="rounded-lg border border-zinc-300 px-3 py-2 text-sm dark:border-zinc-700 dark:bg-black">
                        <option value="">— choisir une conversation —</option>
                        {waChats.map((c) => (
                          <option key={c.jid} value={c.jid}>{c.name}</option>
                        ))}
                        {gf.notifyNumber && !waChats.some((c) => c.jid === gf.notifyNumber) && (
                          <option value={gf.notifyNumber}>{gf.notifyNumber}</option>
                        )}
                      </select>
                    ) : (
                      <input value={gf.notifyNumber} onChange={(e) => setGf((s) => ({ ...s, notifyNumber: e.target.value }))} placeholder="numéro international (ex. 33612345678)" className="rounded-lg border border-zinc-300 px-3 py-2 text-sm dark:border-zinc-700 dark:bg-black" />
                    )}
                  </label>
                </div>

                <div className="flex flex-col gap-1 text-xs text-zinc-500">
                  <span>Message envoyé (laisser vide pour le message par défaut)</span>
                  <textarea
                    ref={messageRef}
                    value={gf.message}
                    onChange={(e) => setGf((s) => ({ ...s, message: e.target.value }))}
                    rows={3}
                    placeholder="Ex. 🚗 {device} a quitté la zone ({distance} m)."
                    className="rounded-lg border border-zinc-300 px-3 py-2 text-sm dark:border-zinc-700 dark:bg-black"
                  />

                  {/* Insertion d'icônes */}
                  <div>
                    <button
                      type="button"
                      onClick={() => setShowEmoji((v) => !v)}
                      className="rounded-md border border-zinc-300 px-2 py-1 text-xs dark:border-zinc-700"
                    >
                      {showEmoji ? "Masquer les icônes" : "😀 Insérer une icône"}
                    </button>
                    {showEmoji && (
                      <div className="mt-2 flex flex-wrap gap-1 rounded-lg border border-zinc-200 p-2 dark:border-zinc-800">
                        {EMOJIS.map((e) => (
                          <button
                            key={e}
                            type="button"
                            onClick={() => insertEmoji(e)}
                            className="rounded-md px-1.5 py-1 text-lg hover:bg-zinc-100 dark:hover:bg-zinc-900"
                            aria-label={`Insérer ${e}`}
                          >
                            {e}
                          </button>
                        ))}
                      </div>
                    )}
                  </div>

                  <span className="text-[11px] text-zinc-400">
                    Variables : <code>{"{device}"}</code> (nom), <code>{"{distance}"}</code> (m), <code>{"{radius}"}</code> (rayon).
                  </span>
                </div>

                {gfMsg && <p className="text-sm text-zinc-600 dark:text-zinc-300">{gfMsg}</p>}
                {gfTestMsg && <p className="text-sm text-zinc-600 dark:text-zinc-300">{gfTestMsg}</p>}

                <div className="flex flex-wrap gap-2">
                  <button onClick={saveGeofence} className="rounded-lg bg-black px-4 py-2 text-sm font-medium text-white hover:bg-zinc-800 dark:bg-white dark:text-black dark:hover:bg-zinc-200">
                    Enregistrer l&apos;alerte
                  </button>
                  <button onClick={testGeofence} className="rounded-lg border border-green-400 px-4 py-2 text-sm font-medium text-green-700 hover:bg-green-50 dark:border-green-800 dark:text-green-400 dark:hover:bg-green-950">
                    Envoyer un test
                  </button>
                  <button onClick={syncWaypoints} className="rounded-lg border border-blue-300 px-4 py-2 text-sm text-blue-700 hover:bg-blue-50 dark:border-blue-900 dark:text-blue-400 dark:hover:bg-blue-950">
                    Renvoyer la zone au téléphone
                  </button>
                  <button onClick={deleteGeofence} className="rounded-lg border border-red-300 px-4 py-2 text-sm text-red-600 hover:bg-red-50 dark:border-red-900 dark:hover:bg-red-950">
                    Supprimer
                  </button>
                </div>
                <p className="text-[11px] text-zinc-400">
                  La zone est aussi envoyée au téléphone (geofence côté appareil, plus fiable) — nécessite « Remote commands » activé dans OwnTracks.
                </p>
              </div>
            </div>
          )}

          {/* Alerte batterie faible — Super Admin uniquement */}
          {selected && canManage && (
            <div className="mt-4 rounded-2xl border border-zinc-200 bg-white p-5 dark:border-zinc-800 dark:bg-zinc-950">
              <h2 className="font-medium text-black dark:text-zinc-50">🔋 Alerte batterie faible (WhatsApp)</h2>
              <p className="mt-1 text-xs text-zinc-500">
                Envoie un message quand la batterie passe sous le seuil. ⚠️ Nécessite WhatsApp connecté.
              </p>

              <div className="mt-4 flex flex-col gap-3">
                <label className="flex items-center gap-2 text-sm">
                  <input type="checkbox" checked={batt.enabled} onChange={(e) => setBatt((s) => ({ ...s, enabled: e.target.checked }))} />
                  Alerte activée
                </label>

                <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
                  <label className="flex flex-col gap-1 text-xs text-zinc-500">
                    Seuil (%)
                    <input type="number" min={1} max={100} value={batt.threshold} onChange={(e) => setBatt((s) => ({ ...s, threshold: e.target.value }))} className="rounded-lg border border-zinc-300 px-3 py-2 text-sm dark:border-zinc-700 dark:bg-black" />
                  </label>
                  <label className="flex flex-col gap-1 text-xs text-zinc-500">
                    Conversation WhatsApp à alerter
                    {waChats.length > 0 ? (
                      <select value={batt.notifyNumber} onChange={(e) => setBatt((s) => ({ ...s, notifyNumber: e.target.value }))} className="rounded-lg border border-zinc-300 px-3 py-2 text-sm dark:border-zinc-700 dark:bg-black">
                        <option value="">— choisir une conversation —</option>
                        {waChats.map((c) => (
                          <option key={c.jid} value={c.jid}>{c.name}</option>
                        ))}
                        {batt.notifyNumber && !waChats.some((c) => c.jid === batt.notifyNumber) && (
                          <option value={batt.notifyNumber}>{batt.notifyNumber}</option>
                        )}
                      </select>
                    ) : (
                      <input value={batt.notifyNumber} onChange={(e) => setBatt((s) => ({ ...s, notifyNumber: e.target.value }))} placeholder="numéro international" className="rounded-lg border border-zinc-300 px-3 py-2 text-sm dark:border-zinc-700 dark:bg-black" />
                    )}
                  </label>
                </div>

                <div className="flex flex-col gap-1 text-xs text-zinc-500">
                  <span>Message envoyé (laisser vide pour le message par défaut)</span>
                  <textarea
                    ref={battMsgRef}
                    value={batt.message}
                    onChange={(e) => setBatt((s) => ({ ...s, message: e.target.value }))}
                    rows={2}
                    placeholder="Ex. 🔋 {device} est à {battery}% (seuil {threshold}%)"
                    className="rounded-lg border border-zinc-300 px-3 py-2 text-sm dark:border-zinc-700 dark:bg-black"
                  />
                  <div>
                    <button type="button" onClick={() => setBattShowEmoji((v) => !v)} className="rounded-md border border-zinc-300 px-2 py-1 text-xs dark:border-zinc-700">
                      {battShowEmoji ? "Masquer les icônes" : "😀 Insérer une icône"}
                    </button>
                    {battShowEmoji && (
                      <div className="mt-2 flex flex-wrap gap-1 rounded-lg border border-zinc-200 p-2 dark:border-zinc-800">
                        {EMOJIS.map((e) => (
                          <button key={e} type="button" onClick={() => insertBattEmoji(e)} className="rounded-md px-1.5 py-1 text-lg hover:bg-zinc-100 dark:hover:bg-zinc-900" aria-label={`Insérer ${e}`}>
                            {e}
                          </button>
                        ))}
                      </div>
                    )}
                  </div>
                  <span className="text-[11px] text-zinc-400">
                    Variables : <code>{"{device}"}</code> (nom), <code>{"{battery}"}</code> (%), <code>{"{threshold}"}</code> (seuil).
                  </span>
                </div>

                {battMsg && <p className="text-sm text-zinc-600 dark:text-zinc-300">{battMsg}</p>}
                {battTestMsg && <p className="text-sm text-zinc-600 dark:text-zinc-300">{battTestMsg}</p>}

                <div className="flex flex-wrap gap-2">
                  <button onClick={saveBattery} className="rounded-lg bg-black px-4 py-2 text-sm font-medium text-white hover:bg-zinc-800 dark:bg-white dark:text-black dark:hover:bg-zinc-200">
                    Enregistrer l&apos;alerte
                  </button>
                  <button onClick={testBattery} className="rounded-lg border border-green-400 px-4 py-2 text-sm font-medium text-green-700 hover:bg-green-50 dark:border-green-800 dark:text-green-400 dark:hover:bg-green-950">
                    Envoyer un test
                  </button>
                  <button onClick={deleteBattery} className="rounded-lg border border-red-300 px-4 py-2 text-sm text-red-600 hover:bg-red-50 dark:border-red-900 dark:hover:bg-red-950">
                    Supprimer
                  </button>
                </div>
              </div>
            </div>
          )}
        </>
      )}
    </div>
  );
}
