import { useState, useEffect, useCallback, useRef } from "react";
import { useParams, useNavigate } from "react-router-dom";
import QRCode from "qrcode";
import { MapContainer, TileLayer, Marker } from "react-leaflet";
import "leaflet/dist/leaflet.css";
import L from "leaflet";
import api from "../api/client";
import { useAuth } from "../auth/AuthContext";
import ConfirmDialog from "../components/ConfirmDialog";
import NotesPanel from "../equipment/NotesPanel";
import useMqttWebSocket from "../mqtt/useMqttWebSocket";

// Fix default Leaflet marker icon
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
  iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png",
  iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png",
  shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png",
});

// --- Helpers ---

function formatSecondsAgo(seconds) {
  if (seconds == null) return null;
  if (seconds < 60) return `${seconds}s ago`;
  if (seconds < 3600) {
    const m = Math.round(seconds / 60);
    return `${m}m ago`;
  }
  if (seconds < 86400) {
    const h = Math.round(seconds / 3600);
    return `${h}h ago`;
  }
  const d = Math.round(seconds / 86400);
  return `${d}d ago`;
}

// --- Helper components ---

function Field({ label, children }) {
  const isUnavailableText =
    typeof children === "string" &&
    (children.toLowerCase() === "unavailable info" || children.toLowerCase() === "info unavailable");

  return (
    <div className="device-field">
      <dt className="db-info-label">{label}</dt>
      <dd className="device-field-value db-info-value">
        {children == null ? (
          <span className="device-value-secondary">Unavailable info</span>
        ) : isUnavailableText ? (
          <span className="device-value-secondary">{children}</span>
        ) : (
          children
        )}
      </dd>
    </div>
  );
}

function BoolBadge({ value, yesLabel = "Yes", noLabel = "No" }) {
  const isMissing = typeof value !== "boolean";
  const text = isMissing ? "Unavailable info" : value ? yesLabel : noLabel;
  const tone = isMissing ? "muted" : value ? "positive" : "negative";
  return (
    <span className={`device-status-text device-status-text--${tone}`}>
      {text}
    </span>
  );
}

function SectionCard({ title, children, onEdit }) {
  return (
    <section className="device-section-card">
      <div className="device-section-card__title-row">
        <h2 className="device-section-card__title">{title}</h2>
        {onEdit && (
          <button
            type="button"
            onClick={onEdit}
            className="section-edit-btn"
            title={`Edit ${title}`}
          >
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="13" height="13" aria-hidden="true">
              <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
              <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
            </svg>
            Edit
          </button>
        )}
      </div>
      {children}
    </section>
  );
}

function Subsection({ title, children, isFirst = false }) {
  return (
    <div className={isFirst ? "" : "mt-4 pt-4 border-t"} style={isFirst ? {} : { borderColor: "var(--border-secondary)" }}>
      <h3 className="text-sm font-semibold mb-3" style={{ color: "var(--text-primary)" }}>{title}</h3>
      {children}
    </div>
  );
}

/** A grid row of fields â€” Nth items align across rows within a subsection */
function FieldRow({ children, columns }) {
  const childArray = Array.isArray(children) ? children.filter(Boolean) : [children];
  const count = columns || childArray.length;
  return (
    <dl className="device-field-row" style={{ gridTemplateColumns: `repeat(${count}, minmax(0, 1fr))` }}>
      {children}
    </dl>
  );
}

function EmptyCell() {
  return <div className="device-empty-cell" aria-hidden="true" />;
}

function TabButton({ active, onClick, tone, children }) {
  return (
    <button
      onClick={onClick}
      className={`device-tab-btn device-tab-btn--${tone} ${active ? "device-tab-btn--active" : ""}`}
      type="button"
    >
      {children}
    </button>
  );
}

function ProgressBar({ label, value, tone = "var(--accent)" }) {
  const width = Number.isFinite(value) ? Math.max(0, Math.min(100, value)) : 0;
  return (
    <div className="space-y-2">
      {label && <p className="text-xs font-medium" style={{ color: "var(--text-muted)" }}>{label}</p>}
      <div className="device-progress-track">
        <div className="device-progress-fill" style={{ width: `${width}%`, backgroundColor: tone }} />
      </div>
      <p className="text-xs" style={{ color: "var(--text-secondary)" }}>{Math.round(width)}%</p>
    </div>
  );
}

// --- Shared modal helpers ---

function ToggleSwitch({ value, onChange, onLabel = "On", offLabel = "Off" }) {
  return (
    <button
      type="button"
      onClick={() => onChange(!value)}
      className="toggle-switch"
      data-on={value ? "true" : "false"}
      aria-pressed={value}
    >
      <span className="toggle-switch__track" data-on={value ? "true" : "false"}>
        <span className="toggle-switch__thumb" />
      </span>
      <span className="toggle-switch__label" data-on={value ? "true" : "false"}>
        {value ? onLabel : offLabel}
      </span>
    </button>
  );
}

const LOG_LEVEL_OPTIONS = [
  { value: 0, label: "Disabled", color: "var(--text-muted)" },
  { value: 1, label: "Error", color: "var(--danger-text)" },
  { value: 2, label: "Warning", color: "#f59e0b" },
  { value: 3, label: "Info", color: "#63b3ed" },
  { value: 4, label: "Debug", color: "#9f7aea" },
  { value: 5, label: "Verbose", color: "#20c997" },
];

function LogLevelSelect({ value, onChange }) {
  return (
    <div className="flex flex-wrap gap-2">
      {LOG_LEVEL_OPTIONS.map((opt) => {
        const isSelected = value === opt.value;
        return (
          <button
            key={opt.value}
            type="button"
            onClick={() => onChange(opt.value)}
            className="px-3 py-1.5 text-xs rounded-md border cursor-pointer transition-colors"
            style={{
              backgroundColor: isSelected ? opt.color : "var(--bg-input)",
              borderColor: isSelected ? opt.color : "var(--border-input)",
              color: isSelected ? "#000" : opt.color,
              fontWeight: isSelected ? 700 : 400,
            }}
          >
            {opt.label}
          </button>
        );
      })}
    </div>
  );
}

function QrModal({ value, onClose }) {
  const canvasRef = useRef(null);
  useEffect(() => {
    if (!value || !canvasRef.current) return;
    QRCode.toCanvas(canvasRef.current, value, { width: 220, margin: 2, color: { dark: "#e3e5ea", light: "#1f2937" } });
  }, [value]);
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [onClose]);
  if (!value) return null;
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center" style={{ backgroundColor: "rgba(0,0,0,0.7)" }}
      onClick={onClose}>
      <div className="rounded-xl border p-5 shadow-2xl flex flex-col items-center gap-3"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}
        onClick={(e) => e.stopPropagation()}>
        <p className="text-xs font-mono" style={{ color: "var(--text-muted)" }}>{value}</p>
        <canvas ref={canvasRef} style={{ borderRadius: 8 }} />
        <p className="text-xs" style={{ color: "var(--text-muted)" }}>Click outside or press ESC to close</p>
      </div>
    </div>
  );
}

function SectionModal({ open, title, onCancel, onSave, saving, disabled, size = "max-w-lg", children }) {
  if (!open) return null;
  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4">
      <div className="fixed inset-0 bg-black/60" onClick={onCancel} />
      <div
        className={`relative rounded-xl shadow-2xl border w-full ${size} flex flex-col`}
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)", maxHeight: "90vh" }}
      >
        <div className="flex items-center justify-between px-6 pt-5 pb-4 border-b" style={{ borderColor: "var(--border-secondary)" }}>
          <h3 className="text-base font-semibold" style={{ color: "var(--text-heading)" }}>{title}</h3>
          <button type="button" onClick={onCancel} className="text-lg leading-none hover:opacity-70 cursor-pointer" style={{ color: "var(--text-muted)" }}>&#x2715;</button>
        </div>
        <div className="overflow-y-auto px-6 py-5 flex-1">
          {children}
        </div>
        <div className="flex justify-end gap-3 px-6 py-4 border-t" style={{ borderColor: "var(--border-secondary)" }}>
          <button
            type="button"
            onClick={onCancel}
            className="px-4 py-2 text-sm rounded-md cursor-pointer hover:opacity-80"
            style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-primary)" }}
          >
            Cancel
          </button>
          <button
            type="button"
            onClick={onSave}
            disabled={saving || disabled}
            className="px-4 py-2 text-sm rounded-md font-medium cursor-pointer hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed"
            style={{ backgroundColor: "var(--accent)", color: "#000" }}
          >
            {saving ? "Saving..." : "Sync & Save Settings"}
          </button>
        </div>
      </div>
    </div>
  );
}

function ModalField({ label, children, hint }) {
  return (
    <div className="mb-4">
      <label className="block text-xs font-semibold mb-1.5 uppercase tracking-wide" style={{ color: "var(--text-muted)" }}>{label}</label>
      {children}
      {hint && <p className="mt-1 text-xs" style={{ color: "var(--text-muted)" }}>{hint}</p>}
    </div>
  );
}

function PulseButtonGroup({ value, onChange, options }) {
  return (
    <div className="flex flex-wrap gap-2">
      {options.map((ms) => (
        <button
          key={ms}
          type="button"
          onClick={() => onChange(ms)}
          className="px-3 py-1.5 text-xs rounded-md border cursor-pointer transition-colors"
          style={{
            backgroundColor: value === ms ? "var(--accent)" : "var(--bg-input)",
            borderColor: value === ms ? "var(--accent)" : "var(--border-input)",
            color: value === ms ? "#000" : "var(--text-primary)",
            fontWeight: value === ms ? 600 : 400,
          }}
        >
          {(ms / 1000).toFixed(1)}s
        </button>
      ))}
    </div>
  );
}

function AlertTypeGroup({ value, onChange }) {
  const opts = [
    { val: "disabled", label: "Disabled" },
    { val: "single", label: "Single Fire" },
    { val: "multi", label: "Hour Indicating" },
  ];
  return (
    <div className="flex gap-2">
      {opts.map((o) => (
        <button
          key={o.val}
          type="button"
          onClick={() => onChange(o.val)}
          className="flex-1 px-3 py-2 text-xs rounded-md border cursor-pointer transition-colors"
          style={{
            backgroundColor: value === o.val ? "var(--accent)" : "var(--bg-input)",
            borderColor: value === o.val ? "var(--accent)" : "var(--border-input)",
            color: value === o.val ? "#000" : "var(--text-primary)",
            fontWeight: value === o.val ? 600 : 400,
          }}
        >
          {o.label}
        </button>
      ))}
    </div>
  );
}

function extractTimeFromTimestamp(tsStr) {
  if (!tsStr) return "";
  const d = new Date(tsStr.replace(" at ", " ").replace("UTC+0000", "UTC").replace(/UTC\+(\d{4})/, "UTC"));
  if (!isNaN(d.getTime())) {
    return `${d.getUTCHours().toString().padStart(2, "0")}:${d.getUTCMinutes().toString().padStart(2, "0")}`;
  }
  const match = tsStr.match(/(\d{1,2}):(\d{2})/);
  if (match) return `${match[1].padStart(2, "0")}:${match[2]}`;
  return "";
}

function timeToIso(hhmm) {
  if (!hhmm) return "";
  return `2000-01-01T${hhmm}:00Z`;
}

function DraggableMarker({ lat, lng, onMove }) {
  const markerRef = useRef(null);
  const eventHandlers = {
    dragend() {
      const m = markerRef.current;
      if (m) {
        const pos = m.getLatLng();
        onMove(parseFloat(pos.lat.toFixed(7)), parseFloat(pos.lng.toFixed(7)));
      }
    },
  };
  return <Marker draggable eventHandlers={eventHandlers} position={[lat, lng]} ref={markerRef} />;
}

// --- Top-level modal components (defined before DeviceDetail to satisfy rules of hooks) ---

