// sleep.jsx — Sleep Timer: lives on each aircon's own card.
//
// The idea (grounded in sleep-temperature research — see the comment on
// buildOffsets/defaultTemps below for sources): set a bedtime + wake time and
// let that aircon ease its target temperature up a little through the night
// (cooler room helps sleep onset; less cooling is needed once you're asleep),
// then ramp down harder in the last hour before wake — saves energy and lines
// up with the body's own natural pre-wake temperature rise. Only three big
// vertical bars to drag (temperature only — no dragging in time, no small
// hit targets), so it stays as easy to use as the aircon's own temp slider.
//
// Firing happens entirely on Ashlee's backend now (confirmed working even
// with the browser closed) — this file used to also run its own client-side
// "best-effort" scheduler as a stand-in before that existed, but running both
// at once was sending conflicting/duplicate commands to the aircon, so the
// local one has been removed. This file is now just the editing UI (draft a
// schedule, save it to the backend) and the "last night" log/estimate reader.
//
// Public surface (used by aircon.jsx): <AirconSleepSheet c={c} deviceId label onClose />
//   and the useDeviceSleepSchedule(deviceId) hook, both used from the aircon's
//   own tile/panel — there is no separate global entry point any more.
(function () {
const { useState, useEffect, useRef } = React;

// ── constants ─────────────────────────────────────────────────────────────
const SLEEP_KEY = 'savehor_sleep_schedules';         // { [deviceId]: schedule }

const AC_TMIN = window.AC_TMIN, AC_TMAX = window.AC_TMAX;

// ── time helpers ─────────────────────────────────────────────────────────
function parseHHMM(s) {
  const [h, m] = String(s || '00:00').split(':').map((n) => parseInt(n, 10) || 0);
  return { h, m };
}
function fmtClock(date) {
  return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' });
}

// Given a schedule + "now", find the (bedtime, wake) Date pair that "now"
// currently belongs to — either tonight's upcoming window, or the one we're
// already inside (it's 2am and bedtime was yesterday evening).
function resolveWindow(schedule, now) {
  const { h: bh, m: bm } = parseHHMM(schedule.bedtime);
  const { h: wh, m: wm } = parseHHMM(schedule.wake);
  let bed = new Date(now); bed.setHours(bh, bm, 0, 0);
  let wake = new Date(bed); wake.setHours(wh, wm, 0, 0);
  if (wake <= bed) wake.setDate(wake.getDate() + 1);   // wake clock-time is next calendar day
  if (now < bed) {
    const bedPrev = new Date(bed); bedPrev.setDate(bedPrev.getDate() - 1);
    const wakePrev = new Date(wake); wakePrev.setDate(wakePrev.getDate() - 1);
    if (now >= bedPrev && now <= wakePrev) { bed = bedPrev; wake = wakePrev; }
  }
  return { bed, wake };
}
function durationMin(bed, wake) { return Math.round((wake - bed) / 60000); }

// ── the curve — three fixed moments, only temperature is ever dragged ────
// Sources (see also the chat writeup for full citations):
//  • Sleep Foundation / Cleveland Clinic: a cool bedroom supports the body's
//    natural pre-sleep core-temperature drop and sleep onset.
//  • Commercial AC "sleep mode" behavior (Haier/Frigidaire/GE): nearly every
//    brand steps the setpoint up a couple of degrees ~1-2h in, then holds —
//    avoids overcooling once you're asleep, without an abrupt jump.
//  • DOE / setback studies: an overnight setback of a few degrees saves
//    several percent in energy per degree, rising with the size of the setback.
//  • Sleep-mode patents (e.g. US8146833): gradually raising temperature in the
//    hour before wake assists a comfortable, natural-feeling wake-up — mirrors
//    the body's own pre-wake core-temperature rise — while cutting the compressor's
//    hardest overnight hours.
function buildOffsets(duration) {
  const p2 = Math.max(30, Math.min(120, Math.round(duration * 0.25)));
  const p3 = Math.max(p2 + 30, duration - 60);
  return [0, p2, Math.min(p3, Math.max(p2 + 15, duration - 15))];
}
function defaultTemps(baseline) {
  const base = baseline || 24;
  return [base, Math.min(AC_TMAX, base + 1), Math.min(AC_TMAX, base + 3)];
}
// Only affects the starting suggestion for a schedule that's never been set
// up — never auto-enables anything or touches a live aircon by itself. If
// it's evening/night (7pm–3am) when someone opens this for the first time,
// they're likely setting it up close to actual bedtime, so default to "now"
// (rounded to the nearest 15 min) instead of a generic 11pm. Daytime keeps
// the generic default since there's no signal to infer from.
function inferBedtime() {
  const now = new Date();
  const h = now.getHours();
  if (h >= 19 || h < 3) {
    const rounded = new Date(Math.round(now.getTime() / 900000) * 900000);
    return `${String(rounded.getHours()).padStart(2, '0')}:${String(rounded.getMinutes()).padStart(2, '0')}`;
  }
  return '23:00';
}
function defaultSchedule() {
  return { enabled: false, bedtime: inferBedtime(), wake: '07:00', powerOffAtWake: true, temps: defaultTemps(24) };
}

// ── store — a tiny module-level pub/sub over localStorage, keyed by device id.
// Plain (not React state) so any mounted UI (the tile's moon icon, the edit
// sheet) shares one source of truth without prop-drilling, matching how
// SaveHorAPI/AirconAPI are used elsewhere in this app. The backend database
// is the real source of truth; this is the local optimistic copy.
const listeners = new Set();
function loadAll() {
  try {
    const raw = localStorage.getItem(SLEEP_KEY);
    if (raw) return JSON.parse(raw);
  } catch (e) { /* private mode / corrupt value → fall through to empty */ }
  return {};
}
function loadOne(deviceId) { return loadAll()[deviceId] || null; }
// Writes local storage immediately (optimistic UI + offline fallback), but
// the backend is the real source of truth now that its scheduler is live —
// returns the backend save's promise so the caller can find out whether the
// change actually reached the server, instead of silently assuming it did.
function saveOne(deviceId, schedule) {
  const all = loadAll();
  all[deviceId] = schedule;
  try { localStorage.setItem(SLEEP_KEY, JSON.stringify(all)); } catch (e) { /* storage blocked */ }
  listeners.forEach((fn) => fn(all));
  if (window.SaveHorAPI && SaveHorAPI.saveDeviceSleepSchedule) {
    return SaveHorAPI.saveDeviceSleepSchedule(deviceId, schedule);
  }
  return Promise.resolve();
}
function useDeviceSleepSchedule(deviceId) {
  const [all, setAll] = useState(loadAll);
  useEffect(() => {
    const fn = (a) => setAll(a);
    listeners.add(fn);
    return () => listeners.delete(fn);
  }, []);
  return deviceId ? (all[deviceId] || null) : null;
}

// The client-side firing engine (a tab-local setInterval that sent the actual
// aircon commands) has been REMOVED now that Ashlee's backend scheduler is
// confirmed live and reliable (verified by closing the browser overnight and
// the schedule still fired). Having both running at once was firing
// conflicting/duplicate commands at the aircon — that's what was causing fan
// speed to change unexpectedly and made "disable" feel like it didn't stick,
// since the browser-side engine had no idea the backend had its own copy.
// The backend is now the ONLY thing that ever sends a scheduled command;
// this file is purely the editing UI (draft a schedule, save it) plus the
// "last night" log/estimate reader — see resolveWindow/durationMin/
// buildOffsets below, still used for the UI's own display of when each step
// will happen, not for firing anything.

window.useDeviceSleepSchedule = useDeviceSleepSchedule;

// ── UI: one big draggable bar — same single-axis drag as the aircon's own
// temp slider, just simpler (no room marker/mode badge) and no time dragging.
function SleepBar({ c, label, time, value, accent, onChange }) {
  const ref = useRef(null);
  const [drag, setDrag] = useState(false);
  const H = 160;

  const fromY = (clientY) => {
    const r = ref.current.getBoundingClientRect();
    const frac = 1 - (clientY - r.top) / r.height;
    return Math.max(AC_TMIN, Math.min(AC_TMAX, Math.round(AC_TMIN + frac * (AC_TMAX - AC_TMIN))));
  };
  const down = (e) => { e.currentTarget.setPointerCapture && e.currentTarget.setPointerCapture(e.pointerId); setDrag(true); onChange(fromY(e.clientY)); };
  const move = (e) => { if (e.buttons > 0) onChange(fromY(e.clientY)); };
  const up = () => setDrag(false);
  const pct = (value - AC_TMIN) / (AC_TMAX - AC_TMIN);

  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, flex: 1, minWidth: 0 }}>
      <div style={{ fontSize: 16, fontWeight: 700, color: c.text, fontVariantNumeric: 'tabular-nums' }}>{value}°</div>
      <div ref={ref} onPointerDown={down} onPointerMove={move} onPointerUp={up} onPointerCancel={up}
        style={{
          position: 'relative', width: '100%', height: H, borderRadius: 20, overflow: 'hidden',
          background: c.surfaceAlt, border: `1px solid ${c.borderSoft}`,
          touchAction: 'none', userSelect: 'none', cursor: 'ns-resize',
        }}>
        <div style={{
          position: 'absolute', left: 0, right: 0, bottom: 0, height: `${pct * 100}%`,
          background: accent, opacity: 0.85, transition: drag ? 'none' : 'height .2s ease',
        }} />
      </div>
      <div style={{ textAlign: 'center' }}>
        <div style={{ fontSize: 11.5, fontWeight: 700, color: c.text }}>{label}</div>
        <div style={{ fontSize: 10.5, color: c.textLight, fontVariantNumeric: 'tabular-nums' }}>{time}</div>
      </div>
    </div>
  );
}

