const CIRC = 2 * Math.PI * 52;  // ~326.73
const ring = document.getElementById('ring');
const cd = document.getElementById('countdown');
const label = document.getElementById('label');
const badge = document.getElementById('badge');
const badgeTxt = document.getElementById('badge-text');
const glow = document.getElementById('glow');
const durInput = document.getElementById('dur-input');
const btnRun = document.getElementById('btn-run');
const btnLearn = document.getElementById('btn-learn');
const btnOpen = document.getElementById('btn-open');
const btnClose = document.getElementById('btn-close');

function fmt(ms) {
  if (ms <= 0) return '0.0';
  const totalSec = ms / 1000;
  if (totalSec < 60) return totalSec.toFixed(1);
  const m = Math.floor(totalSec / 60);
  const s = Math.floor(totalSec % 60);
  return m + ':' + String(s).padStart(2, '0');
}

function fmtDur(ms) {
  const s = (ms / 1000).toFixed(1);
  return s + 's';
}

let state = null;
let serverOffset = 0; // server time - client time

async function poll() {
  try {
    const r = await fetch('/api/status');
    state = await r.json();
    // Compute clock offset so countdown works even with clock skew
    serverOffset = state.now - Date.now();
  } catch (e) { /* retry next tick */ }
}

function render() {
  if (!state) return;

  // Sync duration input if user isn't editing it
  if (document.activeElement !== durInput) {
    const secs = Math.round(state.storedDuration / 1000);
    if (durInput.value !== String(secs)) durInput.value = secs;
  }

  // Disable controls while timer is running or learning
  const running = state.timerActive;
  const learning = state.countMode;
  btnRun.disabled = running || learning;
  durInput.disabled = running || learning;
  btnOpen.disabled = running || learning;
  btnClose.disabled = running || learning;

  // Learn button toggles label and action
  if (learning) {
    btnLearn.textContent = '⏹ Stop Learn';
    btnLearn.onclick = doStopLearn;
    btnLearn.classList.add('active-learn');
  } else {
    btnLearn.textContent = '📐 Learn';
    btnLearn.onclick = doLearn;
    btnLearn.classList.remove('active-learn');
  }
  btnLearn.disabled = running;  // can't start learn while timer runs

  if (state.timerActive && state.timerStartedAt && state.timerEndsAt) {
    const total = state.timerEndsAt - state.timerStartedAt;
    const serverNow = Date.now() + serverOffset;
    const remaining = Math.max(0, state.timerEndsAt - serverNow);
    const progress = total > 0 ? remaining / total : 0;

    ring.style.strokeDashoffset = CIRC * (1 - progress);
    cd.textContent = fmt(remaining);
    cd.className = 'countdown';
    label.textContent = 'REMAINING';
    badge.className = 'badge running';
    badgeTxt.textContent = 'Timer Running';
    glow.classList.add('active');
  } else if (state.countMode) {
    const elapsed = state.countStart
      ? Math.max(0, (Date.now() + serverOffset) - state.countStart)
      : 0;
    ring.style.strokeDashoffset = CIRC;
    cd.textContent = fmt(elapsed);
    cd.className = 'countdown';
    label.textContent = 'LEARNING';
    badge.className = 'badge counting';
    badgeTxt.textContent = 'Count Mode';
    glow.classList.remove('active');
  } else {
    ring.style.strokeDashoffset = CIRC;
    cd.textContent = '—';
    cd.className = 'countdown idle';
    label.textContent = 'IDLE';
    badge.className = 'badge closed';
    badgeTxt.textContent = 'Idle';
    glow.classList.remove('active');
  }
}

// Quindar tones via Web Audio API
let audioCtx = null;
let endBeeped = false;
let wasTimerActive = false;

function playTone(freq) {
  if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  const t = audioCtx.currentTime;
  const osc = audioCtx.createOscillator();
  const gain = audioCtx.createGain();
  osc.type = 'sine';
  osc.frequency.value = freq;
  gain.gain.setValueAtTime(0.3, t);
  gain.gain.setValueAtTime(0.3, t + 0.24);
  gain.gain.linearRampToValueAtTime(0, t + 0.25);
  osc.connect(gain);
  gain.connect(audioCtx.destination);
  osc.start(t);
  osc.stop(t + 0.25);
}