function LocationModal({ open, onClose, onSaved, device, coords, id }) {
  const [locName, setLocName] = useState(device?.device_location || "");
  const [lat, setLat] = useState(coords ? String(coords.lat) : "");
  const [lng, setLng] = useState(coords ? String(coords.lng) : "");
  const [saving, setSaving] = useState(false);

  const handleSave = async () => {
    setSaving(true);
    const latNum = parseFloat(lat);
    const lngNum = parseFloat(lng);
    const coordPayload = (!isNaN(latNum) && !isNaN(lngNum))
      ? { lat: latNum, lng: lngNum }
      : (device?.device_location_coordinates || null);
    try {
      await api.put(`/devices/${id}`, { device_location: locName, device_location_coordinates: coordPayload });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Device Location" onCancel={onClose} onSave={handleSave} saving={saving} size="max-w-lg">
      <ModalField label="Location Name" hint={`${locName.length}/35 characters`}>
        <input
          type="text"
          value={locName}
          maxLength={35}
          onChange={(e) => setLocName(e.target.value)}
          className="w-full px-3 py-2 rounded-md text-sm border"
          style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
          placeholder="e.g. Saint Mary's Church"
        />
      </ModalField>
      <div className="flex gap-3 mb-4">
        <ModalField label="Latitude">
          <input
            type="number"
            value={lat}
            step="0.0000001"
            onChange={(e) => setLat(e.target.value)}
            className="w-full px-3 py-2 rounded-md text-sm border"
            style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
            placeholder="e.g. 37.9838"
          />
        </ModalField>
        <ModalField label="Longitude">
          <input
            type="number"
            value={lng}
            step="0.0000001"
            onChange={(e) => setLng(e.target.value)}
            className="w-full px-3 py-2 rounded-md text-sm border"
            style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
            placeholder="e.g. 23.7275"
          />
        </ModalField>
      </div>
      {(() => {
        const latN = parseFloat(lat);
        const lngN = parseFloat(lng);
        if (isNaN(latN) || isNaN(lngN)) return null;
        return (
          <div className="rounded-md overflow-hidden border mb-2" style={{ borderColor: "var(--border-primary)", height: 220 }}>
            <MapContainer
              key={`${lat}-${lng}`}
              center={[latN, lngN]}
              zoom={13}
              style={{ height: "100%", width: "100%" }}
              scrollWheelZoom={true}
            >
              <TileLayer
                attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
                url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
              />
              <DraggableMarker lat={latN} lng={lngN} onMove={(newLat, newLng) => { setLat(String(newLat)); setLng(String(newLng)); }} />
            </MapContainer>
          </div>
        );
      })()}
      <p className="text-xs" style={{ color: "var(--text-muted)" }}>Drag the pin to update coordinates, or type them directly above.</p>
    </SectionModal>
  );
}

function AttributesModal({ open, onClose, onSaved, device, attr, id }) {
  const [bellGuardOn, setBellGuardOn] = useState(attr?.bellGuardOn ?? false);
  const [warningsOn, setWarningsOn] = useState(attr?.warningsOn ?? false);
  const [bellGuardSafetyOn, setBellGuardSafetyOn] = useState(attr?.bellGuardSafetyOn ?? false);
  const [hasBells, setHasBells] = useState(attr?.hasBells ?? false);
  const [hasClock, setHasClock] = useState(attr?.hasClock ?? false);
  const [saving, setSaving] = useState(false);

  const handleSave = async () => {
    setSaving(true);
    try {
      await api.put(`/devices/${id}`, { device_attributes: { bellGuardOn, warningsOn, bellGuardSafetyOn, hasBells, hasClock } });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Basic Attributes" onCancel={onClose} onSave={handleSave} saving={saving} size="max-w-md">
      <div className="flex gap-4">
        <div className="flex-1">
          <ModalField label="Bell Guard">
            <ToggleSwitch value={bellGuardOn} onChange={setBellGuardOn} onLabel="Active" offLabel="Disabled" />
          </ModalField>
        </div>
        <div className="flex-1">
          <ModalField label="Bell Guard Safety">
            <ToggleSwitch value={bellGuardSafetyOn} onChange={setBellGuardSafetyOn} onLabel="Armed" offLabel="Disarmed" />
          </ModalField>
        </div>
      </div>
      <ModalField label="Warnings">
        <ToggleSwitch value={warningsOn} onChange={setWarningsOn} onLabel="Active" offLabel="Disabled" />
      </ModalField>
      <div className="flex gap-4">
        <div className="flex-1">
          <ModalField label="Bells Mechanism">
            <ToggleSwitch value={hasBells} onChange={setHasBells} onLabel="Enabled" offLabel="Disabled" />
          </ModalField>
        </div>
        <div className="flex-1">
          <ModalField label="Bells Output">
            <ToggleSwitch value={hasClock} onChange={setHasClock} onLabel="Enabled" offLabel="Disabled" />
          </ModalField>
        </div>
      </div>
      <div className="mt-2 p-3 rounded-md" style={{ backgroundColor: "var(--bg-primary)", borderColor: "var(--border-secondary)" }}>
        <p className="text-xs" style={{ color: "var(--text-muted)" }}>
          Connected Bells: <span style={{ color: "var(--text-primary)" }}>{formatConnectedBells(attr?.totalBells)}</span> — managed via Bell Mechanisms tab.
        </p>
      </div>
    </SectionModal>
  );
}

function LoggingModal({ open, onClose, onSaved, attr, id }) {
  const [serialLevel, setSerialLevel] = useState(attr?.serialLogLevel ?? 0);
  const [sdLevel, setSdLevel] = useState(attr?.sdLogLevel ?? 0);
  const [mqttLevel, setMqttLevel] = useState(attr?.mqttLogLevel ?? 0);
  const [saving, setSaving] = useState(false);

  const handleSave = async () => {
    setSaving(true);
    try {
      await api.put(`/devices/${id}`, { device_attributes: { serialLogLevel: serialLevel, sdLogLevel: sdLevel, mqttLogLevel: mqttLevel } });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Log Levels" onCancel={onClose} onSave={handleSave} saving={saving} size="max-w-lg">
      <div className="flex flex-col gap-5">
        <ModalField label="Serial Logs Level">
          <LogLevelSelect value={serialLevel} onChange={setSerialLevel} />
        </ModalField>
        <ModalField label="SD Card Log Level">
          <LogLevelSelect value={sdLevel} onChange={setSdLevel} />
        </ModalField>
        <ModalField label="MQTT Log Level">
          <LogLevelSelect value={mqttLevel} onChange={setMqttLevel} />
        </ModalField>
      </div>
    </SectionModal>
  );
}

function MiscModal({ open, onClose, onSaved, device, attr, id }) {
  const [eventsOn, setEventsOn] = useState(device?.events_on ?? false);
  const [locale, setLocale] = useState(attr?.deviceLocale || "all");
  const [wsUrl, setWsUrl] = useState(device?.websocket_url || "");
  const [saving, setSaving] = useState(false);

  const handleSave = async () => {
    setSaving(true);
    try {
      await api.put(`/devices/${id}`, { events_on: eventsOn, device_attributes: { deviceLocale: locale }, websocket_url: wsUrl });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Misc Settings" onCancel={onClose} onSave={handleSave} saving={saving} size="max-w-md">
      <ModalField label="Automated Events">
        <ToggleSwitch value={eventsOn} onChange={setEventsOn} onLabel="Enabled" offLabel="Disabled" />
      </ModalField>
      <ModalField label="Church Type">
        <select
          value={locale}
          onChange={(e) => setLocale(e.target.value)}
          className="w-full px-3 py-2 rounded-md text-sm border"
          style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
        >
          <option value="all">All (Global)</option>
          <option value="orthodox">Orthodox</option>
          <option value="catholic">Catholic</option>
        </select>
      </ModalField>
      <ModalField label="WebSocket URL">
        <input
          type="text"
          value={wsUrl}
          onChange={(e) => setWsUrl(e.target.value)}
          className="w-full px-3 py-2 rounded-md text-sm border"
          style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
          placeholder="ws://..."
        />
      </ModalField>
    </SectionModal>
  );
}

function BellOutputsModal({ open, onClose, onSaved, attr, clock, sub, id }) {
  const maxOutputs = sub?.maxOutputs || 8;
  const clockOutputsUsed = (clock?.clockOutputs || []).filter((o) => o > 0);
  const existingCount = attr?.totalBells || 0;
  const existingOutputs = attr?.bellOutputs || [];
  const existingTimings = attr?.hammerTimings || [];
  const TIMING_OPTIONS = [80, 85, 90, 95, 100, 110, 120, 140];
  const TIMING_LABELS = { 80: "Min - 80ms", 85: "Size 1 - 85ms", 90: "Size 2 - 90ms", 95: "Size 3 - 95ms", 100: "Size 4 - 100ms", 110: "Size 5 - 110ms", 120: "Size 6 - 120ms", 140: "Max - 140ms" };

  const buildDefaults = (count) =>
    Array.from({ length: count }, (_, i) => ({
      output: existingOutputs[i] ?? 0,
      timing: existingTimings[i] ?? 90,
    }));

  const [activeBells, setActiveBells] = useState(existingCount);
  const [rows, setRows] = useState(buildDefaults(maxOutputs));
  const [saving, setSaving] = useState(false);

  const updateRow = (i, field, val) => {
    setRows((prev) => prev.map((r, idx) => idx === i ? { ...r, [field]: val } : r));
  };

  const activeRows = rows.slice(0, activeBells);

  const duplicateOutputs = activeRows
    .map((r, i) => ({ output: r.output, i }))
    .filter((r) => r.output > 0)
    .filter((r, _, arr) => arr.filter((x) => x.output === r.output).length > 1)
    .map((r) => r.i);

  const handleSave = async () => {
    setSaving(true);
    try {
      const bellOutputs = activeRows.map((r) => r.output);
      const hammerTimings = activeRows.map((r) => r.timing);
      await api.put(`/devices/${id}`, { device_attributes: { totalBells: activeBells, bellOutputs, hammerTimings } });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Bell Outputs" onCancel={onClose} onSave={handleSave} saving={saving} size="max-w-2xl">
      <p className="text-xs mb-4" style={{ color: "var(--text-muted)" }}>
        Click a bell to set how many bells are active (1 through {maxOutputs}). Then assign each an output and timing below.
      </p>

      <div className="flex gap-3 flex-wrap mb-2">
        {Array.from({ length: maxOutputs }, (_, i) => {
          const n = i + 1;
          const isActive = n <= activeBells;
          return (
            <button
              key={n}
              type="button"
              onClick={() => setActiveBells(n === activeBells ? n - 1 : n)}
              title={`Bell ${n}`}
              className="flex items-center justify-center p-2 rounded-lg border cursor-pointer transition-colors"
              style={{
                borderColor: isActive ? "var(--accent)" : "var(--border-input)",
                backgroundColor: isActive ? "rgba(116,184,22,0.1)" : "var(--bg-input)",
                width: 52,
                height: 52,
              }}
            >
              <svg viewBox="0 0 24 24" fill={isActive ? "var(--accent)" : "var(--text-muted)"} width="32" height="32" aria-hidden="true">
                <path d="M13.73 21a2 2 0 0 1-3.46 0M18.63 13A17.89 17.89 0 0 1 18 8a6 6 0 0 0-12 0c0 1.76-.35 3.45-1 4.98L4 14h16l-.37-1z" />
              </svg>
            </button>
          );
        })}
      </div>
      <p className="text-xs mb-5" style={{ color: "var(--text-secondary)" }}>
        {activeBells} of {maxOutputs} bells active
      </p>

      {activeBells > 0 && (
        <div className="rounded-md border overflow-hidden mb-2" style={{ borderColor: "var(--border-primary)" }}>
          <table className="w-full text-sm">
            <thead>
              <tr style={{ backgroundColor: "var(--bg-primary)", borderBottom: "1px solid var(--border-secondary)" }}>
                <th className="px-3 py-2 text-left text-xs font-semibold" style={{ color: "var(--text-muted)" }}>Bell</th>
                <th className="px-3 py-2 text-left text-xs font-semibold" style={{ color: "var(--text-muted)" }}>Output Assignment</th>
                <th className="px-3 py-2 text-left text-xs font-semibold" style={{ color: "var(--text-muted)" }}>Timing (Striker)</th>
              </tr>
            </thead>
            <tbody>
              {activeRows.map((row, i) => {
                const isDup = duplicateOutputs.includes(i);
                const isClockConflict = row.output > 0 && clockOutputsUsed.includes(row.output);
                return (
                  <tr key={i} style={{ borderBottom: i < activeBells - 1 ? "1px solid var(--border-secondary)" : "none" }}>
                    <td className="px-3 py-2 text-xs font-medium" style={{ color: "var(--text-primary)" }}>
                      {ORDINAL_NAMES[i] ?? `${i + 1}th`} Bell
                    </td>
                    <td className="px-3 py-2">
                      <select
                        value={row.output}
                        onChange={(e) => updateRow(i, "output", Number(e.target.value))}
                        className="w-full px-2 py-1.5 rounded text-xs border"
                        style={{
                          backgroundColor: "var(--bg-input)",
                          borderColor: (isDup || isClockConflict) ? "var(--danger)" : "var(--border-input)",
                          color: "var(--text-primary)",
                        }}
                      >
                        <option value={0}>Disabled</option>
                        {Array.from({ length: maxOutputs }, (_, j) => {
                          const out = j + 1;
                          const isClock = clockOutputsUsed.includes(out);
                          return (
                            <option key={out} value={out} style={{ color: isClock ? "var(--danger-text)" : "inherit" }}>
                              Output {out}{isClock ? " (Clock — conflict)" : ""}
                            </option>
                          );
                        })}
                      </select>
                      {isDup && <p className="text-xs mt-0.5" style={{ color: "var(--danger-text)" }}>Duplicate output</p>}
                      {isClockConflict && <p className="text-xs mt-0.5" style={{ color: "var(--danger-text)" }}>Used by clock</p>}
                    </td>
                    <td className="px-3 py-2">
                      <select
                        value={row.timing}
                        onChange={(e) => updateRow(i, "timing", Number(e.target.value))}
                        className="w-full px-2 py-1.5 rounded text-xs border"
                        style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
                      >
                        {TIMING_OPTIONS.map((t) => (
                          <option key={t} value={t}>{TIMING_LABELS[t]}</option>
                        ))}
                      </select>
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      )}
    </SectionModal>
  );
}

function ClockSettingsModal({ open, onClose, onSaved, attr, clock, sub, id }) {
  const maxOutputs = sub?.maxOutputs || 8;
  const [hasClock, setHasClock] = useState(attr?.hasClock ?? false);
  const [oddOut, setOddOut] = useState(clock?.clockOutputs?.[0] || 0);
  const [evenOut, setEvenOut] = useState(clock?.clockOutputs?.[1] || 0);
  const [runPulse, setRunPulse] = useState(clock?.clockTimings?.[0] || 5000);
  const [pause, setPause] = useState(clock?.clockTimings?.[1] || 2000);
  const [saving, setSaving] = useState(false);

  const bellOutputsUsed = (attr?.bellOutputs || []).filter((o) => o > 0);

  const buildOutputOptions = (excludeOther) =>
    Array.from({ length: maxOutputs }, (_, j) => {
      const out = j + 1;
      const isBell = bellOutputsUsed.includes(out);
      const isOther = out === excludeOther && excludeOther > 0;
      return { out, isBell, isOther };
    });

  const conflictMsg = oddOut > 0 && oddOut === evenOut ? "ODD and EVEN outputs cannot be the same." : null;
  const validationMsg = hasClock && (oddOut === 0 || evenOut === 0 || !runPulse || !pause)
    ? "When clock is enabled, both outputs and both pulse timings must be set."
    : null;
  const canSave = !conflictMsg && !validationMsg;

  const handleSave = async () => {
    setSaving(true);
    try {
      await api.put(`/devices/${id}`, {
        device_attributes: {
          hasClock,
          clockSettings: { clockOutputs: [oddOut, evenOut], clockTimings: [runPulse, pause] },
        },
      });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Clock Settings" onCancel={onClose} onSave={handleSave} saving={saving} disabled={!canSave} size="max-w-lg">
      <ModalField label="Clock Controller">
        <ToggleSwitch value={hasClock} onChange={setHasClock} onLabel="Enabled" offLabel="Disabled" />
      </ModalField>
      <div className="flex gap-4">
        <div className="flex-1">
          <ModalField label="ODD Output">
            <select
              value={oddOut}
              onChange={(e) => setOddOut(Number(e.target.value))}
              className="w-full px-3 py-2 rounded-md text-sm border"
              style={{ backgroundColor: "var(--bg-input)", borderColor: oddOut === evenOut && oddOut > 0 ? "var(--danger)" : "var(--border-input)", color: "var(--text-primary)" }}
            >
              <option value={0}>Disconnected</option>
              {buildOutputOptions(evenOut).map(({ out, isBell }) => (
                <option key={out} value={out} disabled={isBell} style={{ color: isBell ? "var(--danger-text)" : "inherit" }}>
                  Output {out}{isBell ? ` (Bell)` : ""}
                </option>
              ))}
            </select>
          </ModalField>
        </div>
        <div className="flex-1">
          <ModalField label="EVEN Output">
            <select
              value={evenOut}
              onChange={(e) => setEvenOut(Number(e.target.value))}
              className="w-full px-3 py-2 rounded-md text-sm border"
              style={{ backgroundColor: "var(--bg-input)", borderColor: oddOut === evenOut && oddOut > 0 ? "var(--danger)" : "var(--border-input)", color: "var(--text-primary)" }}
            >
              <option value={0}>Disconnected</option>
              {buildOutputOptions(oddOut).map(({ out, isBell }) => (
                <option key={out} value={out} disabled={isBell} style={{ color: isBell ? "var(--danger-text)" : "inherit" }}>
                  Output {out}{isBell ? ` (Bell)` : ""}
                </option>
              ))}
            </select>
          </ModalField>
        </div>
      </div>
      {conflictMsg && <p className="text-xs mb-3" style={{ color: "var(--danger-text)" }}>{conflictMsg}</p>}
      {validationMsg && !conflictMsg && <p className="text-xs mb-3" style={{ color: "#f59e0b" }}>{validationMsg}</p>}
      <ModalField label="Run Pulse">
        <PulseButtonGroup value={runPulse} onChange={setRunPulse} options={[2000, 3000, 4000, 5000, 6000, 7000, 8000]} />
      </ModalField>
      <ModalField label="Pause">
        <PulseButtonGroup value={pause} onChange={setPause} options={[1000, 2000, 3000, 4000, 5000]} />
      </ModalField>
    </SectionModal>
  );
}

function AlertsModal({ open, onClose, onSaved, attr, clock, id }) {
  const [ringAlerts, setRingAlerts] = useState(clock?.ringAlerts || "disabled");
  const [ringIntervals, setRingIntervals] = useState(clock?.ringIntervals ?? 1000);
  const [hourBell, setHourBell] = useState(clock?.hourAlertsBell ?? 0);
  const [halfBell, setHalfBell] = useState(clock?.halfhourAlertsBell ?? 0);
  const [quarterBell, setQuarterBell] = useState(clock?.quarterAlertsBell ?? 0);
  const [isDaySilenceOn, setIsDaySilenceOn] = useState(clock?.isDaySilenceOn ?? false);
  const [daySilenceFrom, setDaySilenceFrom] = useState(extractTimeFromTimestamp(clock?.daySilenceFrom));
  const [daySilenceTo, setDaySilenceTo] = useState(extractTimeFromTimestamp(clock?.daySilenceTo));
  const [isNightSilenceOn, setIsNightSilenceOn] = useState(clock?.isNightSilenceOn ?? false);
  const [nightSilenceFrom, setNightSilenceFrom] = useState(extractTimeFromTimestamp(clock?.nightSilenceFrom));
  const [nightSilenceTo, setNightSilenceTo] = useState(extractTimeFromTimestamp(clock?.nightSilenceTo));
  const [saving, setSaving] = useState(false);

  const totalBells = attr?.totalBells || 0;
  const bellOptions = [{ value: 0, label: "Disabled" }, ...Array.from({ length: totalBells }, (_, i) => ({ value: i + 1, label: `Bell ${i + 1}` }))];

  const alertValidationMsg = (() => {
    if (ringAlerts === "multi" && !ringIntervals) return "Alert Tempo must be selected when Hour Indicating is active.";
    if (isDaySilenceOn && (!daySilenceFrom || !daySilenceTo)) return "Day Silence is ON — both From and To times must be set.";
    if (isNightSilenceOn && (!nightSilenceFrom || !nightSilenceTo)) return "Night Silence is ON — both From and To times must be set.";
    return null;
  })();

  const handleSave = async () => {
    setSaving(true);
    try {
      await api.put(`/devices/${id}`, {
        device_attributes: {
          clockSettings: {
            ringAlerts,
            ringAlertsMasterOn: ringAlerts !== "disabled",
            ringIntervals,
            hourAlertsBell: hourBell,
            halfhourAlertsBell: halfBell,
            quarterAlertsBell: quarterBell,
            isDaySilenceOn,
            daySilenceFrom: daySilenceFrom ? timeToIso(daySilenceFrom) : "",
            daySilenceTo: daySilenceTo ? timeToIso(daySilenceTo) : "",
            isNightSilenceOn,
            nightSilenceFrom: nightSilenceFrom ? timeToIso(nightSilenceFrom) : "",
            nightSilenceTo: nightSilenceTo ? timeToIso(nightSilenceTo) : "",
          },
        },
      });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Alert Settings" onCancel={onClose} onSave={handleSave} saving={saving} disabled={!!alertValidationMsg} size="max-w-xl">
      {alertValidationMsg && <p className="text-xs mb-3 p-2 rounded" style={{ color: "#f59e0b", backgroundColor: "rgba(245,158,11,0.08)", border: "1px solid rgba(245,158,11,0.2)" }}>{alertValidationMsg}</p>}
      <ModalField label="Alerts Type">
        <AlertTypeGroup value={ringAlerts} onChange={setRingAlerts} />
      </ModalField>
      <ModalField label="Alert Tempo">
        <PulseButtonGroup value={ringIntervals} onChange={setRingIntervals} options={[500, 1000, 1500, 2000, 2500]} />
      </ModalField>
      <div className="flex gap-4">
        <div className="flex-1">
          <ModalField label="Hour Bell">
            <select value={hourBell} onChange={(e) => setHourBell(Number(e.target.value))} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}>
              {bellOptions.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
            </select>
          </ModalField>
        </div>
        <div className="flex-1">
          <ModalField label="Half-Hour Bell">
            <select value={halfBell} onChange={(e) => setHalfBell(Number(e.target.value))} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}>
              {bellOptions.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
            </select>
          </ModalField>
        </div>
        <div className="flex-1">
          <ModalField label="Quarter Bell">
            <select value={quarterBell} onChange={(e) => setQuarterBell(Number(e.target.value))} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}>
              {bellOptions.map((o) => <option key={o.value} value={o.value}>{o.label}</option>)}
            </select>
          </ModalField>
        </div>
      </div>

      <div className="p-4 rounded-lg border mb-4" style={{ borderColor: "var(--border-secondary)", backgroundColor: "var(--bg-primary)" }}>
        <div className="flex items-center justify-between mb-3">
          <span className="text-sm font-semibold" style={{ color: "var(--text-primary)" }}>Day Silence</span>
          <ToggleSwitch value={isDaySilenceOn} onChange={setIsDaySilenceOn} onLabel="On" offLabel="Off" />
        </div>
        {isDaySilenceOn && (
          <div className="flex gap-4">
            <ModalField label="From">
              <input type="time" value={daySilenceFrom} onChange={(e) => setDaySilenceFrom(e.target.value)} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }} />
            </ModalField>
            <ModalField label="Up To">
              <input type="time" value={daySilenceTo} onChange={(e) => setDaySilenceTo(e.target.value)} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }} />
            </ModalField>
          </div>
        )}
      </div>

      <div className="p-4 rounded-lg border" style={{ borderColor: "var(--border-secondary)", backgroundColor: "var(--bg-primary)" }}>
        <div className="flex items-center justify-between mb-3">
          <span className="text-sm font-semibold" style={{ color: "var(--text-primary)" }}>Night Silence</span>
          <ToggleSwitch value={isNightSilenceOn} onChange={setIsNightSilenceOn} onLabel="On" offLabel="Off" />
        </div>
        {isNightSilenceOn && (
          <div className="flex gap-4">
            <ModalField label="From">
              <input type="time" value={nightSilenceFrom} onChange={(e) => setNightSilenceFrom(e.target.value)} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }} />
            </ModalField>
            <ModalField label="Up To">
              <input type="time" value={nightSilenceTo} onChange={(e) => setNightSilenceTo(e.target.value)} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }} />
            </ModalField>
          </div>
        )}
      </div>
    </SectionModal>
  );
}

function BacklightModal({ open, onClose, onSaved, attr, clock, sub, id }) {
  const maxOutputs = sub?.maxOutputs || 8;
  const [isOn, setIsOn] = useState(clock?.isBacklightAutomationOn ?? false);
  const [output, setOutput] = useState(clock?.backlightOutput ?? 0);
  const [onTime, setOnTime] = useState(extractTimeFromTimestamp(clock?.backlightTurnOnTime));
  const [offTime, setOffTime] = useState(extractTimeFromTimestamp(clock?.backlightTurnOffTime));
  const [saving, setSaving] = useState(false);

  const bellOutputsUsed = (attr?.bellOutputs || []).filter((o) => o > 0);
  const clockOutputsUsed = (clock?.clockOutputs || []).filter((o) => o > 0);

  const backlightValidationMsg = (() => {
    if (!isOn) return null;
    if (!output) return "An output must be assigned when backlight automation is enabled.";
    if (!onTime || !offTime) return "Both Turn On and Turn Off times must be set when automation is enabled.";
    return null;
  })();

  const handleSave = async () => {
    setSaving(true);
    try {
      await api.put(`/devices/${id}`, {
        device_attributes: {
          clockSettings: {
            isBacklightAutomationOn: isOn,
            backlightOutput: output,
            backlightTurnOnTime: isOn && onTime ? timeToIso(onTime) : "",
            backlightTurnOffTime: isOn && offTime ? timeToIso(offTime) : "",
          },
        },
      });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Backlight Settings" onCancel={onClose} onSave={handleSave} saving={saving} disabled={!!backlightValidationMsg} size="max-w-md">
      {backlightValidationMsg && <p className="text-xs mb-3 p-2 rounded" style={{ color: "#f59e0b", backgroundColor: "rgba(245,158,11,0.08)", border: "1px solid rgba(245,158,11,0.2)" }}>{backlightValidationMsg}</p>}
      <ModalField label="Automation">
        <ToggleSwitch value={isOn} onChange={setIsOn} onLabel="Automated" offLabel="Disabled" />
      </ModalField>
      <ModalField label="Output">
        <select
          value={output}
          onChange={(e) => setOutput(Number(e.target.value))}
          className="w-full px-3 py-2 rounded-md text-sm border"
          style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
        >
          <option value={0}>Disabled</option>
          {Array.from({ length: maxOutputs }, (_, j) => {
            const out = j + 1;
            const isBell = bellOutputsUsed.includes(out);
            const isClock = clockOutputsUsed.includes(out);
            const isConflict = isBell || isClock;
            return (
              <option key={out} value={out} style={{ color: isConflict ? "var(--danger-text)" : "inherit" }}>
                Output {out}{isBell ? " (Bell)" : isClock ? " (Clock)" : ""}
              </option>
            );
          })}
        </select>
      </ModalField>
      {isOn && (
        <div className="flex gap-4">
          <ModalField label="Turn On Time">
            <input type="time" value={onTime} onChange={(e) => setOnTime(e.target.value)} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }} />
          </ModalField>
          <ModalField label="Turn Off Time">
            <input type="time" value={offTime} onChange={(e) => setOffTime(e.target.value)} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }} />
          </ModalField>
        </div>
      )}
    </SectionModal>
  );
}