// ── overnight log + savings estimate ─────────────────────────────────────
// The %-saved figure here is a rough estimate, not a measurement — there's no
// way to know what would have happened without the schedule. It's built from:
//  • ACTUAL data: real kWh used overnight (getUsageHistory), and the actual
//    temps/times the log says fired (not the current schedule, which may
//    have changed since).
//  • ONE assumption: that without the schedule, the resident would have left
//    the aircon at its bedtime temp (the coldest step) all night — the most
//    representative "set it and forget it" baseline.
//  • ONE heuristic coefficient, grounded in the same overnight-setback
//    research cited above (buildOffsets): roughly a few % less cooling
//    energy per °C of setback. We use a conservative 3%/°C and cap the total
//    at 30% so a big setback never produces an implausible number.
// Always shown in the UI as "≈ estimated", never as a precise/guaranteed figure.
const SAVINGS_PCT_PER_DEGREE = 0.03;
const SAVINGS_PCT_CAP = 0.30;

function estimateSavings(events, actualKwh) {
  const temps = events.filter((e) => e.action === 'temp')
    .map((e) => ({ temp: Number(e.value), at: new Date(e.sent_at) }))
    .sort((a, b) => a.at - b.at);
  if (temps.length < 2) return null;   // not enough of the night logged yet

  const offEvent = events.find((e) => e.step === 'wake_off');
  const endAt = offEvent ? new Date(offEvent.sent_at) : new Date();
  const baseline = temps[0].temp;

  let degreeHours = 0, totalHours = 0;
  for (let i = 0; i < temps.length; i++) {
    const start = temps[i].at;
    const end = i + 1 < temps.length ? temps[i + 1].at : endAt;
    const hours = Math.max(0, (end - start) / 3600000);
    degreeHours += hours * Math.max(0, temps[i].temp - baseline);
    totalHours += hours;
  }
  if (totalHours <= 0) return null;

  const avgSetback = degreeHours / totalHours;
  const pct = Math.min(SAVINGS_PCT_CAP, avgSetback * SAVINGS_PCT_PER_DEGREE);
  const kwhSaved = (actualKwh != null && pct > 0) ? (actualKwh * pct) / (1 - pct) : null;
  return { pct, kwhSaved, baseline };
}