function checkBeep() {
  if (!state) return;
  const active = state.timerActive && state.timerStartedAt && state.timerEndsAt;

  // Start tone (2525 Hz) when timer begins
  if (active && !wasTimerActive) {
    playTone(2525);
    endBeeped = false;
  }

  // End tone (2475 Hz) at 10s remaining
  if (active) {
    const serverNow = Date.now() + serverOffset;
    const remaining = Math.max(0, state.timerEndsAt - serverNow);
    if (remaining <= 10000 && remaining > 0 && !endBeeped) {
      endBeeped = true;
      playTone(2475);
    }
  } else {
    endBeeped = false;
  }

  wasTimerActive = !!active;
}

// ── Wake Lock ──
let wakeLock = null;
let lockAcquired = false;

async function requestWakeLock() {
  try {
    if ('wakeLock' in navigator && !wakeLock) {
      wakeLock = await navigator.wakeLock.request('screen');
      wakeLock.addEventListener('release', () => { wakeLock = null; });
    }
  } catch (e) { console.error('Wake Lock error:', e); }
}

function releaseWakeLock() {
  if (wakeLock) { wakeLock.release().catch(() => { }); wakeLock = null; }
}

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible' && lockAcquired) requestWakeLock();
});

function checkWakeLock() {
  if (!state) return;
  const active = state.timerActive || state.countMode;
  if (active && !lockAcquired) {
    requestWakeLock();
    lockAcquired = true;
  } else if (!active && lockAcquired) {
    releaseWakeLock();
    lockAcquired = false;
  }
}

// Poll server every 2s, render locally at 20fps for smooth countdown
poll();
setInterval(poll, 2000);
setInterval(() => { render(); checkBeep(); checkWakeLock(); }, 50);

// ── Control actions ──
async function apiPost(path, body = {}) {
  try {
    const r = await fetch(path, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body)
    });
    const d = await r.json();
    if (!d.ok) console.error('API error:', d.error);
    await poll();
  } catch (e) { console.error('API call failed:', e); }
}

async function doRun() {
  // Save duration first if changed
  const secs = parseInt(durInput.value, 10);
  if (secs > 0 && secs <= 300) {
    await apiPost('/api/duration', { seconds: secs });
  }
  await apiPost('/api/run');
}

function doOpen() { apiPost('/api/open'); }
function doClose() { apiPost('/api/close'); }

async function doLearn() { await apiPost('/api/learn'); }
async function doStopLearn() { await apiPost('/api/stop-learn'); }


// Also save duration on Enter or blur
durInput.addEventListener('change', () => {
  const secs = parseInt(durInput.value, 10);
  if (secs > 0 && secs <= 300) {
    apiPost('/api/duration', { seconds: secs });
  }
});

// ── Button online status ──
const deviceDot = document.getElementById('device-dot');
const deviceState = document.getElementById('device-state');
const valveDot = document.getElementById('valve-dot');
const valveState = document.getElementById('valve-state');

function updateDeviceWidget(dot, stateEl, online) {
  if (online === true) {
    dot.className = 'device-dot online';
    stateEl.className = 'device-state online';
    stateEl.textContent = 'Online';
  } else if (online === false) {
    dot.className = 'device-dot offline';
    stateEl.className = 'device-state offline';
    stateEl.textContent = 'Offline';
  } else {
    dot.className = 'device-dot';
    stateEl.className = 'device-state';
    stateEl.textContent = 'Unknown';
  }
}

async function pollButtonOnline() {
  try {
    const r = await fetch('/api/button-online');
    const d = await r.json();
    updateDeviceWidget(deviceDot, deviceState, d.online);
  } catch {
    updateDeviceWidget(deviceDot, deviceState, null);
  }
}

async function pollValveOnline() {
  try {
    const r = await fetch('/api/valve-online');
    const d = await r.json();
    updateDeviceWidget(valveDot, valveState, d.online);
  } catch {
    updateDeviceWidget(valveDot, valveState, null);
  }
}

pollButtonOnline();
pollValveOnline();
setInterval(pollButtonOnline, 10000);
setInterval(pollValveOnline, 10000);