function SubscriptionModal({ open, onClose, onSaved, sub, id }) {
  const [tier, setTier] = useState(sub?.subscrTier || "basic");
  const [startDateStr, setStartDateStr] = useState(() => {
    const d = parseFirestoreDate(sub?.subscrStart);
    return d ? d.toISOString().split("T")[0] : "";
  });
  const [duration, setDuration] = useState(sub?.subscrDuration || 0);
  const [maxUsers, setMaxUsers] = useState(sub?.maxUsers || 1);
  const [maxOutputsVal, setMaxOutputsVal] = useState(sub?.maxOutputs || 1);
  const [saving, setSaving] = useState(false);

  const addDuration = (days) => setDuration((prev) => Math.max(0, prev + days));

  const calcEnd = () => {
    if (!startDateStr || !duration) return null;
    const d = new Date(startDateStr + "T00:00:00Z");
    return new Date(d.getTime() + duration * 86400000);
  };
  const endDate = calcEnd();

  const handleSave = async () => {
    setSaving(true);
    try {
      await api.put(`/devices/${id}`, {
        device_subscription: {
          subscrTier: tier,
          subscrStart: startDateStr ? startDateStr + "T00:00:00Z" : sub?.subscrStart,
          subscrDuration: duration,
          maxUsers: Number(maxUsers),
          maxOutputs: Number(maxOutputsVal),
        }
      });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Subscription" onCancel={onClose} onSave={handleSave} saving={saving} size="max-w-lg">
      <ModalField label="Tier">
        <select value={tier} onChange={(e) => setTier(e.target.value)} className="w-full px-3 py-2 rounded-md text-sm border capitalize" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}>
          {["basic", "small", "mini", "premium", "vip", "custom"].map((t) => (
            <option key={t} value={t} className="capitalize">{t.charAt(0).toUpperCase() + t.slice(1)}</option>
          ))}
        </select>
      </ModalField>
      <ModalField label="Start Date">
        <input type="date" value={startDateStr} onChange={(e) => setStartDateStr(e.target.value)} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }} />
      </ModalField>
      <ModalField label="Duration (days)" hint={endDate ? `End date: ${formatDateNice(endDate)}` : undefined}>
        <div className="flex gap-2 items-center mb-2">
          <input
            type="number"
            min={0}
            value={duration}
            onChange={(e) => setDuration(Math.max(0, parseInt(e.target.value) || 0))}
            className="w-32 px-3 py-2 rounded-md text-sm border"
            style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
          />
          <span className="text-xs" style={{ color: "var(--text-muted)" }}>days</span>
        </div>
        <div className="flex gap-2 flex-wrap">
          {[{ label: "+1 Month", days: 30 }, { label: "+6 Months", days: 180 }, { label: "+1 Year", days: 365 }].map((q) => (
            <button key={q.label} type="button" onClick={() => addDuration(q.days)} className="px-3 py-1.5 text-xs rounded-md border cursor-pointer hover:opacity-80" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}>
              {q.label}
            </button>
          ))}
        </div>
      </ModalField>
      <div className="flex gap-4">
        <div className="flex-1">
          <ModalField label="Max Users">
            <select value={maxUsers} onChange={(e) => setMaxUsers(Number(e.target.value))} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}>
              {Array.from({ length: 10 }, (_, i) => i + 1).map((n) => <option key={n} value={n}>{n} {n === 1 ? "User" : "Users"}</option>)}
            </select>
          </ModalField>
        </div>
        <div className="flex-1">
          <ModalField label="Max Outputs">
            <select value={maxOutputsVal} onChange={(e) => setMaxOutputsVal(Number(e.target.value))} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}>
              {Array.from({ length: 16 }, (_, i) => i + 1).map((n) => <option key={n} value={n}>{n} {n === 1 ? "Output" : "Outputs"}</option>)}
            </select>
          </ModalField>
        </div>
      </div>
    </SectionModal>
  );
}

function WarrantyModal({ open, onClose, onSaved, stats, id }) {
  const [active, setActive] = useState(stats?.warrantyActive ?? true);
  const [startDateStr, setStartDateStr] = useState(() => {
    const d = parseFirestoreDate(stats?.warrantyStart);
    return d ? d.toISOString().split("T")[0] : "";
  });
  const [period, setPeriod] = useState(stats?.warrantyPeriod || 0);
  const [saving, setSaving] = useState(false);

  const addPeriod = (days) => setPeriod((prev) => Math.max(0, prev + days));

  const calcExpiry = () => {
    if (!startDateStr || !period) return null;
    const d = new Date(startDateStr + "T00:00:00Z");
    return new Date(d.getTime() + period * 86400000);
  };
  const expiry = calcExpiry();

  const handleSave = async () => {
    setSaving(true);
    try {
      await api.put(`/devices/${id}`, {
        device_stats: {
          warrantyActive: active,
          warrantyStart: startDateStr ? startDateStr + "T00:00:00Z" : stats?.warrantyStart,
          warrantyPeriod: period,
        }
      });
      await onSaved();
      onClose();
    } finally { setSaving(false); }
  };

  return (
    <SectionModal open={open} title="Edit Warranty" onCancel={onClose} onSave={handleSave} saving={saving} size="max-w-md">
      <ModalField label="Warranty Status">
        <ToggleSwitch value={active} onChange={setActive} onLabel="Active" offLabel="Voided" />
        {!active && (
          <p className="mt-2 text-xs" style={{ color: "var(--danger-text)" }}>
            Warning: Setting this to Voided will mark the warranty as void, even if it has not yet expired.
          </p>
        )}
      </ModalField>
      <ModalField label="Start Date">
        <input type="date" value={startDateStr} onChange={(e) => setStartDateStr(e.target.value)} className="w-full px-3 py-2 rounded-md text-sm border" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }} />
      </ModalField>
      <ModalField label="Period (days)" hint={expiry ? `Expiry: ${formatDateNice(expiry)}` : undefined}>
        <div className="flex gap-2 items-center mb-2">
          <input
            type="number"
            min={0}
            value={period}
            onChange={(e) => setPeriod(Math.max(0, parseInt(e.target.value) || 0))}
            className="w-32 px-3 py-2 rounded-md text-sm border"
            style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
          />
          <span className="text-xs" style={{ color: "var(--text-muted)" }}>days</span>
        </div>
        <div className="flex gap-2 flex-wrap">
          {[{ label: "+1 Month", days: 30 }, { label: "+6 Months", days: 180 }, { label: "+1 Year", days: 365 }].map((q) => (
            <button key={q.label} type="button" onClick={() => addPeriod(q.days)} className="px-3 py-1.5 text-xs rounded-md border cursor-pointer hover:opacity-80" style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}>
              {q.label}
            </button>
          ))}
        </div>
      </ModalField>
    </SectionModal>
  );
}