// Actual overnight kWh for one device, from the same usage-history endpoint
// the rest of the app already charts — bucketed hourly, so this is a sum of
// whichever hour-buckets fall inside the sleep window (a rough overlap, fine
// for an estimate).
async function fetchActualKwh(deviceId, startAt, endAt) {
  try {
    const hist = await SaveHorAPI.getUsageHistory('24h');
    const series = (hist && hist.series) || [];
    let sum = 0, any = false;
    for (const row of series) {
      if (row.device_id !== deviceId) continue;
      const t = new Date(row.bucket_start);
      if (t >= startAt && t <= endAt) { sum += Number(row.kwh) || 0; any = true; }
    }
    return any ? sum : null;
  } catch (e) { return null; }
}

const LOG_STEP_LABEL = { bedtime: 'Bedtime', overnight: 'Overnight', before_wake: 'Before wake', wake_off: 'Wake' };
const LOG_ACTION_VALUE = (action, value) =>
  action === 'temp' ? `${value}°` : action === 'mode' ? (value === 'off' ? 'Off' : 'On') : value;
const fmtLogTime = (iso) => new Date(iso).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' });

// ── UI: "last night" log sheet — what actually fired, and the estimate above ─
function AirconSleepLogSheet({ c, deviceId, label, onClose }) {
  const [shown, setShown] = useState(false);
  useEffect(() => { requestAnimationFrame(() => setShown(true)); }, []);
  const close = () => { setShown(false); setTimeout(onClose, 220); };

  const [loading, setLoading] = useState(true);
  const [events, setEvents] = useState([]);
  const [savings, setSavings] = useState(null);

  useEffect(() => {
    let alive = true;
    (async () => {
      let data = null;
      try { data = await SaveHorAPI.getDeviceSleepScheduleLog(deviceId); }
      catch (e) { /* 404 = nothing logged yet, or the endpoint doesn't exist yet — either way, empty state */ }
      if (!alive) return;
      const evs = (data && data.events) || [];
      setEvents(evs);
      setLoading(false);
      if (evs.length >= 2) {
        const bedEvt = evs.find((e) => e.step === 'bedtime') || evs[0];
        const offEvt = evs.find((e) => e.step === 'wake_off');
        const startAt = new Date(bedEvt.sent_at);
        const endAt = offEvt ? new Date(offEvt.sent_at) : new Date();
        const kwh = await fetchActualKwh(deviceId, startAt, endAt);
        if (!alive) return;
        setSavings(estimateSavings(evs, kwh));
      }
    })();
    return () => { alive = false; };
  }, [deviceId]);

  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 170 }}>
      <div onClick={close} style={{
        position: 'absolute', inset: 0, background: 'rgba(20,30,25,0.44)',
        opacity: shown ? 1 : 0, transition: 'opacity .25s ease',
      }} />
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 0, maxHeight: '82vh',
        background: c.bg, borderRadius: '28px 28px 0 0', boxShadow: '0 -8px 40px rgba(20,30,25,0.18)',
        display: 'flex', flexDirection: 'column', overflow: 'hidden',
        transform: shown ? 'translateY(0)' : 'translateY(100%)',
        transition: 'transform .32s cubic-bezier(.32,.72,0,1)',
      }}>
        <div style={{ width: 38, height: 4, background: c.border, borderRadius: 2, margin: '10px auto 4px', flexShrink: 0 }} />
        <div style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: '6px 22px calc(24px + env(safe-area-inset-bottom))' }}>

          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
            <div style={{ width: 44, height: 44, borderRadius: 13, background: c.primarySoft, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <IcClock size={22} color={c.primary} />
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 19, fontWeight: 700, color: c.text, letterSpacing: -0.3 }}>Last night</div>
              <div style={{ fontSize: 13, color: c.textMuted, marginTop: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{label || 'Aircon'}</div>
            </div>
            <button onClick={close} aria-label="Close" style={{ width: 34, height: 34, border: 'none', borderRadius: 999, background: c.surfaceAlt, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <IcX size={16} color={c.textMuted} />
            </button>
          </div>

          {loading && (
            <div style={{ fontSize: 13, color: c.textMuted, textAlign: 'center', padding: '32px 0' }}>Loading…</div>
          )}

          {!loading && events.length === 0 && (
            <div style={{ fontSize: 13, color: c.textMuted, textAlign: 'center', padding: '32px 12px', lineHeight: 1.5 }}>
              Nothing logged yet — either the sleep timer hasn't run overnight yet, or this device is still on the older tab-only scheduler.
            </div>
          )}

          {!loading && events.length > 0 && (
            <>
              {savings && (
                <div style={{ background: c.primarySoft, borderRadius: 16, padding: '14px 16px', marginBottom: 16 }}>
                  <div style={{ fontSize: 11, fontWeight: 700, color: c.primary, letterSpacing: 1, textTransform: 'uppercase' }}>Estimated savings</div>
                  <div style={{ fontSize: 22, fontWeight: 700, color: c.text, marginTop: 4, fontVariantNumeric: 'tabular-nums' }}>
                    ≈{Math.round(savings.pct * 100)}%{savings.kwhSaved != null ? <> · ≈{savings.kwhSaved.toFixed(2)} kWh</> : ''}
                  </div>
                  <div style={{ fontSize: 11.5, color: c.textMuted, marginTop: 4, lineHeight: 1.4 }}>
                    Rough estimate vs. holding {savings.baseline}° all night — not a precise or measured figure.
                  </div>
                </div>
              )}

              <div style={{ display: 'flex', flexDirection: 'column' }}>
                {events.map((e, i) => (
                  <div key={i} style={{
                    display: 'flex', alignItems: 'center', gap: 10, padding: '10px 4px',
                    borderBottom: i < events.length - 1 ? `1px solid ${c.borderSoft}` : 'none',
                  }}>
                    <div style={{ fontSize: 12.5, fontWeight: 700, color: c.textLight, width: 64, flexShrink: 0, fontVariantNumeric: 'tabular-nums' }}>
                      {fmtLogTime(e.sent_at)}
                    </div>
                    <div style={{ flex: 1, minWidth: 0, fontSize: 13.5, fontWeight: 600, color: c.text }}>
                      {LOG_STEP_LABEL[e.step] || e.step}
                    </div>
                    <div style={{ fontSize: 13, fontWeight: 700, color: c.primary, fontVariantNumeric: 'tabular-nums' }}>
                      {LOG_ACTION_VALUE(e.action, e.value)}
                    </div>
                  </div>
                ))}
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

// ── UI: the sheet — scoped to one aircon, opened from that aircon's own card
function AirconSleepSheet({ c, deviceId, label, onClose }) {
  const [shown, setShown] = useState(false);
  useEffect(() => { requestAnimationFrame(() => setShown(true)); }, []);
  const close = () => { setShown(false); setTimeout(onClose, 220); };

  const [draft, setDraft] = useState(() => loadOne(deviceId) || defaultSchedule());
  const [wasEnabled] = useState(() => !!(loadOne(deviceId) && loadOne(deviceId).enabled));
  const [logOpen, setLogOpen] = useState(false);
  const [saving, setSaving] = useState(false);
  const [saveError, setSaveError] = useState('');

  const { bed, wake } = resolveWindow(draft, new Date());
  const duration = durationMin(bed, wake);
  const offsets = buildOffsets(duration);
  const times = offsets.map((min) => fmtClock(new Date(bed.getTime() + min * 60000)));
  const barLabels = ['Bedtime', 'Overnight', 'Before wake'];

  const setTime = (key) => (e) => setDraft((d) => ({ ...d, [key]: e.target.value }));
  const setTemp = (i, val) => setDraft((d) => ({ ...d, temps: d.temps.map((t, idx) => (idx === i ? val : t)) }));
  const reset = () => setDraft((d) => ({ ...d, temps: defaultTemps(d.temps[0]) }));

  // Waits for the backend save to actually succeed before closing — the
  // backend is what really runs the schedule now, so an unconfirmed save
  // (network hiccup, expired session, etc.) must never be shown as "done."
  const save = async (enabled) => {
    setSaving(true);
    setSaveError('');
    try {
      await saveOne(deviceId, { ...draft, enabled });
      close();
    } catch (e) {
      setSaving(false);
      setSaveError(e.message || "Couldn't save — check your connection and try again.");
    }
  };

  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 160 }}>
      <div onClick={close} style={{
        position: 'absolute', inset: 0, background: 'rgba(20,30,25,0.44)',
        opacity: shown ? 1 : 0, transition: 'opacity .25s ease',
      }} />
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 0, maxHeight: '88vh',
        background: c.bg, borderRadius: '28px 28px 0 0', boxShadow: '0 -8px 40px rgba(20,30,25,0.18)',
        display: 'flex', flexDirection: 'column', overflow: 'hidden',
        transform: shown ? 'translateY(0)' : 'translateY(100%)',
        transition: 'transform .32s cubic-bezier(.32,.72,0,1)',
      }}>
        <div style={{ width: 38, height: 4, background: c.border, borderRadius: 2, margin: '10px auto 4px', flexShrink: 0 }} />
        <div style={{ flex: 1, minHeight: 0, overflowY: 'auto', padding: '6px 22px calc(24px + env(safe-area-inset-bottom))' }}>

          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
            <div style={{ width: 44, height: 44, borderRadius: 13, background: c.primarySoft, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <IcMoon size={22} color={c.primary} />
            </div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 19, fontWeight: 700, color: c.text, letterSpacing: -0.3 }}>Sleep timer</div>
              <div style={{ fontSize: 13, color: c.textMuted, marginTop: 1, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{label || 'Aircon'}</div>
            </div>
            <button onClick={close} aria-label="Close" style={{ width: 34, height: 34, border: 'none', borderRadius: 999, background: c.surfaceAlt, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
              <IcX size={16} color={c.textMuted} />
            </button>
          </div>

          <div style={{ display: 'flex', gap: 10 }}>
            <Field c={c} label="Bedtime" type="time" value={draft.bedtime} onChange={setTime('bedtime')} style={{ flex: 1 }} />
            <Field c={c} label="Wake up" type="time" value={draft.wake} onChange={setTime('wake')} style={{ flex: 1 }} />
          </div>

          <div style={{ marginTop: 18, background: c.surface, border: `1px solid ${c.borderSoft}`, borderRadius: 16, padding: '14px 14px 12px' }}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
              <div style={{ fontSize: 11, fontWeight: 700, color: c.textLight, letterSpacing: 1, textTransform: 'uppercase' }}>Overnight temperature</div>
              <button onClick={reset} style={{ border: 'none', background: 'transparent', cursor: 'pointer', fontSize: 12, fontWeight: 700, color: c.primary, fontFamily: 'inherit' }}>Reset</button>
            </div>
            <div style={{ display: 'flex', gap: 14 }}>
              {draft.temps.map((t, i) => (
                <SleepBar key={i} c={c} label={barLabels[i]} time={times[i]} value={t} accent={c.primary} onChange={(v) => setTemp(i, v)} />
              ))}
            </div>
            <div style={{ fontSize: 12, color: c.textMuted, marginTop: 10, textAlign: 'center' }}>Drag any bar up or down to change its temperature.</div>
          </div>

          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: 14, padding: '2px 2px' }}>
            <div style={{ fontSize: 13.5, fontWeight: 600, color: c.text }}>Turn off at wake time</div>
            <button onClick={() => setDraft((d) => ({ ...d, powerOffAtWake: !d.powerOffAtWake }))} style={{
              width: 46, height: 28, borderRadius: 14, border: 'none', cursor: 'pointer', position: 'relative',
              background: draft.powerOffAtWake ? c.primary : c.borderSoft, transition: 'background .15s',
            }}>
              <span style={{
                position: 'absolute', top: 3, left: draft.powerOffAtWake ? 21 : 3, width: 22, height: 22,
                borderRadius: 11, background: '#fff', transition: 'left .15s', boxShadow: '0 1px 3px rgba(0,0,0,0.2)',
              }} />
            </button>
          </div>

          <div style={{ fontSize: 11.5, color: c.textLight, marginTop: 16, lineHeight: 1.5 }}>
            Runs on our servers, so it keeps going overnight even if you close the app or lock your phone.
          </div>

          <button onClick={() => setLogOpen(true)} style={{
            display: 'flex', alignItems: 'center', gap: 10, width: '100%', marginTop: 14,
            padding: '12px 14px', borderRadius: 14, border: `1px solid ${c.borderSoft}`,
            background: c.surface, cursor: 'pointer', fontFamily: 'inherit', textAlign: 'left',
          }}>
            <IcClock size={16} color={c.textMuted} />
            <span style={{ flex: 1, fontSize: 13.5, fontWeight: 600, color: c.text }}>Last night's log</span>
            <IcChevR size={14} color={c.textLight} />
          </button>

          {saveError && (
            <div style={{
              fontSize: 12.5, fontWeight: 500, color: c.danger, background: c.dangerSoft,
              borderRadius: 12, padding: '10px 14px', marginTop: 14, lineHeight: 1.4,
            }}>{saveError}</div>
          )}

          <div style={{ display: 'flex', gap: 10, marginTop: 16 }}>
            {wasEnabled && (
              <Btn kind="secondary" size="lg" c={c} disabled={saving} onClick={() => save(false)} style={{ flex: 1 }}>Turn off</Btn>
            )}
            <Btn kind="primary" size="lg" full={!wasEnabled} c={c} disabled={saving} onClick={() => save(true)}
              style={wasEnabled ? { flex: 1 } : undefined}>
              {saving ? 'Saving…' : wasEnabled ? 'Save changes' : 'Turn on sleep timer'}
            </Btn>
          </div>
        </div>
      </div>

      {logOpen && <AirconSleepLogSheet c={c} deviceId={deviceId} label={label} onClose={() => setLogOpen(false)} />}
    </div>
  );
}

Object.assign(window, { AirconSleepSheet });
})();