// --- Log level styles ---
const LOG_LEVEL_STYLES = {
  INFO: { bg: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" },
  WARN: { bg: "#3d2e00", color: "#fbbf24" },
  ERROR: { bg: "var(--danger-bg)", color: "var(--danger-text)" },
};

// --- Date / time helpers ---

function parseFirestoreDate(str) {
  if (!str) return null;
  // Handle Firestore Timestamp objects serialised as {_seconds, _nanoseconds} or {seconds, nanoseconds}
  if (typeof str === "object") {
    const secs = str._seconds ?? str.seconds;
    if (typeof secs === "number") return new Date(secs * 1000);
    return null;
  }
  const cleaned = str.replace(" at ", " ").replace("UTC+0000", "UTC").replace(/UTC\+(\d{4})/, "UTC");
  const d = new Date(cleaned);
  return isNaN(d.getTime()) ? null : d;
}

function formatDate(str) {
  const d = parseFirestoreDate(str);
  if (!d) return str || "-";
  const day = d.getUTCDate().toString().padStart(2, "0");
  const month = (d.getUTCMonth() + 1).toString().padStart(2, "0");
  const year = d.getUTCFullYear();
  return `${day}-${month}-${year}`;
}

function addDays(date, days) {
  return new Date(date.getTime() + days * 86400000);
}

function formatDateNice(d) {
  if (!d) return "-";
  const months = ["January","February","March","April","May","June","July","August","September","October","November","December"];
  return `${d.getUTCDate()} ${months[d.getUTCMonth()]} ${d.getUTCFullYear()}`;
}

function daysUntil(targetDate) {
  const now = new Date();
  const diff = targetDate.getTime() - now.getTime();
  return Math.ceil(diff / 86400000);
}

function formatRelativeTime(days) {
  if (days < 0) return null;
  if (days === 0) return "today";
  if (days === 1) return "in 1 day";
  if (days < 30) return `in ${days} days`;
  const months = Math.floor(days / 30);
  const remainDays = days % 30;
  if (remainDays === 0) return `in ${months} month${months > 1 ? "s" : ""}`;
  return `in ${months} month${months > 1 ? "s" : ""} and ${remainDays} day${remainDays > 1 ? "s" : ""}`;
}

function daysToDisplay(days) {
  if (days >= 365 && days % 365 === 0) return `${days / 365} year${days / 365 > 1 ? "s" : ""} (${days} days)`;
  if (days >= 30) {
    const months = Math.floor(days / 30);
    const rem = days % 30;
    if (rem === 0) return `${months} month${months > 1 ? "s" : ""} (${days} days)`;
    return `${days} days (~${months} month${months > 1 ? "s" : ""})`;
  }
  return `${days} days`;
}

function formatTimestamp(str) {
  if (!str) return "-";
  const d = parseFirestoreDate(str);
  if (d) {
    return `${d.getUTCHours().toString().padStart(2, "0")}:${d.getUTCMinutes().toString().padStart(2, "0")}`;
  }
  if (/^\d{1,2}:\d{2}/.test(str)) return str;
  const match = str.match(/(\d{1,2}):(\d{2})/);
  if (match) return `${match[1].padStart(2, "0")}:${match[2]}`;
  return str;
}

function msToSeconds(ms) {
  if (ms == null) return "-";
  return `${(ms / 1000).toFixed(1)}s`;
}

const UNAVAILABLE_INFO = "Unavailable info";
const LOG_LEVEL_META = {
  0: { label: "Disabled", description: "No logs", color: "var(--text-muted)" },
  1: { label: "Error", description: "Errors only", color: "var(--danger-text)" },
  2: { label: "Warning", description: "Warnings and errors", color: "#f59e0b" },
  3: { label: "Info", description: "Info, warnings, and errors", color: "#63b3ed" },
  4: { label: "Debug", description: "Debug logs. Really high level (full debugging)", color: "#9f7aea" },
  5: { label: "Verbose", description: "Nearly every command gets printed", color: "#20c997" },
};

function hasValue(value) {
  if (value === null || value === undefined) return false;
  if (typeof value === "string") return value.trim().length > 0;
  if (Array.isArray(value)) return value.length > 0;
  return true;
}

function formatBoolean(value, trueText, falseText, missingText = UNAVAILABLE_INFO) {
  if (typeof value !== "boolean") return missingText;
  return value ? trueText : falseText;
}

function formatCount(value, singular, plural = `${singular}s`) {
  if (!Number.isFinite(value)) return UNAVAILABLE_INFO;
  const count = Number(value);
  if (count === 1) return `1 ${singular}`;
  return `${count} ${plural}`;
}

const ORDINAL_NAMES = ["First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth", "Ninth", "Tenth"];

function getStrikerSize(ms) {
  if (!Number.isFinite(ms)) return null;
  if (ms <= 89) return 1;
  if (ms <= 94) return 2;
  if (ms <= 99) return 3;
  if (ms <= 109) return 4;
  if (ms <= 119) return 5;
  return 6;
}

function formatConnectedBells(value) {
  if (!Number.isFinite(value)) return <span className="device-value-secondary">{UNAVAILABLE_INFO}</span>;
  const count = Number(value);
  return count === 1 ? "1 bell" : `${count} Bells`;
}

function formatOutput(value, noun = "Output") {
  if (!Number.isFinite(value)) return UNAVAILABLE_INFO;
  return `${noun} ${value}`;
}

function formatBell(value) {
  if (!Number.isFinite(value)) return UNAVAILABLE_INFO;
  if (Number(value) === 0) return "Disabled";
  return `Bell ${value}`;
}

function formatOutputWithDisabledZero(value, noun = "Output") {
  if (!Number.isFinite(value)) return UNAVAILABLE_INFO;
  if (Number(value) === 0) return "Disabled";
  return `${noun} ${value}`;
}

function formatLogLevel(levelRaw) {
  if (!Number.isFinite(levelRaw)) return null;
  const level = Number(levelRaw);
  const meta = LOG_LEVEL_META[level];
  if (!meta) return null;
  return {
    text: `(${level}) ${meta.label}`,
    description: meta.description,
    color: meta.color,
  };
}

function formatLocale(locale) {
  if (!hasValue(locale)) return UNAVAILABLE_INFO;
  if (locale === "all") return "Global";
  if (locale === "orthodox") return "Orthodox";
  if (locale === "catholic") return "Catholic";
  return locale;
}

function formatMsWithSeconds(ms) {
  if (!Number.isFinite(ms)) return UNAVAILABLE_INFO;
  const seconds = (Number(ms) / 1000).toFixed(1);
  return (
    <span>
      {seconds} sec{" "}
      <span className="device-value-secondary">({ms} ms)</span>
    </span>
  );
}

function formatDaysVerbose(days) {
  if (!Number.isFinite(days) || days <= 0) return null;
  if (days >= 30) {
    const months = Math.floor(days / 30);
    return (
      <span>
        {months} {months === 1 ? "Month" : "Months"}
        <span className="device-value-secondary"> · {days} days</span>
      </span>
    );
  }
  return `${days} ${days === 1 ? "Day" : "Days"}`;
}

function formatAnyDateVerbose(value) {
  if (!hasValue(value)) return UNAVAILABLE_INFO;
  const parsed = parseFirestoreDate(value);
  if (parsed) return formatDateNice(parsed);
  const fromNative = new Date(value);
  if (!isNaN(fromNative.getTime())) return formatDateNice(fromNative);
  return UNAVAILABLE_INFO;
}

function formatSilencePeriod(isEnabled, fromValue, toValue) {
  if (typeof isEnabled !== "boolean") return UNAVAILABLE_INFO;
  if (!isEnabled) return "Disabled";
  const hasFrom = hasValue(fromValue);
  const hasTo = hasValue(toValue);
  if (hasFrom !== hasTo) return "Not Set Correctly";
  if (!hasFrom && !hasTo) return "Not Set Correctly";
  return (
    <span>
      From <strong>{formatTimestamp(fromValue)}</strong> to <strong>{formatTimestamp(toValue)}</strong>
    </span>
  );
}

function formatSilenceRange(isEnabled, fromValue, toValue) {
  if (typeof isEnabled !== "boolean") return UNAVAILABLE_INFO;
  if (!isEnabled) return "Disabled";
  const hasFrom = hasValue(fromValue);
  const hasTo = hasValue(toValue);
  if (hasFrom !== hasTo) return "Not Set Correctly";
  if (!hasFrom && !hasTo) return "Not Set Correctly";
  return `${formatTimestamp(fromValue)} - ${formatTimestamp(toValue)}`;
}

function playbackPlaceholderForId(seedValue) {
  const seed = String(seedValue || "device");
  let hash = 0;
  for (let i = 0; i < seed.length; i += 1) {
    hash = (hash * 31 + seed.charCodeAt(i)) >>> 0;
  }
  return 130 + (hash % 121);
}

// --- Coordinates helpers ---

function parseCoordinates(coordStr) {
  if (!coordStr) return null;
  // Support plain { lat, lng } objects (from API returning GeoPoint as dict)
  if (typeof coordStr === "object" && coordStr !== null) {
    const lat = parseFloat(coordStr.lat ?? coordStr.latitude);
    const lng = parseFloat(coordStr.lng ?? coordStr.longitude);
    if (!isNaN(lat) && !isNaN(lng)) return { lat, lng };
    return null;
  }
  const numbers = coordStr.match(/-?\d+(?:\.\d+)?/g);
  if (numbers && numbers.length >= 2) {
    let lat = parseFloat(numbers[0]);
    let lng = parseFloat(numbers[1]);
    if (!isNaN(lat) && !isNaN(lng)) {
      // Restore sign from direction letters (legacy string format)
      if (/\bS\b/.test(coordStr)) lat = -Math.abs(lat);
      if (/\bW\b/.test(coordStr)) lng = -Math.abs(lng);
      return { lat, lng };
    }
  }
  return null;
}

function formatCoordinates(coords) {
  if (!coords) return "-";
  const latDir = coords.lat >= 0 ? "N" : "S";
  const lngDir = coords.lng >= 0 ? "E" : "W";
  return `${Math.abs(coords.lat).toFixed(7)}° ${latDir}, ${Math.abs(coords.lng).toFixed(7)}° ${lngDir}`;
}

function getNearestPlaceLabel(data) {
  const addr = data?.address || {};
  const name = addr.city || addr.town || addr.village || addr.hamlet || addr.municipality || data?.display_name?.split(",")[0] || "";
  const region = addr.state || addr.county || "";
  const country = addr.country || "";
  return [name, region, country].filter(Boolean).join(", ");
}

// --- Device Logs Panel ---

function DeviceLogsPanel({ deviceSerial }) {
  const [logs, setLogs] = useState([]);
  const [loading, setLoading] = useState(false);
  const [levelFilter, setLevelFilter] = useState("");
  const [searchText, setSearchText] = useState("");
  const [autoRefresh, setAutoRefresh] = useState(false);
  const [liveLogs, setLiveLogs] = useState([]);
  const limit = 25;

  const fetchLogs = useCallback(async () => {
    if (!deviceSerial) return;
    setLoading(true);
    try {
      const params = new URLSearchParams();
      if (levelFilter) params.set("level", levelFilter);
      if (searchText) params.set("search", searchText);
      params.set("limit", limit.toString());
      params.set("offset", "0");
      const data = await api.get(`/mqtt/logs/${deviceSerial}?${params}`);
      setLogs(data.logs || []);
    } catch {
      // silently fail in compact view
    } finally {
      setLoading(false);
    }
  }, [deviceSerial, levelFilter, searchText]);

  useEffect(() => {
    if (deviceSerial) fetchLogs();
  }, [deviceSerial, fetchLogs]);

  useEffect(() => {
    if (!autoRefresh || !deviceSerial) return;
    const interval = setInterval(fetchLogs, 5000);
    return () => clearInterval(interval);
  }, [autoRefresh, deviceSerial, fetchLogs]);

  const handleWsMessage = useCallback(
    (data) => {
      if (data.type === "logs" && data.device_serial === deviceSerial) {
        const logEntry = {
          id: Date.now(),
          level: data.payload?.level?.includes("EROR") ? "ERROR" : data.payload?.level?.includes("WARN") ? "WARN" : "INFO",
          message: data.payload?.message || "",
          received_at: new Date().toISOString(),
          _live: true,
        };
        setLiveLogs((prev) => [logEntry, ...prev].slice(0, 50));
      }
    },
    [deviceSerial]
  );

  useMqttWebSocket({ enabled: true, onMessage: handleWsMessage });

  const allLogs = [...liveLogs.filter((l) => {
    if (levelFilter && l.level !== levelFilter) return false;
    if (searchText && !l.message.toLowerCase().includes(searchText.toLowerCase())) return false;
    return true;
  }), ...logs];

  return (
    <SectionCard title="Latest Logs">
      {/* Controls */}
      <div style={{ display: "flex", flexWrap: "wrap", gap: "0.6rem", alignItems: "center", marginBottom: "1rem" }}>
        <select
          value={levelFilter}
          onChange={(e) => setLevelFilter(e.target.value)}
          className="px-2 rounded-md text-xs border"
          style={{ height: 30, backgroundColor: "var(--bg-primary)", color: "var(--text-primary)", borderColor: "var(--border-primary)" }}
        >
          <option value="">All Levels</option>
          <option value="INFO">INFO</option>
          <option value="WARN">WARN</option>
          <option value="ERROR">ERROR</option>
        </select>
        <input
          type="text"
          value={searchText}
          onChange={(e) => setSearchText(e.target.value)}
          placeholder="Search log messages..."
          className="flex-1 min-w-32 px-2 rounded-md text-xs border"
          style={{ height: 30, minWidth: 120, backgroundColor: "var(--bg-primary)", color: "var(--text-primary)", borderColor: "var(--border-primary)" }}
        />
        <ToggleSwitch value={autoRefresh} onChange={setAutoRefresh} onLabel="Auto-refresh" offLabel="Auto-refresh" />
        <button
          onClick={fetchLogs}
          className="px-2.5 rounded-md text-xs border hover:opacity-80 cursor-pointer"
          style={{ height: 30, borderColor: "var(--badge-blue-bg)", backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" }}
        >
          Refresh
        </button>
      </div>

      {/* Log table */}
      {loading && logs.length === 0 ? (
        <p className="text-xs" style={{ color: "var(--text-muted)" }}>Loading...</p>
      ) : allLogs.length === 0 ? (
        <p className="text-xs" style={{ color: "var(--text-muted)" }}>No logs found.</p>
      ) : (
        <div className="rounded-md overflow-hidden border" style={{ borderColor: "var(--border-primary)" }}>
          <div className="overflow-x-auto" style={{ maxHeight: 220, overflowY: "auto" }}>
            <table className="w-full text-xs">
              <thead style={{ position: "sticky", top: 0, zIndex: 1 }}>
                <tr style={{ backgroundColor: "var(--bg-primary)", borderBottom: "1px solid var(--border-primary)" }}>
                  <th className="px-3 py-2 text-left font-medium w-40" style={{ color: "var(--text-secondary)" }}>Time</th>
                  <th className="px-3 py-2 text-left font-medium w-16" style={{ color: "var(--text-secondary)" }}>Level</th>
                  <th className="px-3 py-2 text-left font-medium" style={{ color: "var(--text-secondary)" }}>Message</th>
                </tr>
              </thead>
              <tbody>
                {allLogs.slice(0, limit).map((log, index) => {
                  const style = LOG_LEVEL_STYLES[log.level] || LOG_LEVEL_STYLES.INFO;
                  return (
                    <tr
                      key={log._live ? `live-${log.id}` : log.id}
                      style={{
                        borderBottom: index < Math.min(allLogs.length, limit) - 1 ? "1px solid var(--border-primary)" : "none",
                        backgroundColor: log._live ? "rgba(116, 184, 22, 0.05)" : "transparent",
                      }}
                    >
                      <td className="px-3 py-2 font-mono" style={{ color: "var(--text-muted)" }}>
                        {log.received_at?.replace("T", " ").substring(0, 19)}
                        {log._live && <span className="ml-1" style={{ color: "var(--accent)" }}>LIVE</span>}
                      </td>
                      <td className="px-3 py-2">
                        <span className="px-1.5 py-0.5 text-xs rounded-full" style={{ backgroundColor: style.bg, color: style.color }}>
                          {log.level}
                        </span>
                      </td>
                      <td className="px-3 py-2 font-mono" style={{ color: "var(--text-primary)" }}>
                        {log.message}
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>
      )}
    </SectionCard>
  );
}

// --- Main component ---

const TAB_DEFS = [
  { id: "dashboard", label: "Dashboard", tone: "dashboard" },
  { id: "general", label: "General Information", tone: "general" },
  { id: "bells", label: "Bell Mechanisms", tone: "bells" },
  { id: "clock", label: "Clock & Alerts", tone: "clock" },
  { id: "warranty", label: "Warranty & Subscription", tone: "warranty" },
  { id: "manage", label: "Manage", tone: "manage" },
  { id: "control", label: "Control", tone: "control" },
];

function calcPeriodProgress(start, end) {
  if (!start || !end) return null;
  const total = end.getTime() - start.getTime();
  if (total <= 0) return null;
  const elapsed = Date.now() - start.getTime();
  return Math.max(0, Math.min(100, (elapsed / total) * 100));
}

function calcMaintenanceProgress(lastDate, periodDays) {
  if (!lastDate || !periodDays) return null;
  const total = periodDays * 86400000;
  const elapsed = Date.now() - lastDate.getTime();
  return Math.max(0, Math.min(100, (elapsed / total) * 100));
}

// ─── Customer Assign Modal ────────────────────────────────────────────────────
function CustomerAssignModal({ deviceId, onSelect, onCancel }) {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState([]);
  const [searching, setSearching] = useState(false);
  const inputRef = useRef(null);

  useEffect(() => { inputRef.current?.focus(); }, []);

  const search = useCallback(async (q) => {
    setSearching(true);
    try {
      const data = await api.get(`/devices/${deviceId}/customer-search?q=${encodeURIComponent(q)}`);
      setResults(data.results || []);
    } catch {
      setResults([]);
    } finally {
      setSearching(false);
    }
  }, [deviceId]);

  useEffect(() => {
    const t = setTimeout(() => search(query), 250);
    return () => clearTimeout(t);
  }, [query, search]);

  return (
    <div className="fixed inset-0 z-50 flex items-center justify-center p-4" style={{ backgroundColor: "rgba(0,0,0,0.6)" }}>
      <div
        className="rounded-xl border w-full max-w-lg flex flex-col"
        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)", maxHeight: "80vh" }}
      >
        <div className="flex items-center justify-between px-6 pt-5 pb-4 border-b" style={{ borderColor: "var(--border-secondary)" }}>
          <h3 className="text-base font-semibold" style={{ color: "var(--text-heading)" }}>Assign to Customer</h3>
          <button type="button" onClick={onCancel} className="text-lg leading-none hover:opacity-70 cursor-pointer" style={{ color: "var(--text-muted)" }}>&#x2715;</button>
        </div>
        <div className="px-6 pt-4 pb-2">
          <div style={{ position: "relative" }}>
            <input
              ref={inputRef}
              type="text"
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              placeholder="Search by name, email, phone, org, tags…"
              className="w-full px-3 py-2 rounded-md text-sm border"
              style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
            />
            {searching && (
              <span className="absolute right-3 top-1/2 -translate-y-1/2 text-xs" style={{ color: "var(--text-muted)" }}>…</span>
            )}
          </div>
        </div>
        <div className="overflow-y-auto flex-1 px-6 pb-4">
          <div className="rounded-md border overflow-hidden" style={{ borderColor: "var(--border-secondary)", minHeight: 60 }}>
            {results.length === 0 ? (
              <p className="px-3 py-3 text-sm" style={{ color: "var(--text-muted)" }}>
                {searching ? "Searching…" : query ? "No customers found." : "Type to search customers…"}
              </p>
            ) : (
              results.map((c) => (
                <button
                  key={c.id}
                  type="button"
                  onClick={() => onSelect(c)}
                  className="w-full text-left px-3 py-2.5 text-sm hover:opacity-80 cursor-pointer border-b last:border-b-0 transition-colors"
                  style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-secondary)", color: "var(--text-primary)" }}
                >
                  <span className="font-medium block">
                    {[c.name, c.surname].filter(Boolean).join(" ")}
                    {c.city && (
                      <>
                        <span className="mx-1.5" style={{ color: "var(--text-muted)", fontSize: "8px", verticalAlign: "middle" }}>●</span>
                        <span style={{ color: "var(--text-muted)", fontWeight: 400 }}>{c.city}</span>
                      </>
                    )}
                  </span>
                  {c.organization && (
                    <span className="text-xs block" style={{ color: "var(--text-muted)" }}>{c.organization}</span>
                  )}
                </button>
              ))
            )}
          </div>
        </div>
        <div className="flex justify-end px-6 py-4 border-t" style={{ borderColor: "var(--border-secondary)" }}>
          <button
            onClick={onCancel}
            className="px-4 py-1.5 text-sm rounded-md hover:opacity-80 cursor-pointer"
            style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)" }}
          >
            Cancel
          </button>
        </div>
      </div>
    </div>
  );
}

export default function DeviceDetail() {
  const { id } = useParams();
  const navigate = useNavigate();
  const { hasPermission } = useAuth();
  const canEdit = hasPermission("devices", "edit");

  const [device, setDevice] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState("");
  const [showDelete, setShowDelete] = useState(false);
  const [mqttStatus, setMqttStatus] = useState(null);
  const [locationName, setLocationName] = useState(null);
  const [activeTab, setActiveTab] = useState("dashboard");
  const [deviceUsers, setDeviceUsers] = useState([]);
  const [staffNotes, setStaffNotes] = useState("");
  const [editingNotes, setEditingNotes] = useState(false);
  const [savingNotes, setSavingNotes] = useState(false);
  const notesRef = useRef(null);
  const [usersLoading, setUsersLoading] = useState(false);
  const [unresolvedIssues, setUnresolvedIssues] = useState(0);
  const [notesPanelTab, setNotesPanelTab] = useState(null);
  const notesPanelRef = useRef(null);
  const [liveStrikeCounters, setLiveStrikeCounters] = useState(null);
  const [requestingStrikeCounters, setRequestingStrikeCounters] = useState(false);
  const lastStrikeRequestAtRef = useRef(0);
  const [hwProduct, setHwProduct] = useState(null);

  // --- Section edit modal open/close state ---
  const [editingLocation, setEditingLocation] = useState(false);
  const [editingAttributes, setEditingAttributes] = useState(false);
  const [editingLogging, setEditingLogging] = useState(false);
  const [editingMisc, setEditingMisc] = useState(false);
  const [editingBellOutputs, setEditingBellOutputs] = useState(false);
  const [editingClockSettings, setEditingClockSettings] = useState(false);
  const [editingAlerts, setEditingAlerts] = useState(false);
  const [editingBacklight, setEditingBacklight] = useState(false);
  const [editingSubscription, setEditingSubscription] = useState(false);
  const [editingWarranty, setEditingWarranty] = useState(false);
  const [qrTarget, setQrTarget] = useState(null);

  useEffect(() => {
    loadData();
  }, [id]);

  useEffect(() => {
    setLiveStrikeCounters(null);
    setRequestingStrikeCounters(false);
    lastStrikeRequestAtRef.current = 0;
  }, [id]);

  const loadData = async () => {
    setLoading(true);
    try {
      // Phase 1: load device from DB immediately — renders page without waiting for MQTT
      const d = await api.get(`/devices/${id}`);
      setDevice(d);
      if (d.staffNotes) setStaffNotes(d.staffNotes);
      if (Array.isArray(d.tags)) setTags(d.tags);
      setLoading(false);

      // Phase 2: fire async background fetches — do not block the render
      const deviceSN = d.serial_number || d.device_id;
      if (deviceSN) {
        api.get("/mqtt/status").then((mqttData) => {
          if (mqttData?.devices) {
            const match = mqttData.devices.find((s) => s.device_serial === deviceSN);
            setMqttStatus(match || null);
          }
        }).catch(() => {});
      }

      // Fetch owner customer details
      if (d.customer_id) {
        api.get(`/devices/${id}/customer`).then((res) => {
          setOwnerCustomer(res.customer || null);
        }).catch(() => setOwnerCustomer(null));
      } else {
        setOwnerCustomer(null);
      }

      setUsersLoading(true);
      api.get(`/devices/${id}/users`).then((data) => {
        setDeviceUsers(data.users || []);
      }).catch(() => {
        setDeviceUsers([]);
      }).finally(() => setUsersLoading(false));

      // Fetch manufacturing record + product catalog to resolve hw image
      if (deviceSN) {
        Promise.all([
          api.get(`/manufacturing/devices/${deviceSN}`).catch(() => null),
          api.get("/crm/products").catch(() => null),
        ]).then(([mfgItem, productsRes]) => {
          const hwType = mfgItem?.hw_type || "";
          if (!hwType) return;
          const products = productsRes?.products || [];
          const norm = (s) => (s || "").toLowerCase().replace(/[^a-z0-9]/g, "");
          const normHw = norm(hwType);
          const match = products.find(
            (p) => norm(p.name) === normHw || norm(p.sku) === normHw ||
                   norm(p.name).includes(normHw) || normHw.includes(norm(p.name))
          );
          if (match) setHwProduct(match);
        }).catch(() => {});
      }

      api.get(`/equipment/notes?device_id=${id}`).then((data) => {
        const issues = (data.notes || []).filter(
          (n) => (n.category === "issue" || n.category === "action_item") && n.status !== "completed"
        );
        setUnresolvedIssues(issues.length);
      }).catch(() => setUnresolvedIssues(0));

      const geoCoords = parseCoordinates(d.device_location_coordinates);
      if (geoCoords) {
        const nominatimUrl = `https://nominatim.openstreetmap.org/reverse?lat=${geoCoords.lat}&lon=${geoCoords.lng}&format=json&zoom=10&addressdetails=1`;
        const mapsCoUrl = `https://geocode.maps.co/reverse?lat=${geoCoords.lat}&lon=${geoCoords.lng}`;
        fetch(nominatimUrl, { headers: { Accept: "application/json", "Accept-Language": "en" } })
          .then((r) => (r.ok ? r.json() : Promise.reject(new Error("nominatim failed"))))
          .then((data) => setLocationName(getNearestPlaceLabel(data) || null))
          .catch(() => {
            fetch(mapsCoUrl)
              .then((r) => (r.ok ? r.json() : Promise.reject(new Error("maps.co failed"))))
              .then((data) => setLocationName(getNearestPlaceLabel(data) || null))
              .catch(() => setLocationName(null));
          });
      }
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };

  const handleDelete = async () => {
    try {
      await api.delete(`/devices/${id}`);
      navigate("/devices");
    } catch (err) {
      setError(err.message);
      setShowDelete(false);
    }
  };

  const saveStaffNotes = async (value) => {
    setSavingNotes(true);
    try {
      await api.put(`/devices/${id}`, { staffNotes: value });
      setStaffNotes(value);
    } catch {
      // silently fail
    } finally {
      setSavingNotes(false);
      setEditingNotes(false);
    }
  };

  // --- Device Notes handlers ---
  const handleAddNote = async () => {
    if (!newNoteText.trim()) return;
    setSavingNote(true);
    try {
      const data = await api.post(`/devices/${id}/notes`, {
        content: newNoteText.trim(),
        created_by: "admin",
      });
      setDeviceNotes((prev) => [data, ...prev]);
      setNewNoteText("");
      setAddingNote(false);
    } catch {} finally { setSavingNote(false); }
  };

  const handleUpdateNote = async (noteId) => {
    if (!editingNoteText.trim()) return;
    setSavingNote(true);
    try {
      const data = await api.put(`/devices/${id}/notes/${noteId}`, { content: editingNoteText.trim() });
      setDeviceNotes((prev) => prev.map((n) => n.id === noteId ? data : n));
      setEditingNoteId(null);
    } catch {} finally { setSavingNote(false); }
  };

  const handleDeleteNote = async (noteId) => {
    if (!window.confirm("Delete this note?")) return;
    try {
      await api.delete(`/devices/${id}/notes/${noteId}`);
      setDeviceNotes((prev) => prev.filter((n) => n.id !== noteId));
    } catch {}
  };

  // --- Tags handlers ---
  const handleAddTag = async (tag) => {
    const trimmed = tag.trim();
    if (!trimmed || tags.includes(trimmed)) return;
    const next = [...tags, trimmed];
    setSavingTags(true);
    try {
      await api.put(`/devices/${id}/tags`, { tags: next });
      setTags(next);
      setTagInput("");
    } catch {} finally { setSavingTags(false); }
  };

  const handleRemoveTag = async (tag) => {
    const next = tags.filter((t) => t !== tag);
    setSavingTags(true);
    try {
      await api.put(`/devices/${id}/tags`, { tags: next });
      setTags(next);
    } catch {} finally { setSavingTags(false); }
  };

  // --- Customer assign handlers ---
  const handleAssignCustomer = async (customer) => {
    setAssigningCustomer(true);
    try {
      await api.post(`/devices/${id}/assign-customer`, { customer_id: customer.id });
      setDevice((prev) => ({ ...prev, customer_id: customer.id }));
      setOwnerCustomer(customer);
      setShowAssignSearch(false);
      setCustomerSearch("");
      setCustomerResults([]);
    } catch {} finally { setAssigningCustomer(false); }
  };

  const handleUnassignCustomer = async () => {
    if (!window.confirm("Remove customer assignment?")) return;
    setAssigningCustomer(true);
    try {
      const cid = device?.customer_id;
      await api.delete(`/devices/${id}/assign-customer${cid ? `?customer_id=${cid}` : ""}`);
      setDevice((prev) => ({ ...prev, customer_id: "" }));
      setOwnerCustomer(null);
    } catch {} finally { setAssigningCustomer(false); }
  };

  const requestStrikeCounters = useCallback(async (force = false) => {
    if (!device?.device_id) return;
    const now = Date.now();
    if (!force && now - lastStrikeRequestAtRef.current < 10000) return;
    lastStrikeRequestAtRef.current = now;
    setRequestingStrikeCounters(true);
    try {
      await api.post(`/mqtt/command/${device.device_id}`, {
        cmd: "system_info",
        contents: {
          action: "report_status",
        },
      });
    } catch {
      setRequestingStrikeCounters(false);
    }
  }, [device?.device_id]);

  const handleDeviceMqttMessage = useCallback((data) => {
    if (!device?.device_id) return;
    if (data?.type !== "data" || data?.device_serial !== device.device_id) return;
    const counters = data?.payload?.payload?.strike_counters;
    if (Array.isArray(counters)) {
      setLiveStrikeCounters(counters);
      setRequestingStrikeCounters(false);
    }
  }, [device?.device_id]);

  useMqttWebSocket({ enabled: true, onMessage: handleDeviceMqttMessage });

  useEffect(() => {
    if (activeTab === "bells" || activeTab === "warranty") {
      requestStrikeCounters(false);
    }
  }, [activeTab, requestStrikeCounters]);

  // --- Control tab state (MUST be before early returns) ---
  const [ctrlFirmwareChannel, setCtrlFirmwareChannel] = useState("stable");
  const [ctrlHttpEnabled, setCtrlHttpEnabled] = useState(true);
  const [ctrlCustomCmd, setCtrlCustomCmd] = useState("");
  const [ctrlCustomResponse, setCtrlCustomResponse] = useState("");
  const [ctrlMelodies, setCtrlMelodies] = useState([]);
  const [ctrlMelodiesLoading, setCtrlMelodiesLoading] = useState(false);
  const [ctrlDeviceTime, setCtrlDeviceTime] = useState(() => {
    const now = new Date();
    return now.toISOString().slice(0, 16);
  });
  const [ctrlTimezone, setCtrlTimezone] = useState("UTC");
  const [ctrlDst, setCtrlDst] = useState(false);
  const [ctrlCmdHistory, setCtrlCmdHistory] = useState([]);
  const [ctrlCmdHistoryLoading, setCtrlCmdHistoryLoading] = useState(false);
  const [ctrlExpandedCmd, setCtrlExpandedCmd] = useState(null);
  const [ctrlCmdAutoRefresh, setCtrlCmdAutoRefresh] = useState(false);
  const [ctrlCustomHeight, setCtrlCustomHeight] = useState(160);

  const fetchCtrlCmdHistory = useCallback(async () => {
    if (!device?.device_id) return;
    setCtrlCmdHistoryLoading(true);
    try {
      const data = await api.get(`/mqtt/commands/${device.device_id}?limit=50`);
      setCtrlCmdHistory(data.commands || []);
    } catch {
      // silently fail
    } finally {
      setCtrlCmdHistoryLoading(false);
    }
  }, [device?.device_id]);

  const fetchCtrlMelodies = useCallback(async () => {
    setCtrlMelodiesLoading(true);
    setCtrlMelodies([]);
    // Placeholder: will send MQTT list_melodies command
    setTimeout(() => setCtrlMelodiesLoading(false), 500);
  }, []);

  useEffect(() => {
    if (!ctrlCmdAutoRefresh || !device?.device_id) return;
    const interval = setInterval(fetchCtrlCmdHistory, 5000);
    return () => clearInterval(interval);
  }, [ctrlCmdAutoRefresh, device?.device_id, fetchCtrlCmdHistory]);

  // --- Device Notes state (MUST be before early returns) ---
  const [deviceNotes, setDeviceNotes] = useState([]);
  const [notesLoaded, setNotesLoaded] = useState(false);
  const [addingNote, setAddingNote] = useState(false);
  const [newNoteText, setNewNoteText] = useState("");
  const [savingNote, setSavingNote] = useState(false);
  const [editingNoteId, setEditingNoteId] = useState(null);
  const [editingNoteText, setEditingNoteText] = useState("");

  const loadDeviceNotes = useCallback(async () => {
    try {
      const data = await api.get(`/devices/${id}/notes`);
      setDeviceNotes(data.notes || []);
      setNotesLoaded(true);
    } catch {
      setNotesLoaded(true);
    }
  }, [id]);

  useEffect(() => {
    if (id) loadDeviceNotes();
  }, [id, loadDeviceNotes]);

  // --- Tags state (MUST be before early returns) ---
  const [tags, setTags] = useState([]);
  const [tagInput, setTagInput] = useState("");
  const [savingTags, setSavingTags] = useState(false);

  // --- Customer assign state (MUST be before early returns) ---
  const [assigningCustomer, setAssigningCustomer] = useState(false);
  const [showAssignSearch, setShowAssignSearch] = useState(false);
  const [ownerCustomer, setOwnerCustomer] = useState(null);

  // --- User assignment state (MUST be before early returns) ---
  const [showUserSearch, setShowUserSearch] = useState(false);
  const [userSearchQuery, setUserSearchQuery] = useState("");
  const [userSearchResults, setUserSearchResults] = useState([]);
  const [userSearching, setUserSearching] = useState(false);
  const [addingUser, setAddingUser] = useState(null);
  const [removingUser, setRemovingUser] = useState(null);
  const userSearchInputRef = useRef(null);

  const searchUsers = useCallback(async (q) => {
    setUserSearching(true);
    try {
      const data = await api.get(`/devices/${id}/user-search?q=${encodeURIComponent(q)}`);
      setUserSearchResults(data.results || []);
    } catch {
      setUserSearchResults([]);
    } finally {
      setUserSearching(false);
    }
  }, [id]);

  useEffect(() => {
    if (!showUserSearch) return;
    const t = setTimeout(() => searchUsers(userSearchQuery), 250);
    return () => clearTimeout(t);
  }, [userSearchQuery, searchUsers, showUserSearch]);

  if (loading) return <div className="text-center py-8" style={{ color: "var(--text-muted)" }}>Loading...</div>;
  if (error) return (
    <div className="text-sm rounded-md p-3 border" style={{ backgroundColor: "var(--danger-bg)", borderColor: "var(--danger)", color: "var(--danger-text)" }}>
      {error}
    </div>
  );
  if (!device) return null;

  const attr = device.device_attributes || {};
  const clock = attr.clockSettings || {};
  const net = attr.networkSettings || {};
  const sub = device.device_subscription || {};
  const stats = device.device_stats || {};
  const coords = parseCoordinates(device.device_location_coordinates);
  const isOnline = mqttStatus ? mqttStatus.online : device.is_Online;

  const subscrStart = parseFirestoreDate(sub.subscrStart);
  const subscrEnd = subscrStart && sub.subscrDuration ? addDays(subscrStart, sub.subscrDuration) : null;
  const subscrDaysLeft = subscrEnd ? daysUntil(subscrEnd) : null;
  const subscrProgress = calcPeriodProgress(subscrStart, subscrEnd);

  const warrantyStart = parseFirestoreDate(stats.warrantyStart);
  const warrantyEnd = warrantyStart && stats.warrantyPeriod ? addDays(warrantyStart, stats.warrantyPeriod) : null;
  const warrantyDaysLeft = warrantyEnd ? daysUntil(warrantyEnd) : null;
  const warrantyProgress = calcPeriodProgress(warrantyStart, warrantyEnd);

  const maintainedOn = parseFirestoreDate(stats.maintainedOn);
  const nextMaintenance = maintainedOn && stats.maintainancePeriod ? addDays(maintainedOn, stats.maintainancePeriod) : null;
  const maintenanceDaysLeft = nextMaintenance ? daysUntil(nextMaintenance) : null;
  const maintenanceProgress = calcMaintenanceProgress(maintainedOn, stats.maintainancePeriod);
  const staticIpAddress = Array.isArray(net.ipAddress) ? net.ipAddress.filter(Boolean).join(".") : "";
  const hammerStrikesFromLive = Array.isArray(liveStrikeCounters)
    ? liveStrikeCounters.reduce((sum, n) => (Number.isFinite(n) ? sum + n : sum), 0)
    : null;
  const randomPlaybacks = playbackPlaceholderForId(id || device.device_id || device.id);

  const hwVariant = hwProduct?.name || "VesperPlus";
  const hwImage = hwProduct?.photo_url ? `/api${hwProduct.photo_url}` : "/devices/VesperPlus.png";

  const locationCard = (
    <section className="device-section-card">
      {coords ? (
        <div className="device-location-grid">
          <div>
            <div className="device-section-card__title-row">
              <h2 className="device-section-card__title">Device Location</h2>
              {canEdit && (
                <button type="button" onClick={() => setEditingLocation(true)} className="section-edit-btn" title="Edit Device Location">
                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="13" height="13" aria-hidden="true">
                    <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
                    <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
                  </svg>
                  Edit
                </button>
              )}
            </div>
            <FieldRow columns={1}>
              <Field label="Location Name">{device.device_location}</Field>
              <Field label="Coordinates">
                <div className="flex flex-wrap items-center gap-2">
                  <span>{formatCoordinates(coords)}</span>
                  <a
                    href={`https://www.google.com/maps?q=${coords.lat},${coords.lng}`}
                    target="_blank"
                    rel="noopener noreferrer"
                    className="px-2 py-0.5 text-xs rounded-md inline-block"
                    style={{ backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" }}
                  >
                    Open in Maps
                  </a>
                </div>
              </Field>
              <Field label="Nearest Place">{locationName || "-"}</Field>
            </FieldRow>
          </div>
          <div style={{ padding: "0 0 0 0", display: "flex", flex: 1 }}>
            <div className="device-map-wrap rounded-md overflow-hidden border" style={{ boxShadow: "0 4px 24px rgba(0,0,0,0.4)", borderColor: "var(--border-primary)", flex: 1, minHeight: 260 }}>
              <MapContainer center={[coords.lat, coords.lng]} zoom={13} style={{ height: "100%", width: "100%" }} scrollWheelZoom={false}>
                <TileLayer
                  attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
                  url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                />
                <Marker position={[coords.lat, coords.lng]} />
              </MapContainer>
            </div>
          </div>
        </div>
      ) : (
        <div>
          <div className="device-section-card__title-row">
            <h2 className="device-section-card__title">Device Location</h2>
            {canEdit && (
              <button type="button" onClick={() => setEditingLocation(true)} className="section-edit-btn" title="Edit Device Location">
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" width="13" height="13" aria-hidden="true">
                  <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" />
                  <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" />
                </svg>
                Edit
              </button>
            )}
          </div>
          <FieldRow columns={1}>
            <Field label="Location Name">{device.device_location}</Field>
            <Field label="Coordinates">{device.device_location_coordinates || "-"}</Field>
          </FieldRow>
        </div>
      )}
    </section>
  );

  // Warranty status label — Void takes priority over period calculation
  const warrantyIsVoid = stats.warrantyActive === false;
  const warrantyStatusLabel = warrantyIsVoid
    ? "Warranty Void"
    : warrantyDaysLeft !== null
      ? (warrantyDaysLeft > 0 ? "Currently Active" : "Expired")
      : (stats.warrantyActive ? "Currently Active" : "Expired");
  const warrantyStatusColor = warrantyIsVoid
    ? "#f59e0b"
    : (warrantyDaysLeft === null ? stats.warrantyActive : warrantyDaysLeft > 0)
      ? "var(--success-text)"
      : "var(--danger-text)";

  // Compact subscription duration — no days, clean units only
  function subscrDurationLabel(days) {
    if (!days) return null;
    if (days % 365 === 0) return `${days / 365} year${days / 365 > 1 ? "s" : ""}`;
    if (days % 30 === 0) return `${days / 30} month${days / 30 > 1 ? "s" : ""}`;
    return `${days} days`;
  }

  const dashboardTab = (
    <div className="device-tab-stack">

      {/* ── Hero row: main card + notes card side-by-side ─────────────── */}
      <div className="db-hero-row">

        {/* ── Main info card ── */}
        <section className="db-hero-card">

          {/* 1. Product photo */}
          <div className={`db-hero-photo${isOnline ? " db-hero-photo--online" : " db-hero-photo--offline"}`}>
            <img src={hwImage} alt={hwVariant} className="db-hero-photo__img" />
          </div>

          <div className="db-hero-divider" />

          {/* 2. Main info — Serial / Hardware / Location */}
          <div className="db-hero-section">
            <div className="db-row">
              <div className="db-info-field">
                <span className="db-info-label">SERIAL NUMBER</span>
                <span className="db-info-value" style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
                  {device.serial_number || device.device_id || "-"}
                  {(device.serial_number || device.device_id) && (
                    <button
                      type="button"
                      title="Show QR Code"
                      onClick={() => setQrTarget(device.serial_number || device.device_id)}
                      className="cursor-pointer hover:opacity-70 transition-opacity"
                      style={{ color: "var(--text-muted)", background: "none", border: "none", padding: 0, lineHeight: 1 }}
                    >
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                        <rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
                        <path d="M14 14h.01M14 18h.01M18 14h.01M18 18h.01M21 14h.01M21 21h.01M14 21h.01"/>
                      </svg>
                    </button>
                  )}
                </span>
              </div>
            </div>
            <div className="db-row">
              <div className="db-info-field">
                <span className="db-info-label">HARDWARE VARIANT</span>
                <span className="db-info-value">{hwVariant}</span>
              </div>
            </div>
            <div className="db-row">
              <div className="db-info-field">
                <span className="db-info-label">LOCATION</span>
                <span className="db-info-value">{device.device_location || "-"}</span>
              </div>
            </div>
          </div>

          <div className="db-hero-divider" />

          {/* 3. Subscription / Warranty */}
          <div className="db-hero-section db-hero-section--sub">
            {/* Row 1: Tier left, Warranty Status right */}
            <div className="db-row db-sub-header-row">
              <div className="db-info-field">
                <span className="db-info-label">SUBSCRIPTION TIER</span>
                <span className="db-info-value">
                  <span style={{ color: "var(--badge-blue-text)" }}>{sub.subscrTier || "-"}</span>
                  {sub.subscrDuration ? (
                    <><span style={{ color: "var(--text-muted)", margin: "0 0.35rem" }}>·</span>
                    <span style={{ color: "var(--text-secondary)" }}>{subscrDurationLabel(sub.subscrDuration)}</span></>
                  ) : null}
                </span>
              </div>
              <div className="db-info-field">
                <span className="db-info-label">WARRANTY STATUS</span>
                <span className="db-info-value" style={{ color: warrantyStatusColor }}>{warrantyStatusLabel}</span>
              </div>
            </div>

            {/* Row 2: Subscription progress */}
            <div className="db-row">
              <div className="db-info-field">
                <span className="db-info-label">SUBSCRIPTION PROGRESS</span>
                <div className="db-progress-track">
                  <div className="db-progress-fill" style={{ width: `${subscrProgress ?? 0}%`, backgroundColor: "var(--accent)" }} />
                </div>
              </div>
            </div>

            {/* Row 3: Warranty progress */}
            <div className="db-row">
              <div className="db-info-field">
                <span className="db-info-label">WARRANTY PROGRESS</span>
                <div className="db-progress-track">
                  <div className="db-progress-fill" style={{
                    width: `${warrantyProgress ?? 0}%`,
                    backgroundColor: (warrantyDaysLeft === null ? stats.warrantyActive : warrantyDaysLeft > 0) ? "#B86716" : "var(--danger)"
                  }} />
                </div>
              </div>
            </div>
          </div>

          <div className="db-hero-divider" />

          {/* 4. Uptime / Latest Issue — 2 items; sits in rows 1 and 2, row 3 empty */}
          <div className="db-hero-section db-hero-section--uptime">
            {/* Row 1: Device Uptime */}
            <div className="db-row">
              <div className="db-info-field">
                <span className="db-info-label">DEVICE UPTIME</span>
                <span className="db-info-value">
                  {mqttStatus?.uptime_seconds != null
                    ? (() => {
                        const s = mqttStatus.uptime_seconds;
                        const d = Math.floor(s / 86400);
                        const h = Math.floor((s % 86400) / 3600);
                        const m = Math.floor((s % 3600) / 60);
                        return [d && `${d} day${d !== 1 ? "s" : ""}`, h && `${h} hour${h !== 1 ? "s" : ""}`, m && `${m} min`].filter(Boolean).join(" · ") || "< 1 min";
                      })()
                    : <span style={{ color: "var(--text-muted)" }}>Unavailable</span>}
                </span>
              </div>
            </div>
            {/* Row 2: Latest Device Issue */}
            <div className="db-row">
              <div className="db-uptime-issue">
                <span className="db-info-label">LATEST DEVICE ISSUE</span>
                {mqttStatus?.last_warn_message ? (
                  <>
                    <span className="db-issue-source">MQTT · WARN · Health Monitor</span>
                    <span className="db-issue-body">{mqttStatus.last_warn_message}</span>
                  </>
                ) : (
                  <span className="db-info-value" style={{ color: "var(--text-muted)" }}>No recent issues</span>
                )}
              </div>
            </div>
          </div>

        </section>

        {/* ── Admin Quick Notes card ── */}
        <section className="db-notes-card">
          <div className="db-notes-header">
            <span className="db-info-label">ADMIN QUICK NOTES</span>
            {!editingNotes && (
              <button
                onClick={() => setEditingNotes(true)}
                className="db-notes-edit-btn"
                type="button"
                title="Edit notes"
              >
                ✎
              </button>
            )}
          </div>
          {editingNotes ? (
            <div style={{ display: "flex", flexDirection: "column", gap: "0.5rem", flex: 1 }}>
              <textarea
                ref={notesRef}
                defaultValue={staffNotes}
                autoFocus
                className="db-notes-textarea"
                style={{ flex: 1 }}
                onKeyDown={(e) => {
                  if (e.key === "Escape") setEditingNotes(false);
                  if (e.key === "Enter" && (e.ctrlKey || e.metaKey)) saveStaffNotes(e.target.value);
                }}
              />
              <div style={{ display: "flex", gap: "0.5rem", justifyContent: "flex-end" }}>
                <button
                  onClick={() => saveStaffNotes(notesRef.current?.value ?? "")}
                  disabled={savingNotes}
                  className="px-2 py-1 text-xs rounded-md"
                  style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
                  type="button"
                >
                  {savingNotes ? "Saving..." : "Save"}
                </button>
                <button
                  onClick={() => setEditingNotes(false)}
                  className="px-2 py-1 text-xs rounded-md border"
                  style={{ borderColor: "var(--border-primary)", color: "var(--text-secondary)" }}
                  type="button"
                >
                  Cancel
                </button>
              </div>
            </div>
          ) : (
            <p
              className="db-notes-body"
              onClick={() => setEditingNotes(true)}
              style={{ color: staffNotes ? "var(--text-secondary)" : "var(--text-muted)" }}
            >
              {staffNotes || "Add a quick note to help identify this device…"}
            </p>
          )}
        </section>

      </div>

      <DeviceLogsPanel deviceSerial={device.serial_number || device.device_id} />

      <div className="dashboard-bottom-grid">
        <div className="dashboard-bottom-grid__notes" ref={notesPanelRef}>
          <NotesPanel key={notesPanelTab || "all"} deviceId={id} initialTab={notesPanelTab} />
        </div>

        <div className="dashboard-bottom-grid__users">
          <SectionCard title={`App Users (${deviceUsers.length})`}>
            {usersLoading ? (
              <p className="text-sm" style={{ color: "var(--text-muted)" }}>Loading users...</p>
            ) : deviceUsers.length === 0 ? (
              <p className="text-sm" style={{ color: "var(--text-muted)" }}>No users assigned to this device.</p>
            ) : (
              <div className="space-y-2">
                {deviceUsers.map((user, i) => (
                  <div
                    key={user.user_id || i}
                    className="p-3 rounded-md border cursor-pointer hover:opacity-90 transition-colors"
                    style={{ backgroundColor: "var(--bg-primary)", borderColor: "var(--border-primary)" }}
                    onClick={() => user.user_id && navigate(`/users/${user.user_id}`)}
                  >
                    <div className="flex items-center justify-between gap-3">
                      <div className="flex items-center gap-3 min-w-0">
                        <div
                          className="w-8 h-8 rounded-full overflow-hidden shrink-0"
                          style={{ backgroundColor: "var(--bg-card-hover)" }}
                        >
                          {user.photo_url ? (
                            <img src={user.photo_url} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} />
                          ) : (
                            <div className="w-full h-full flex items-center justify-center text-xs font-bold" style={{ color: "var(--text-muted)" }}>
                              {(user.display_name || user.email || "?").charAt(0).toUpperCase()}
                            </div>
                          )}
                        </div>
                        <div className="min-w-0">
                          <p className="text-sm font-medium truncate" style={{ color: "var(--text-heading)" }}>
                            {user.display_name || user.email || "Unknown User"}
                          </p>
                          {user.email && user.display_name && (
                            <p className="text-xs truncate" style={{ color: "var(--text-muted)", opacity: 0.7 }}>{user.email}</p>
                          )}
                        </div>
                      </div>
                      {user.role && (
                        <span className="px-2 py-0.5 text-xs rounded-full capitalize shrink-0" style={{ backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" }}>
                          {user.role}
                        </span>
                      )}
                    </div>
                  </div>
                ))}
              </div>
            )}
          </SectionCard>
        </div>
      </div>
    </div>
  );

  const generalInfoTab = (
    <div className="device-tab-stack">
      <div className="device-tab-grid">
        {locationCard}
        <SectionCard title="Basic Attributes" onEdit={canEdit ? () => setEditingAttributes(true) : undefined}>
          <FieldRow columns={3}>
            <Field label="Bell Guard">
              <BoolBadge value={attr.bellGuardOn} yesLabel="Operational" noLabel="Disabled" />
            </Field>
            <Field label="Warnings">
              <BoolBadge value={attr.warningsOn} yesLabel="Active" noLabel="Disabled" />
            </Field>
            <Field label="Bell Guard Safety">
              <BoolBadge value={attr.bellGuardSafetyOn} yesLabel="Armed" noLabel="Disarmed" />
            </Field>
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="Has Bells">
              <BoolBadge value={attr.hasBells} yesLabel="Yes" noLabel="No" />
            </Field>
            <Field label="Has Clock">
              <BoolBadge value={attr.hasClock} yesLabel="Yes" noLabel="No" />
            </Field>
            <Field label="Connected Bells">
              {formatConnectedBells(attr.totalBells)}
            </Field>
          </FieldRow>
        </SectionCard>

        <SectionCard title="Logging" onEdit={canEdit ? () => setEditingLogging(true) : undefined}>
          <FieldRow columns={3}>
            <Field label="Serial Log Level">
              {(() => {
                const info = formatLogLevel(attr.serialLogLevel);
                if (!info) return UNAVAILABLE_INFO;
                return <span title={info.description} style={{ color: info.color }}>{info.text}</span>;
              })()}
            </Field>
            <Field label="SD Log Level">
              {(() => {
                const info = formatLogLevel(attr.sdLogLevel);
                if (!info) return UNAVAILABLE_INFO;
                return <span title={info.description} style={{ color: info.color }}>{info.text}</span>;
              })()}
            </Field>
            <Field label="MQTT Log Level">
              {(() => {
                const info = formatLogLevel(attr.mqttLogLevel);
                if (!info) return UNAVAILABLE_INFO;
                return <span title={info.description} style={{ color: info.color }}>{info.text}</span>;
              })()}
            </Field>
          </FieldRow>
        </SectionCard>

        <SectionCard title="Misc" onEdit={canEdit ? () => setEditingMisc(true) : undefined}>
          <FieldRow columns={3}>
            <Field label="Automated Events">
              <BoolBadge value={device.events_on} yesLabel="Enabled" noLabel="Disabled" />
            </Field>
            <Field label="Church Type">{formatLocale(attr.deviceLocale)}</Field>
            <EmptyCell />
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="WebSocket URL">{hasValue(device.websocket_url) ? device.websocket_url : UNAVAILABLE_INFO}</Field>
            <EmptyCell />
            <EmptyCell />
          </FieldRow>
        </SectionCard>

        <SectionCard title="Network Settings">
          <FieldRow columns={3}>
            <Field label="Hostname">{hasValue(net.hostname) ? net.hostname : UNAVAILABLE_INFO}</Field>
            <Field label="Static IP">{hasValue(staticIpAddress) ? staticIpAddress : UNAVAILABLE_INFO}</Field>
            <EmptyCell />
          </FieldRow>
        </SectionCard>

        {/* ── Tags ── */}
        <SectionCard title="Tags">
          <div className="flex flex-wrap gap-2 mb-3">
            {tags.length === 0 && (
              <span className="text-xs" style={{ color: "var(--text-muted)" }}>No tags yet.</span>
            )}
            {tags.map((tag) => (
              <span
                key={tag}
                className="inline-flex items-center gap-1 px-2.5 py-0.5 rounded-full text-xs font-medium"
                style={{ backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)", border: "1px solid var(--badge-blue-text)" }}
              >
                {tag}
                {canEdit && (
                  <button
                    type="button"
                    onClick={() => handleRemoveTag(tag)}
                    disabled={savingTags}
                    className="ml-0.5 hover:opacity-70 cursor-pointer disabled:opacity-40"
                    style={{ lineHeight: 1 }}
                  >
                    ×
                  </button>
                )}
              </span>
            ))}
          </div>
          {canEdit && (
            <div className="flex gap-2">
              <input
                type="text"
                value={tagInput}
                onChange={(e) => setTagInput(e.target.value)}
                onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); handleAddTag(tagInput); } }}
                placeholder="Add tag and press Enter…"
                className="px-3 py-1.5 rounded-md text-sm border flex-1"
                style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
              />
              <button
                type="button"
                onClick={() => handleAddTag(tagInput)}
                disabled={!tagInput.trim() || savingTags}
                className="px-3 py-1.5 text-sm rounded-md disabled:opacity-50 cursor-pointer"
                style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
              >
                Add
              </button>
            </div>
          )}
        </SectionCard>

        {/* ── Owner ── */}
        <SectionCard title="Owner">
          {device.customer_id ? (
            <div>
              {ownerCustomer ? (
                <div
                  className="flex items-center gap-3 p-3 rounded-md border mb-3 cursor-pointer hover:opacity-80 transition-opacity"
                  style={{ backgroundColor: "var(--bg-primary)", borderColor: "var(--border-secondary)" }}
                  onClick={() => navigate(`/crm/customers/${device.customer_id}`)}
                  title="View customer"
                >
                  <div className="w-8 h-8 rounded-full flex items-center justify-center text-sm font-bold shrink-0"
                    style={{ backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" }}>
                    {(ownerCustomer.name || "?")[0].toUpperCase()}
                  </div>
                  <div className="min-w-0 flex-1">
                    <p className="text-sm font-medium" style={{ color: "var(--text-primary)" }}>{ownerCustomer.name || "—"}</p>
                    {ownerCustomer.organization && (
                      <p className="text-xs" style={{ color: "var(--text-muted)" }}>{ownerCustomer.organization}</p>
                    )}
                  </div>
                  <svg className="w-4 h-4 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" style={{ color: "var(--text-muted)" }}>
                    <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
                  </svg>
                </div>
              ) : (
                <p className="text-sm mb-3" style={{ color: "var(--text-muted)" }}>Customer assigned (loading details…)</p>
              )}
              {canEdit && (
                <div className="flex gap-2">
                  <button
                    type="button"
                    onClick={() => setShowAssignSearch(true)}
                    className="text-xs px-3 py-1.5 rounded-md cursor-pointer hover:opacity-80"
                    style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)", border: "1px solid var(--border-primary)" }}
                  >
                    Reassign
                  </button>
                  <button
                    type="button"
                    onClick={handleUnassignCustomer}
                    disabled={assigningCustomer}
                    className="text-xs px-3 py-1.5 rounded-md cursor-pointer disabled:opacity-50 hover:opacity-80"
                    style={{ backgroundColor: "var(--danger-bg)", color: "var(--danger-text)", border: "1px solid var(--danger)" }}
                  >
                    Remove
                  </button>
                </div>
              )}
            </div>
          ) : (
            <div>
              <p className="text-sm mb-3" style={{ color: "var(--text-muted)" }}>No customer assigned yet.</p>
              {canEdit && (
                <button
                  type="button"
                  onClick={() => setShowAssignSearch(true)}
                  className="text-sm px-3 py-1.5 rounded-md cursor-pointer hover:opacity-80"
                  style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)", border: "1px solid var(--border-primary)" }}
                >
                  Assign to Customer
                </button>
              )}
            </div>
          )}

          {showAssignSearch && (
            <CustomerAssignModal
              deviceId={id}
              onSelect={(c) => { setShowAssignSearch(false); handleAssignCustomer(c); }}
              onCancel={() => setShowAssignSearch(false)}
            />
          )}
          </SectionCard>

        {/* ── Device Notes ── */}
        <SectionCard title="Device Notes">
          {!notesLoaded ? (
            <p className="text-xs" style={{ color: "var(--text-muted)" }}>Loading…</p>
          ) : (
            <>
              {deviceNotes.length === 0 && !addingNote && (
                <p className="text-xs mb-3" style={{ color: "var(--text-muted)" }}>No notes for this device.</p>
              )}
              <div className="space-y-3 mb-3">
                {deviceNotes.map((note) => (
                  <div
                    key={note.id}
                    className="p-3 rounded-md border"
                    style={{ backgroundColor: "var(--bg-primary)", borderColor: "var(--border-secondary)" }}
                  >
                    {editingNoteId === note.id ? (
                      <div className="space-y-2">
                        <textarea
                          value={editingNoteText}
                          onChange={(e) => setEditingNoteText(e.target.value)}
                          autoFocus
                          rows={3}
                          className="w-full px-2 py-1.5 rounded text-sm border"
                          style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)", resize: "vertical" }}
                        />
                        <div className="flex gap-2">
                          <button
                            type="button"
                            onClick={() => handleUpdateNote(note.id)}
                            disabled={savingNote}
                            className="text-xs px-2.5 py-1 rounded-md cursor-pointer disabled:opacity-50"
                            style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
                          >
                            {savingNote ? "Saving…" : "Save"}
                          </button>
                          <button
                            type="button"
                            onClick={() => setEditingNoteId(null)}
                            className="text-xs px-2.5 py-1 rounded-md cursor-pointer"
                            style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)" }}
                          >
                            Cancel
                          </button>
                        </div>
                      </div>
                    ) : (
                      <div className="flex items-start justify-between gap-3">
                        <div className="flex-1 min-w-0">
                          <p className="text-sm whitespace-pre-wrap" style={{ color: "var(--text-secondary)" }}>{note.content}</p>
                          <p className="text-xs mt-1" style={{ color: "var(--text-muted)" }}>
                            {note.created_by}{note.created_at ? ` · ${new Date(note.created_at).toLocaleDateString("en-US", { year: "numeric", month: "short", day: "numeric" })}` : ""}
                          </p>
                        </div>
                        {canEdit && (
                          <div className="flex gap-1.5 shrink-0">
                            <button
                              type="button"
                              onClick={() => { setEditingNoteId(note.id); setEditingNoteText(note.content); }}
                              className="text-xs px-2 py-0.5 rounded cursor-pointer hover:opacity-80"
                              style={{ color: "var(--text-muted)" }}
                            >
                              Edit
                            </button>
                            <button
                              type="button"
                              onClick={() => handleDeleteNote(note.id)}
                              className="text-xs px-2 py-0.5 rounded cursor-pointer hover:opacity-80"
                              style={{ color: "var(--danger-text)" }}
                            >
                              Delete
                            </button>
                          </div>
                        )}
                      </div>
                    )}
                  </div>
                ))}
              </div>

              {canEdit && (
                addingNote ? (
                  <div className="space-y-2">
                    <textarea
                      value={newNoteText}
                      onChange={(e) => setNewNoteText(e.target.value)}
                      autoFocus
                      rows={3}
                      placeholder="Write a note…"
                      className="w-full px-3 py-2 rounded-md text-sm border"
                      style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)", resize: "vertical" }}
                    />
                    <div className="flex gap-2">
                      <button
                        type="button"
                        onClick={handleAddNote}
                        disabled={savingNote || !newNoteText.trim()}
                        className="text-sm px-3 py-1.5 rounded-md cursor-pointer disabled:opacity-50"
                        style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
                      >
                        {savingNote ? "Saving…" : "Add Note"}
                      </button>
                      <button
                        type="button"
                        onClick={() => { setAddingNote(false); setNewNoteText(""); }}
                        className="text-sm px-3 py-1.5 rounded-md cursor-pointer"
                        style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)" }}
                      >
                        Cancel
                      </button>
                    </div>
                  </div>
                ) : (
                  <button
                    type="button"
                    onClick={() => setAddingNote(true)}
                    className="text-sm px-3 py-1.5 rounded-md cursor-pointer"
                    style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)", border: "1px solid var(--border-primary)" }}
                  >
                    + Add Note
                  </button>
                )
              )}
            </>
          )}
        </SectionCard>

      </div>
    </div>
  );

  const bellMechanismsTab = (
    <div className="device-tab-stack">
      <SectionCard title="Overview">
        <div className="device-inline-facts device-inline-facts--overview">
          <div>
            <span className="device-inline-facts__label">Bell Commander</span>
            <BoolBadge value={attr.hasBells} yesLabel="Operational" noLabel="Disabled" />
          </div>
          <div>
            <span className="device-inline-facts__label">Bell Guard</span>
            <BoolBadge value={attr.bellGuardOn} yesLabel="Operational" noLabel="Disabled" />
          </div>
          <div>
            <span className="device-inline-facts__label">Connected Bells</span>
            <span>{formatConnectedBells(attr.totalBells)}</span>
          </div>
        </div>
      </SectionCard>

      {canEdit && (
        <div className="bell-master-strip">
          <div className="bell-master-strip__left">
            <span className="bell-master-strip__warning-badge">CAUTION</span>
            <span className="text-sm font-medium" style={{ color: "var(--text-primary)" }}>Bell Commander</span>
            <ToggleSwitch
              value={attr.hasBells ?? false}
              onChange={async (val) => {
                if (!val && !window.confirm("Are you sure you want to DISABLE the Bell Mechanisms?")) return;
                try {
                  await api.put(`/devices/${id}`, { device_attributes: { hasBells: val } });
                  await loadData();
                } catch {}
              }}
              onLabel="Active"
              offLabel="Disabled"
            />
          </div>
          <div className="bell-master-strip__right">
            <button
              type="button"
              onClick={() => setEditingBellOutputs(true)}
              className="px-4 py-2 text-sm rounded-md font-medium cursor-pointer hover:opacity-90"
              style={{ backgroundColor: "var(--accent)", color: "#000" }}
            >
              Edit Outputs
            </button>
          </div>
        </div>
      )}

      {attr.bellOutputs?.length ? (
        <div className="bell-mechanism-grid">
          {attr.bellOutputs.map((output, i) => {
            const hammingMs = attr.hammerTimings?.[i];
            const strikerSize = getStrikerSize(hammingMs);
            const ordinal = ORDINAL_NAMES[i] ?? `${i + 1}th`;
            return (
              <article key={i} className="bell-mechanism-card">
                {/* Left column */}
                <div className="bell-mechanism-card__left">
                  <div className="bell-mechanism-card__left-top">
                    <h3 className="bell-mechanism-card__title">{ordinal} Bell Mechanism</h3>
                    <hr className="bell-mechanism-card__divider" />
                    <div className="bell-mechanism-card__info">
                      <p>
                        {Number.isFinite(output) && Number(output) === 0
                          ? <strong className="bell-mechanism-card__highlight" style={{ color: "var(--text-muted)" }}>Currently Disabled</strong>
                          : <>Assigned to{" "}<strong className="bell-mechanism-card__highlight">{Number.isFinite(output) ? formatOutput(output) : UNAVAILABLE_INFO}</strong></>
                        }
                      </p>
                      <p>
                        with a relay timing of{" "}
                        <strong className="bell-mechanism-card__highlight">
                          {hammingMs != null ? `${hammingMs} ms` : UNAVAILABLE_INFO}
                        </strong>
                      </p>
                      <p>
                        using a{" "}
                        <strong className="bell-mechanism-card__highlight">
                          {strikerSize ? `Size ${strikerSize}` : UNAVAILABLE_INFO}
                        </strong>{" "}
                        Striker Mechanism
                      </p>
                      <p>
                        has been struck{" "}
                        <strong className="bell-mechanism-card__highlight bell-mechanism-card__highlight--strikes">
                          {Number.isFinite(liveStrikeCounters?.[i])
                            ? `${liveStrikeCounters[i]} Times`
                            : requestingStrikeCounters
                              ? "Loading..."
                              : UNAVAILABLE_INFO}
                        </strong>
                      </p>
                    </div>
                  </div>
                </div>
                {/* Right column */}
                <div className="bell-mechanism-card__right">
                  <div className="bell-mechanism-card__right-top">
                    {strikerSize && (
                      <img
                        className="bell-mechanism-card__striker-img"
                        src={`/devices/Strikers/striker_size_${strikerSize}.png`}
                        alt={`Size ${strikerSize} striker`}
                      />
                    )}
                  </div>
                  <div className="bell-mechanism-card__cert">
                    <span className="bell-mechanism-card__cert-text">
                      Manuf. Certified<br />
                      <span className="bell-mechanism-card__cert-brand">BellSystems</span> Striker
                    </span>
                    <img
                      className="bell-mechanism-card__cert-badge"
                      src="/image-assets/certified_logo.png"
                      alt="Certified"
                    />
                  </div>
                </div>
              </article>
            );
          })}
        </div>
      ) : (
        <p className="text-sm" style={{ color: "var(--text-muted)" }}>No bell mechanism data available.</p>
      )}
    </div>
  );

  const clockAlertsTab = (
    <div className="device-tab-stack">
      <div className="device-tab-grid">
        <SectionCard title="Clock Settings" onEdit={canEdit ? () => setEditingClockSettings(true) : undefined}>
          <FieldRow columns={3}>
            <Field label="Clock Controller">
              <BoolBadge value={attr.hasClock} yesLabel="Enabled" noLabel="Disabled" />
            </Field>
            <EmptyCell />
            <EmptyCell />
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="ODD Output">{formatOutput(clock.clockOutputs?.[0])}</Field>
            <Field label="EVEN Output">{formatOutput(clock.clockOutputs?.[1])}</Field>
            <EmptyCell />
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="Run Pulse">
              {clock.clockTimings?.[0] != null ? formatMsWithSeconds(clock.clockTimings[0]) : UNAVAILABLE_INFO}
            </Field>
            <Field label="Pause Pulse">
              {clock.clockTimings?.[1] != null ? formatMsWithSeconds(clock.clockTimings[1]) : UNAVAILABLE_INFO}
            </Field>
            <EmptyCell />
          </FieldRow>
        </SectionCard>

        <SectionCard title="Alert Settings" onEdit={canEdit ? () => setEditingAlerts(true) : undefined}>
          <FieldRow columns={3}>
            <Field label="Status">
              <BoolBadge value={clock.ringAlertsMasterOn} yesLabel="Alerts Enabled" noLabel="Alerts Disabled" />
            </Field>
            <Field label="Alerts Type">
              {clock.ringAlerts === "multi"
                ? "Hour Indicating"
                : clock.ringAlerts === "single"
                  ? "Single Fire"
                  : clock.ringAlerts === "off" || clock.ringAlerts === "disabled"
                    ? "Deactivated"
                    : UNAVAILABLE_INFO}
            </Field>
            <Field label="Alert Tempo">
              {Number.isFinite(clock.ringIntervals) ? formatMsWithSeconds(clock.ringIntervals) : UNAVAILABLE_INFO}
            </Field>
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="Hour Bell">{formatBell(clock.hourAlertsBell)}</Field>
            <Field label="Half-Hour Bell">{formatBell(clock.halfhourAlertsBell)}</Field>
            <Field label="Quarter Bell">{formatBell(clock.quarterAlertsBell)}</Field>
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="Day Silence">
              {formatSilenceRange(clock.isDaySilenceOn, clock.daySilenceFrom, clock.daySilenceTo)}
            </Field>
            <Field label="Night Silence">
              {formatSilenceRange(clock.isNightSilenceOn, clock.nightSilenceFrom, clock.nightSilenceTo)}
            </Field>
            <EmptyCell />
          </FieldRow>
        </SectionCard>

        <SectionCard title="Backlight" onEdit={canEdit ? () => setEditingBacklight(true) : undefined}>
          <FieldRow columns={3}>
            <Field label="Functionality">
              <BoolBadge value={clock.isBacklightAutomationOn} yesLabel="Automated" noLabel="Disabled" />
            </Field>
            <Field label="Output">{formatOutputWithDisabledZero(clock.backlightOutput)}</Field>
            <Field label="Period">
              {formatSilencePeriod(clock.isBacklightAutomationOn, clock.backlightTurnOnTime, clock.backlightTurnOffTime)}
            </Field>
          </FieldRow>
        </SectionCard>
      </div>
    </div>
  );

  const warrantySubscriptionTab = (
    <div className="device-tab-stack">
      <div className="device-tab-grid">
        <SectionCard title="Subscription" onEdit={canEdit ? () => setEditingSubscription(true) : undefined}>
          <ProgressBar value={subscrProgress ?? 0} tone="var(--accent)" />
          <div className="mt-6" />
          <FieldRow columns={3}>
            <Field label="Tier">
              <span className="device-status-text device-status-text--info capitalize">
                {hasValue(sub.subscrTier) ? sub.subscrTier : UNAVAILABLE_INFO}
              </span>
            </Field>
            <Field label="Time Remaining">
              {subscrDaysLeft == null ? UNAVAILABLE_INFO : subscrDaysLeft <= 0 ? "Expired" : formatDaysVerbose(subscrDaysLeft)}
            </Field>
            <EmptyCell />
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="Max Users">{Number.isFinite(sub.maxUsers) ? formatCount(sub.maxUsers, "User") : UNAVAILABLE_INFO}</Field>
            <Field label="Max Outputs">{Number.isFinite(sub.maxOutputs) ? formatCount(sub.maxOutputs, "Output") : UNAVAILABLE_INFO}</Field>
            <EmptyCell />
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="Start Date">{formatAnyDateVerbose(sub.subscrStart)}</Field>
            <Field label="End Date">{subscrEnd ? formatDateNice(subscrEnd) : UNAVAILABLE_INFO}</Field>
            <Field label="Duration">{Number.isFinite(sub.subscrDuration) && sub.subscrDuration > 0 ? formatDaysVerbose(sub.subscrDuration) : UNAVAILABLE_INFO}</Field>
          </FieldRow>
        </SectionCard>

        <SectionCard title="Warranty" onEdit={canEdit ? () => setEditingWarranty(true) : undefined}>
          <ProgressBar value={warrantyProgress ?? 0} tone={warrantyDaysLeft <= 0 ? "var(--danger)" : "var(--accent)"} />
          <div className="mt-6" />
          <FieldRow columns={3}>
            <Field label="Warranty Status">
              <span className={`device-status-text ${warrantyIsVoid ? "device-status-text--warning" : (warrantyDaysLeft !== null ? warrantyDaysLeft > 0 : stats.warrantyActive) ? "device-status-text--positive" : "device-status-text--negative"}`}>
                {warrantyIsVoid ? "Warranty Void" : warrantyDaysLeft !== null ? (warrantyDaysLeft > 0 ? "Active" : "Expired") : formatBoolean(stats.warrantyActive, "Active", "Expired")}
              </span>
            </Field>
            <Field label="Time Remaining">
              {warrantyDaysLeft == null ? UNAVAILABLE_INFO : warrantyDaysLeft <= 0 ? "Expired" : formatDaysVerbose(warrantyDaysLeft)}
            </Field>
            <EmptyCell />
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="Start Date">{formatAnyDateVerbose(stats.warrantyStart)}</Field>
            <Field label="Expiry Date">{warrantyEnd ? formatDateNice(warrantyEnd) : UNAVAILABLE_INFO}</Field>
            <Field label="Period">{Number.isFinite(stats.warrantyPeriod) && stats.warrantyPeriod > 0 ? formatDaysVerbose(stats.warrantyPeriod) : UNAVAILABLE_INFO}</Field>
          </FieldRow>
        </SectionCard>

        <SectionCard title="Maintenance">
          <ProgressBar value={maintenanceProgress ?? 0} tone={maintenanceDaysLeft <= 0 ? "var(--danger)" : "#f59e0b"} />
          <div className="mt-6" />
          <FieldRow columns={3}>
            <Field label="Last Maintained">{formatAnyDateVerbose(stats.maintainedOn)}</Field>
            <Field label="Maintenance Period">
              {Number.isFinite(stats.maintainancePeriod) && stats.maintainancePeriod > 0 ? formatDaysVerbose(stats.maintainancePeriod) : UNAVAILABLE_INFO}
            </Field>
            <Field label="Next Scheduled">
              {nextMaintenance ? `${formatDateNice(nextMaintenance)} (${maintenanceDaysLeft <= 0 ? "overdue" : formatRelativeTime(maintenanceDaysLeft)})` : UNAVAILABLE_INFO}
            </Field>
          </FieldRow>
        </SectionCard>
      </div>

      <div className="device-tab-grid">
        <SectionCard title="Statistics">
          <FieldRow columns={3}>
            <Field label="Total Playbacks">
              {randomPlaybacks} Times
            </Field>
            <Field label="Hammer Strikes">
              {Number.isFinite(hammerStrikesFromLive)
                ? (
                  <span>
                    Striked{" "}
                    <strong style={{ color: hammerStrikesFromLive > 0 ? "#f59e0b" : "var(--success-text)" }}>
                      {hammerStrikesFromLive}
                    </strong>{" "}
                    Times
                  </span>
                )
                : requestingStrikeCounters
                  ? "Loading..."
                  : UNAVAILABLE_INFO}
            </Field>
            <Field label="Warnings Given">
              {Number.isFinite(stats.totalWarningsGiven)
                ? stats.totalWarningsGiven === 0
                  ? <span style={{ color: "var(--success-text)" }}>No Warnings</span>
                  : stats.totalWarningsGiven === 1
                    ? <span style={{ color: "#f59e0b" }}>1 Warning given</span>
                    : <span style={{ color: "var(--danger-text)" }}>{stats.totalWarningsGiven} Warnings given</span>
                : UNAVAILABLE_INFO}
            </Field>
          </FieldRow>
          <FieldRow columns={3}>
            <Field label="Total Melodies">
              {Array.isArray(device.device_melodies_all)
                ? device.device_melodies_all.length === 0
                  ? "No Melodies yet"
                  : `${device.device_melodies_all.length} on-board Melodies`
                : UNAVAILABLE_INFO}
            </Field>
            <Field label="Favorite Melodies">
              {Array.isArray(device.device_melodies_favorites)
                ? device.device_melodies_favorites.length === 0
                  ? "No Melodies yet"
                  : `${device.device_melodies_favorites.length} on-board Melodies`
                : UNAVAILABLE_INFO}
            </Field>
            <EmptyCell />
          </FieldRow>
        </SectionCard>
      </div>
    </div>
  );

  const totalBells = attr?.totalBells || 0;
  const bellNumbers = Array.from({ length: totalBells }, (_, i) => i + 1);

  const CTRL_STATUS_STYLES = {
    success: { bg: "var(--success-bg)", color: "var(--success-text)" },
    error: { bg: "var(--danger-bg)", color: "var(--danger-text)" },
    pending: { bg: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" },
    timeout: { bg: "var(--danger-bg)", color: "var(--danger-text)" },
  };

  const ctrlFormatPayload = (jsonStr) => {
    if (!jsonStr) return null;
    try { return JSON.stringify(JSON.parse(jsonStr), null, 2); } catch { return jsonStr; }
  };

  // Control card button style
  const ctrlBtn = (danger = false) => ({
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "center",
    padding: "0.6rem 1.25rem",
    borderRadius: "0.5rem",
    border: `1px solid ${danger ? "var(--danger)" : "var(--border-secondary)"}`,
    backgroundColor: danger ? "var(--danger-btn, #7f1d1d)" : "var(--bg-input, #2E3D51)",
    color: danger ? "#fff" : "var(--text-primary)",
    fontSize: "0.875rem",
    cursor: "pointer",
    fontFamily: "inherit",
    transition: "opacity 0.15s",
    whiteSpace: "nowrap",
  });

  const ctrlSelect = {
    display: "inline-flex",
    alignItems: "center",
    justifyContent: "space-between",
    padding: "0.6rem 1rem",
    borderRadius: "0.5rem",
    border: "1px solid var(--border-secondary)",
    backgroundColor: "var(--bg-input, #2E3D51)",
    color: "var(--text-primary)",
    fontSize: "0.875rem",
    cursor: "pointer",
    fontFamily: "inherit",
    minWidth: 140,
  };

  const ctrlLabel = {
    display: "block",
    fontSize: "0.65rem",
    fontWeight: 600,
    letterSpacing: "0.07em",
    textTransform: "uppercase",
    color: "var(--text-muted)",
    marginBottom: "0.45rem",
  };

  const ctrlFieldGroup = {
    display: "flex",
    flexDirection: "column",
    gap: "0.35rem",
  };

  const ORDINAL_CTRL = ["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th", "11th", "12th"];

  const TIMEZONES = [
    "UTC", "Europe/London", "Europe/Paris", "Europe/Athens", "Europe/Helsinki",
    "America/New_York", "America/Chicago", "America/Denver", "America/Los_Angeles",
    "Asia/Tokyo", "Asia/Shanghai", "Australia/Sydney",
  ];

  const controlTab = (
    <div className="device-tab-stack">
      {/* ── Grid of control cards ── */}
      <div className="device-tab-grid">

        {/* 1. Restart */}
        <SectionCard title="Restart Device">
          <p className="text-sm mb-5" style={{ color: "var(--text-secondary)", lineHeight: 1.6 }}>
            Send a restart command to the device. The device will reboot and reconnect automatically.
          </p>
          <button style={ctrlBtn(true)} type="button" onClick={() => {}}>
            Restart Device
          </button>
        </SectionCard>

        {/* 2. Firmware Update */}
        <SectionCard title="Firmware Control">
          <div style={{ display: "flex", justifyContent: "space-between", gap: "2rem", flexWrap: "wrap" }}>
            {/* Info column */}
            <div style={{ display: "flex", flexDirection: "column", gap: "1.1rem" }}>
              <div style={ctrlFieldGroup}>
                <span style={ctrlLabel}>Device Firmware</span>
                <span style={{ fontSize: "1rem", color: "var(--text-primary)" }}>
                  {device?.device_firmware_version || "—"}
                </span>
              </div>
              <div style={ctrlFieldGroup}>
                <span style={ctrlLabel}>Firmware Family</span>
                <span style={{ fontSize: "1rem", color: "var(--text-primary)" }}>
                  {device?.device_firmware_family || "Bell Core"}
                </span>
              </div>
              <div style={ctrlFieldGroup}>
                <span style={ctrlLabel}>Last Updated On</span>
                <span style={{ fontSize: "1rem", color: "var(--text-primary)" }}>
                  {device?.device_firmware_updated ? formatDate(device.device_firmware_updated) : "—"}
                </span>
              </div>
            </div>
            {/* Controls column — pushed to far right */}
            <div style={{ display: "flex", flexDirection: "column", gap: "1.25rem", alignItems: "flex-end" }}>
              <div style={{ ...ctrlFieldGroup, alignItems: "flex-end" }}>
                <span style={ctrlLabel}>Change Branch</span>
                <select
                  value={ctrlFirmwareChannel}
                  onChange={(e) => setCtrlFirmwareChannel(e.target.value)}
                  style={ctrlSelect}
                >
                  <option value="stable">Stable</option>
                  <option value="beta">Beta</option>
                  <option value="alpha">Alpha</option>
                </select>
              </div>
              <button style={ctrlBtn()} type="button" onClick={() => {}}>
                Force Update Now
              </button>
            </div>
          </div>
        </SectionCard>

        {/* 3. Test Fire Bells */}
        <SectionCard title="Test Fire Bells">
          <p className="text-xs mb-4" style={{ color: "var(--text-muted)" }}>
            Click any bell button to fire it once. Use "Fire All" to trigger each bell in sequence.
          </p>
          {totalBells === 0 ? (
            <p className="text-sm" style={{ color: "var(--text-secondary)" }}>No bells configured on this device.</p>
          ) : (
            <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }}>
              <div style={{ display: "flex", flexWrap: "wrap", gap: "0.6rem" }}>
                {bellNumbers.map((n) => (
                  <button
                    key={n}
                    style={ctrlBtn()}
                    type="button"
                    onClick={() => {}}
                    title={`Test fire ${ORDINAL_CTRL[n - 1] ?? n + "th"} bell`}
                  >
                    {ORDINAL_CTRL[n - 1] ?? `${n}th`} Bell
                  </button>
                ))}
              </div>
              <div style={{ borderTop: "1px solid var(--border-primary)", paddingTop: "0.85rem" }}>
                <button style={{ ...ctrlBtn(), border: "1px solid var(--accent)", color: "var(--accent)" }} type="button" onClick={() => {}}>
                  Fire All Bells in Sequence
                </button>
              </div>
            </div>
          )}
        </SectionCard>

        {/* 4. HTTP Server */}
        <SectionCard title="HTTP Server">
          <p className="text-sm mb-5" style={{ color: "var(--text-secondary)", lineHeight: 1.6 }}>
            Enable or disable the HTTP server on this device. Turning it off will make the web interface inaccessible.
          </p>
          <div style={ctrlFieldGroup}>
            <span style={ctrlLabel}>HTTP Server Status</span>
            <span style={{
              fontSize: "1rem",
              fontWeight: 600,
              color: ctrlHttpEnabled ? "var(--success-text, #68d391)" : "var(--text-muted)",
            }}>
              {ctrlHttpEnabled ? "Running" : "Disabled"}
            </span>
          </div>
          <div style={{ display: "flex", gap: "0.75rem", marginTop: "1.25rem", flexWrap: "wrap" }}>
            <button
              style={{ ...ctrlBtn(), border: "1px solid var(--accent)", color: "var(--accent)" }}
              type="button"
              onClick={() => setCtrlHttpEnabled(true)}
            >
              Switch On
            </button>
            <button
              style={ctrlBtn(true)}
              type="button"
              onClick={() => setCtrlHttpEnabled(false)}
            >
              Switch Off
            </button>
          </div>
        </SectionCard>

        {/* 5. SD Card */}
        <SectionCard title="SD Card">
          <p className="text-sm mb-5" style={{ color: "var(--text-secondary)", lineHeight: 1.6 }}>
            List all files stored on the device's SD card, or permanently delete all contents including melodies, logs, and configuration files.
          </p>
          <div style={{ display: "flex", gap: "0.75rem", flexWrap: "wrap" }}>
            <button
              style={{ ...ctrlBtn(), border: "1px solid var(--accent)", color: "var(--accent)" }}
              type="button"
              onClick={() => {}}
            >
              List Files
            </button>
            <button style={ctrlBtn(true)} type="button" onClick={() => {}}>
              Delete All Contents
            </button>
          </div>
        </SectionCard>

        {/* 6. Time Sync */}
        <SectionCard title="Time & Timezone Sync">
          <div style={{ display: "flex", flexDirection: "column", gap: "1.1rem" }}>
            <div style={ctrlFieldGroup}>
              <span style={ctrlLabel}>Device Date & Time</span>
              <input
                type="datetime-local"
                value={ctrlDeviceTime}
                onChange={(e) => setCtrlDeviceTime(e.target.value)}
                style={{
                  ...ctrlSelect,
                  minWidth: 200,
                  padding: "0.55rem 0.85rem",
                }}
              />
            </div>
            <div style={ctrlFieldGroup}>
              <span style={ctrlLabel}>Timezone</span>
              <select value={ctrlTimezone} onChange={(e) => setCtrlTimezone(e.target.value)} style={ctrlSelect}>
                {TIMEZONES.map((tz) => (
                  <option key={tz} value={tz}>{tz}</option>
                ))}
              </select>
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: "0.75rem" }}>
              <ToggleSwitch
                value={ctrlDst}
                onChange={(val) => setCtrlDst(val)}
                onLabel="DST On"
                offLabel="DST Off"
              />
            </div>
            <div style={{ display: "flex", gap: "0.75rem", flexWrap: "wrap", paddingTop: "0.25rem" }}>
              <button style={ctrlBtn()} type="button" onClick={() => setCtrlDeviceTime(new Date().toISOString().slice(0, 16))}>
                Use Current Time
              </button>
              <button style={{ ...ctrlBtn(), border: "1px solid var(--accent)", color: "var(--accent)" }} type="button" onClick={() => {}}>
                Send Time Sync
              </button>
            </div>
          </div>
        </SectionCard>

      </div>{/* end device-tab-grid */}

      {/* 7. Device Melodies List — full width */}
      <SectionCard title="Device Melodies (SD Card)">
        <div style={{ display: "flex", alignItems: "center", gap: "1rem", marginBottom: "1rem", flexWrap: "wrap" }}>
          <button style={ctrlBtn()} type="button" onClick={fetchCtrlMelodies}>
            {ctrlMelodiesLoading ? "Loading..." : "Fetch Melody List"}
          </button>
          {ctrlMelodies.length > 0 && (
            <button style={ctrlBtn(true)} type="button" onClick={() => {}}>
              Delete All
            </button>
          )}
        </div>
        {ctrlMelodiesLoading ? (
          <p className="text-xs" style={{ color: "var(--text-muted)" }}>Requesting melody list from device...</p>
        ) : ctrlMelodies.length === 0 ? (
          <p className="text-xs" style={{ color: "var(--text-muted)" }}>No melodies loaded yet. Press "Fetch Melody List" to request them from the device.</p>
        ) : (
          <div className="rounded-md overflow-hidden border" style={{ borderColor: "var(--border-primary)" }}>
            <table className="w-full text-sm">
              <thead>
                <tr style={{ backgroundColor: "var(--bg-primary)", borderBottom: "1px solid var(--border-primary)" }}>
                  <th className="px-4 py-2.5 text-left font-medium" style={{ color: "var(--text-secondary)" }}>#</th>
                  <th className="px-4 py-2.5 text-left font-medium" style={{ color: "var(--text-secondary)" }}>Filename</th>
                  <th className="px-4 py-2.5 text-left font-medium" style={{ color: "var(--text-secondary)" }}>Size</th>
                  <th className="px-4 py-2.5 text-right font-medium" style={{ color: "var(--text-secondary)" }}>Actions</th>
                </tr>
              </thead>
              <tbody>
                {ctrlMelodies.map((mel, i) => (
                  <tr key={mel.name || i} style={{ borderBottom: i < ctrlMelodies.length - 1 ? "1px solid var(--border-primary)" : "none" }}>
                    <td className="px-4 py-2.5 font-mono text-xs" style={{ color: "var(--text-muted)" }}>{i + 1}</td>
                    <td className="px-4 py-2.5 font-mono text-xs" style={{ color: "var(--text-primary)" }}>{mel.name}</td>
                    <td className="px-4 py-2.5 text-xs" style={{ color: "var(--text-muted)" }}>{mel.size ? `${mel.size} bytes` : "—"}</td>
                    <td className="px-4 py-2.5 text-right">
                      <button style={{ ...ctrlBtn(true), padding: "0.3rem 0.75rem", fontSize: "0.75rem" }} type="button" onClick={() => {}}>
                        Delete
                      </button>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        )}
      </SectionCard>

      {/* 8. Custom Command */}
      <SectionCard title="Send Custom Command">
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem" }}>
          <div style={ctrlFieldGroup}>
            <span style={ctrlLabel}>Command</span>
            <textarea
              value={ctrlCustomCmd}
              onChange={(e) => setCtrlCustomCmd(e.target.value)}
              placeholder={`Enter command JSON...\ne.g. {"cmd": "ping", "contents": {}}`}
              className="w-full px-3 py-2 rounded-md text-xs border font-mono"
              style={{
                backgroundColor: "var(--bg-primary)",
                borderColor: "var(--border-primary)",
                color: "var(--text-primary)",
                resize: "vertical",
                height: ctrlCustomHeight,
                minHeight: 120,
              }}
              onMouseUp={(e) => { if (e.target.style.height) setCtrlCustomHeight(parseInt(e.target.style.height) || ctrlCustomHeight); }}
            />
          </div>
          <div style={ctrlFieldGroup}>
            <span style={ctrlLabel}>Response</span>
            <textarea
              readOnly
              value={ctrlCustomResponse}
              placeholder="Response will appear here..."
              className="w-full px-3 py-2 rounded-md text-xs border font-mono"
              style={{
                backgroundColor: "var(--bg-primary)",
                borderColor: "var(--border-primary)",
                color: "var(--text-muted)",
                resize: "none",
                height: ctrlCustomHeight,
                minHeight: 120,
              }}
            />
          </div>
        </div>
        <div style={{ marginTop: "1rem" }}>
          <button
            style={{ ...ctrlBtn(), border: "1px solid var(--accent)", color: "var(--accent)" }}
            type="button"
            onClick={() => {}}
          >
            Send Command
          </button>
        </div>
      </SectionCard>

      {/* 9. Logs — full width, scrollable */}
      <DeviceLogsPanel deviceSerial={device?.device_id} />

      {/* 10. Command History — full width */}
      <SectionCard title="Command History">
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: "1rem", gap: "1rem", flexWrap: "wrap" }}>
          <span className="text-xs" style={{ color: "var(--text-muted)" }}>{ctrlCmdHistory.length} commands</span>
          <div style={{ display: "flex", alignItems: "center", gap: "0.75rem" }}>
            <ToggleSwitch value={ctrlCmdAutoRefresh} onChange={setCtrlCmdAutoRefresh} onLabel="Auto-refresh" offLabel="Auto-refresh" />
            <button
              onClick={fetchCtrlCmdHistory}
              type="button"
              className="px-3 rounded-md text-xs hover:opacity-80 cursor-pointer"
              style={{ height: 30, backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)", border: "none" }}
            >
              Refresh
            </button>
          </div>
        </div>
        {ctrlCmdHistoryLoading ? (
          <p className="text-xs" style={{ color: "var(--text-muted)" }}>Loading...</p>
        ) : ctrlCmdHistory.length === 0 ? (
          <p className="text-xs" style={{ color: "var(--text-muted)" }}>
            No commands sent to this device yet.{" "}
            <button
              onClick={fetchCtrlCmdHistory}
              className="hover:opacity-80 cursor-pointer"
              style={{ color: "var(--accent)", textDecoration: "underline" }}
              type="button"
            >
              Load History
            </button>
          </p>
        ) : (
          <div className="rounded-md overflow-hidden border" style={{ borderColor: "var(--border-primary)" }}>
            <div style={{ maxHeight: 320, overflowY: "auto" }}>
              <table className="w-full text-xs">
                <thead style={{ position: "sticky", top: 0, zIndex: 1 }}>
                  <tr style={{ backgroundColor: "var(--bg-primary)", borderBottom: "1px solid var(--border-primary)" }}>
                    <th className="px-3 py-2.5 text-left font-medium" style={{ color: "var(--text-secondary)" }}>Time</th>
                    <th className="px-3 py-2.5 text-left font-medium" style={{ color: "var(--text-secondary)" }}>Command</th>
                    <th className="px-3 py-2.5 text-left font-medium" style={{ color: "var(--text-secondary)" }}>Status</th>
                    <th className="px-3 py-2.5 text-left font-medium" style={{ color: "var(--text-secondary)" }}>Details</th>
                  </tr>
                </thead>
                <tbody>
                  {ctrlCmdHistory.map((cmd, index) => {
                    const s = CTRL_STATUS_STYLES[cmd.status] || CTRL_STATUS_STYLES.pending;
                    const isExpanded = ctrlExpandedCmd === cmd.id;
                    return (
                      <>
                        <tr
                          key={cmd.id}
                          className="cursor-pointer hover:opacity-80"
                          style={{ borderBottom: (!isExpanded && index < ctrlCmdHistory.length - 1) ? "1px solid var(--border-primary)" : "none" }}
                          onClick={() => setCtrlExpandedCmd(isExpanded ? null : cmd.id)}
                        >
                          <td className="px-3 py-2 font-mono" style={{ color: "var(--text-muted)" }}>
                            {cmd.sent_at?.replace("T", " ").substring(0, 19)}
                          </td>
                          <td className="px-3 py-2 font-mono" style={{ color: "var(--text-heading)" }}>
                            {cmd.command_name}
                          </td>
                          <td className="px-3 py-2">
                            <span className="px-2 py-0.5 text-xs rounded-full" style={{ backgroundColor: s.bg, color: s.color }}>
                              {cmd.status}
                            </span>
                          </td>
                          <td className="px-3 py-2" style={{ color: "var(--text-muted)" }}>
                            {cmd.responded_at ? `Replied ${cmd.responded_at.replace("T", " ").substring(0, 19)}` : "Awaiting response..."}
                          </td>
                        </tr>
                        {isExpanded && (
                          <tr key={`${cmd.id}-exp`} style={{ borderBottom: index < ctrlCmdHistory.length - 1 ? "1px solid var(--border-primary)" : "none" }}>
                            <td colSpan={4} className="px-3 py-3" style={{ backgroundColor: "var(--bg-primary)" }}>
                              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "1rem" }}>
                                <div>
                                  <p className="text-xs font-medium mb-1" style={{ color: "var(--text-secondary)" }}>Sent Payload</p>
                                  <pre className="text-xs p-2 rounded overflow-auto font-mono" style={{ maxHeight: 160, backgroundColor: "var(--bg-card)", color: "var(--text-primary)" }}>
                                    {ctrlFormatPayload(cmd.command_payload) || "—"}
                                  </pre>
                                </div>
                                <div>
                                  <p className="text-xs font-medium mb-1" style={{ color: "var(--text-secondary)" }}>Response</p>
                                  <pre className="text-xs p-2 rounded overflow-auto font-mono" style={{ maxHeight: 160, backgroundColor: "var(--bg-card)", color: "var(--text-primary)" }}>
                                    {ctrlFormatPayload(cmd.response_payload) || "Waiting..."}
                                  </pre>
                                </div>
                              </div>
                            </td>
                          </tr>
                        )}
                      </>
                    );
                  })}
                </tbody>
              </table>
            </div>
          </div>
        )}
      </SectionCard>
    </div>
  );

  // ── Manage tab ──────────────────────────────────────────────────────────────
  const manageTab = (
    <div className="device-tab-stack">
      {/* Issues & Notes — full width */}
      <NotesPanel key={`manage-${id}`} deviceId={id} />

      {/* User Assignment */}
      <section className="rounded-lg border p-6 mt-4" style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}>
        <div className="flex items-center justify-between mb-4">
          <h2 className="text-lg font-semibold" style={{ color: "var(--text-heading)" }}>
            App Users ({deviceUsers.length})
          </h2>
          {canEdit && (
            <button
              type="button"
              onClick={() => { setShowUserSearch(true); setUserSearchQuery(""); setUserSearchResults([]); setTimeout(() => userSearchInputRef.current?.focus(), 50); }}
              className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md cursor-pointer hover:opacity-90 transition-opacity"
              style={{ backgroundColor: "var(--btn-primary)", color: "var(--text-white)" }}
            >
              + Add User
            </button>
          )}
        </div>

        {usersLoading ? (
          <p className="text-sm" style={{ color: "var(--text-muted)" }}>Loading users…</p>
        ) : deviceUsers.length === 0 ? (
          <p className="text-sm" style={{ color: "var(--text-muted)" }}>No users assigned. Users are added when they claim the device via the app, or you can add them manually.</p>
        ) : (
          <div className="space-y-2">
            {deviceUsers.map((u) => (
              <div
                key={u.user_id}
                className="flex items-center gap-3 px-3 py-2.5 rounded-md border"
                style={{ backgroundColor: "var(--bg-primary)", borderColor: "var(--border-secondary)" }}
              >
                {u.photo_url ? (
                  <img src={u.photo_url} alt="" className="w-8 h-8 rounded-full object-cover shrink-0" />
                ) : (
                  <div className="w-8 h-8 rounded-full flex items-center justify-center text-sm font-bold shrink-0"
                    style={{ backgroundColor: "var(--badge-blue-bg)", color: "var(--badge-blue-text)" }}>
                    {(u.display_name || u.email || "?")[0].toUpperCase()}
                  </div>
                )}
                <div className="flex-1 min-w-0">
                  <p className="text-sm font-medium" style={{ color: "var(--text-primary)" }}>{u.display_name || "—"}</p>
                  {u.email && <p className="text-xs" style={{ color: "var(--text-muted)" }}>{u.email}</p>}
                </div>
                {u.role && (
                  <span className="text-xs px-2 py-0.5 rounded-full shrink-0"
                    style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-muted)" }}>
                    {u.role}
                  </span>
                )}
                {canEdit && (
                  <button
                    type="button"
                    disabled={removingUser === u.user_id}
                    onClick={async () => {
                      setRemovingUser(u.user_id);
                      try {
                        await api.delete(`/devices/${id}/user-list/${u.user_id}`);
                        setDeviceUsers((prev) => prev.filter((x) => x.user_id !== u.user_id));
                      } catch (err) {
                        setError(err.message);
                      } finally {
                        setRemovingUser(null);
                      }
                    }}
                    className="text-xs px-2 py-0.5 rounded cursor-pointer hover:opacity-80 disabled:opacity-40 shrink-0"
                    style={{ color: "var(--danger-text)" }}
                  >
                    {removingUser === u.user_id ? "…" : "Remove"}
                  </button>
                )}
              </div>
            ))}
          </div>
        )}

        {/* User search modal */}
        {showUserSearch && (
          <div
            className="fixed inset-0 z-50 flex items-center justify-center"
            style={{ backgroundColor: "rgba(0,0,0,0.6)" }}
          >
            <div
              className="rounded-xl border p-6 w-full max-w-md"
              style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-primary)" }}
            >
              <h2 className="text-base font-bold mb-4" style={{ color: "var(--text-heading)" }}>
                Add User
              </h2>
              <div style={{ position: "relative" }} className="mb-3">
                <input
                  ref={userSearchInputRef}
                  type="text"
                  value={userSearchQuery}
                  onChange={(e) => setUserSearchQuery(e.target.value)}
                  placeholder="Search by name, email, or phone…"
                  className="w-full px-3 py-2 rounded-md text-sm border"
                  style={{ backgroundColor: "var(--bg-input)", borderColor: "var(--border-input)", color: "var(--text-primary)" }}
                />
                {userSearching && (
                  <span className="absolute right-3 top-1/2 -translate-y-1/2 text-xs" style={{ color: "var(--text-muted)" }}>…</span>
                )}
              </div>
              <div
                className="rounded-md border overflow-y-auto mb-4"
                style={{ borderColor: "var(--border-secondary)", maxHeight: 260, minHeight: 48 }}
              >
                {userSearchResults.length === 0 ? (
                  <p className="px-3 py-3 text-sm" style={{ color: "var(--text-muted)" }}>
                    {userSearching ? "Searching…" : userSearchQuery ? "No users found." : "Type to search users…"}
                  </p>
                ) : (
                  userSearchResults.map((u) => {
                    const alreadyAdded = deviceUsers.some((du) => du.user_id === u.id);
                    return (
                      <button
                        key={u.id}
                        type="button"
                        disabled={alreadyAdded || addingUser === u.id}
                        onClick={async () => {
                          setAddingUser(u.id);
                          try {
                            await api.post(`/devices/${id}/user-list`, { user_id: u.id });
                            setDeviceUsers((prev) => [...prev, {
                              user_id: u.id,
                              display_name: u.display_name,
                              email: u.email,
                              photo_url: u.photo_url,
                              role: "",
                            }]);
                            setShowUserSearch(false);
                          } catch (err) {
                            setError(err.message);
                          } finally {
                            setAddingUser(null);
                          }
                        }}
                        className="w-full text-left px-3 py-2.5 text-sm border-b last:border-b-0 transition-colors cursor-pointer disabled:opacity-50"
                        style={{ backgroundColor: "var(--bg-card)", borderColor: "var(--border-secondary)", color: "var(--text-primary)" }}
                      >
                        <span className="font-medium">{u.display_name || u.email || u.id}</span>
                        {u.email && <span className="block text-xs" style={{ color: "var(--text-muted)" }}>{u.email}</span>}
                        {alreadyAdded && <span className="ml-2 text-xs" style={{ color: "var(--success-text)" }}>Already added</span>}
                      </button>
                    );
                  })
                )}
              </div>
              <div className="flex justify-end">
                <button
                  type="button"
                  onClick={() => setShowUserSearch(false)}
                  className="px-4 py-1.5 text-sm rounded-md hover:opacity-80 cursor-pointer"
                  style={{ backgroundColor: "var(--bg-card-hover)", color: "var(--text-secondary)" }}
                >
                  Cancel
                </button>
              </div>
            </div>
          </div>
        )}
      </section>
    </div>
  );

  const renderTabContent = () => {
    if (activeTab === "dashboard") return dashboardTab;
    if (activeTab === "general") return generalInfoTab;
    if (activeTab === "bells") return bellMechanismsTab;
    if (activeTab === "clock") return clockAlertsTab;
    if (activeTab === "warranty") return warrantySubscriptionTab;
    if (activeTab === "manage") return manageTab;
    return controlTab;
  };

  return (
    <div>
      <div className="flex items-center justify-between mb-6">
        <div>
          <button onClick={() => navigate("/devices")} className="text-sm hover:underline mb-2 inline-block" style={{ color: "var(--accent)" }}>
            &larr; Back to Devices
          </button>
          <div className="flex items-center gap-4">
            <h1 className="text-2xl font-bold" style={{ color: "var(--text-heading)" }}>
              {device.device_name || "Unnamed Device"}
            </h1>
            <div style={{ width: 1, height: 24, backgroundColor: "var(--border-primary)" }} />
            <div style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
              <span className="w-3 h-3 rounded-full inline-block" style={{ backgroundColor: isOnline ? "var(--success-text)" : "var(--text-muted)" }} />
              <span className="text-sm font-semibold" style={{ color: isOnline ? "var(--success-text)" : "var(--text-muted)" }}>
                {isOnline ? "Online" : "Offline"}
                {mqttStatus && (
                  <span className="ml-2 text-xs font-normal" style={{ color: "var(--text-muted)" }}>
                    {formatSecondsAgo(mqttStatus.seconds_since_heartbeat)}
                  </span>
                )}
              </span>
            </div>
            {unresolvedIssues > 0 && (
              <>
                <div style={{ width: 1, height: 24, backgroundColor: "var(--border-primary)" }} />
                <button
                  onClick={() => {
                    setActiveTab("dashboard");
                    setNotesPanelTab("issues");
                    notesPanelRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
                  }}
                  className="flex items-center gap-1.5 px-2.5 py-1 text-xs font-medium rounded-md cursor-pointer hover:opacity-80 transition-opacity"
                  style={{ backgroundColor: "var(--danger-bg)", color: "var(--danger-text)", border: "none" }}
                  type="button"
                >
                  <svg
                    aria-hidden="true"
                    viewBox="0 0 24 24"
                    className="w-3.5 h-3.5"
                    fill="none"
                    stroke="currentColor"
                    strokeWidth="2"
                    strokeLinecap="round"
                    strokeLinejoin="round"
                  >
                    <path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.72 3h16.92a2 2 0 0 0 1.72-3l-8.47-14.14a2 2 0 0 0-3.42 0z" />
                    <line x1="12" y1="9" x2="12" y2="13" />
                    <line x1="12" y1="17" x2="12.01" y2="17" />
                  </svg>
                  {unresolvedIssues} Issue{unresolvedIssues !== 1 ? "s" : ""} Unresolved
                </button>
              </>
            )}
          </div>
        </div>
        {canEdit && (
          <div className="flex gap-2">
            <button
              onClick={() => setShowDelete(true)}
              className="px-4 py-2 text-sm rounded-md transition-colors"
              style={{ backgroundColor: "var(--danger)", color: "var(--text-white)" }}
              type="button"
            >
              Delete
            </button>
          </div>
        )}
      </div>

      <section className="device-tabs-wrap">
        <div className="device-tabs-bar">
          {TAB_DEFS.map((tab, index) => (
            <div key={tab.id} className="device-tab-item">
              <TabButton active={activeTab === tab.id} tone={tab.tone} onClick={() => setActiveTab(tab.id)}>
                {tab.label}
              </TabButton>
              {index < TAB_DEFS.length - 1 && <span className="device-tab-divider" aria-hidden="true" />}
            </div>
          ))}
        </div>
      </section>

      <div className="mt-5">{renderTabContent()}</div>

      <ConfirmDialog
        open={showDelete}
        title="Delete Device"
        message={`Are you sure you want to delete "${device.device_name || "this device"}" (${device.serial_number || device.device_id})? This action cannot be undone.`}
        onConfirm={handleDelete}
        onCancel={() => setShowDelete(false)}
      />
      <LocationModal
        open={editingLocation}
        onClose={() => setEditingLocation(false)}
        onSaved={loadData}
        device={device}
        coords={coords}
        id={id}
      />
      <AttributesModal
        open={editingAttributes}
        onClose={() => setEditingAttributes(false)}
        onSaved={loadData}
        device={device}
        attr={attr}
        id={id}
      />
      <LoggingModal
        open={editingLogging}
        onClose={() => setEditingLogging(false)}
        onSaved={loadData}
        attr={attr}
        id={id}
      />
      <MiscModal
        open={editingMisc}
        onClose={() => setEditingMisc(false)}
        onSaved={loadData}
        device={device}
        attr={attr}
        id={id}
      />
      <BellOutputsModal
        open={editingBellOutputs}
        onClose={() => setEditingBellOutputs(false)}
        onSaved={loadData}
        attr={attr}
        clock={clock}
        sub={sub}
        id={id}
      />
      <ClockSettingsModal
        open={editingClockSettings}
        onClose={() => setEditingClockSettings(false)}
        onSaved={loadData}
        attr={attr}
        clock={clock}
        sub={sub}
        id={id}
      />
      <AlertsModal
        open={editingAlerts}
        onClose={() => setEditingAlerts(false)}
        onSaved={loadData}
        attr={attr}
        clock={clock}
        id={id}
      />
      <BacklightModal
        open={editingBacklight}
        onClose={() => setEditingBacklight(false)}
        onSaved={loadData}
        attr={attr}
        clock={clock}
        sub={sub}
        id={id}
      />
      <SubscriptionModal
        open={editingSubscription}
        onClose={() => setEditingSubscription(false)}
        onSaved={loadData}
        sub={sub}
        id={id}
      />
      <WarrantyModal
        open={editingWarranty}
        onClose={() => setEditingWarranty(false)}
        onSaved={loadData}
        stats={stats}
        id={id}
      />
      <QrModal value={qrTarget} onClose={() => setQrTarget(null)} />
    </div>
  );
}


