// KueSync — Companion variables
// © 2026 Krometis Analytics. All rights reserved.

export function getVariableDefinitions() {
  const defs = [
    { variableId: 'connected', name: 'Any agent connected (true/false)' },
    { variableId: 'connected_count', name: 'Agents connected (socket open)' },
    { variableId: 'healthy_count', name: 'Agents healthy (reporting, not stale)' },
    { variableId: 'target_count', name: 'Agents configured' },
    { variableId: 'app', name: 'Active app on primary machine' },
    { variableId: 'current_slide', name: 'Current slide — real Keynote number (primary)' },
    { variableId: 'total_slides', name: 'Total slides — real count incl. hidden (primary)' },
    { variableId: 'current_slide_shown', name: 'Current slide — excluding hidden/skipped (primary)' },
    { variableId: 'total_slides_shown', name: 'Total slides — excluding hidden/skipped (primary)' },
    { variableId: 'playing', name: 'Slideshow playing (primary, true/false)' },
    { variableId: 'fleet_in_sync', name: 'All machines on the same slide (in sync / DRIFT)' },
    { variableId: 'show_running', name: 'A slideshow is running on the primary (true/false)' },
    { variableId: 'slide_top', name: 'Slide button top line (Slide / No show)' },
    { variableId: 'slide_bot', name: 'Slide button bottom line (5/10 / stopped)' },
    { variableId: 'fleet_phase', name: 'Fleet phase (paused when no show / in sync / DRIFT)' },
    { variableId: 'fleet_spread', name: 'Slide spread across machines (0 = locked)' },
    { variableId: 'fleet_slides', name: 'Per-machine slides, e.g. 5·5·4 (✕ = unhealthy)' },
    { variableId: 'cue_running', name: 'Cue running (true/false)' },
    // Cue light (hardware-style): lamp state mirroring the fleet's last move direction.
    { variableId: 'cue_signal', name: 'Cue light signal (go / back / idle)' },
    { variableId: 'cue_go', name: 'Cue light = GO / forward (true/false)' },
    { variableId: 'cue_back', name: 'Cue light = BACK / reverse (true/false)' },
    { variableId: 'build_step', name: 'Build step on current slide (inferred; Nexts since slide changed)' },
    { variableId: 'build_index', name: 'Build position on current slide (real; PowerPoint, — if N/A)' },
    { variableId: 'build_count', name: 'Total builds on current slide (real; PowerPoint, — if N/A)' },
    { variableId: 'build_readout', name: 'Build position e.g. 2/5 (— when not reported / no builds)' },
    { variableId: 'slide_notes', name: 'Presenter notes for the current slide (primary)' },
    { variableId: 'slide_name', name: 'Current slide name/title (primary)' },
    { variableId: 'presentation_name', name: 'Active presentation/document name (primary)' },
    { variableId: 'cue_dwell_remaining', name: 'Seconds left on current cue slide' },
    // v6.0.8 video clock — TRT / remaining / elapsed for the current slide's media.
    { variableId: 'media_trt', name: 'Video TRT — total running time, M:SS (— if none)' },
    { variableId: 'media_remaining', name: 'Video time remaining, M:SS (— if none)' },
    { variableId: 'media_elapsed', name: 'Video elapsed, M:SS (— if none)' },
    { variableId: 'media_remaining_secs', name: 'Video time remaining, whole seconds (for feedbacks)' },
    { variableId: 'media_playing', name: 'Video clock running (true/false)' },
    { variableId: 'media_has', name: 'Current slide has a timed video (true/false)' },
    { variableId: 'media_armed', name: 'Video cued, waiting for its play trigger (true/false)' },
    { variableId: 'media_time_source', name: 'Where the time came from: measured / planned (cue sheet) / not available' },
    { variableId: 'media_source_short', name: 'Video time source, short for a button: measured / planned / n/a' },
    { variableId: 'license_state', name: 'License state (trial/licensed/expired)' },
    { variableId: 'trial_days_left', name: 'Trial days remaining' },
    { variableId: 'licensed_to', name: 'Licensed customer name' },
    { variableId: 'license_provider', name: 'Machine providing the license (Companion or an agent IP)' },
    // v6 Leader/Follower — who is driving the fleet right now (machine number AND IP).
    { variableId: 'leader_present', name: 'A Leader is driving the fleet (true/false)' },
    { variableId: 'leader_machine', name: 'Current Leader — machine number (e.g. M2, — = none)' },
    { variableId: 'leader_ip', name: 'Current Leader — IP/host (— = none)' },
    { variableId: 'leader_label', name: 'Current Leader — machine + IP (e.g. M2 · 192.168.1.51)' },
    { variableId: 'leader_name', name: 'Current Leader — friendly name (— = none)' },
    { variableId: 'fleet_versions', name: 'Agent versions across the fleet (compact)' },
    { variableId: 'version_status', name: 'Fleet version status (OK / MIXED / UPDATE)' },
    { variableId: 'update_version', name: 'Newest KueSync version available (blank = up to date)' },
    { variableId: 'late_ms', name: 'Worst command lateness across the fleet, ms (0 = on time)' },
    { variableId: 'clock_source', name: 'Fleet timebase: ptp (grandmaster-locked) / ptp-holdover / peer' },
    // 0.10.2 KueSync Timer (fleet show timer). Values are updated once a second from the leader's authoritative timer.
    { variableId: 'kuetimer_time', name: 'KueSync Timer display (M:SS / +overtime / time of day)' },
    { variableId: 'kuetimer_state', name: 'KueSync Timer state (idle/set/running/warning/overtime/countup/tod)' },
    { variableId: 'kuetimer_mode', name: 'KueSync Timer mode (down/up/tod)' },
    { variableId: 'kuetimer_running', name: 'KueSync Timer running (true/false)' },
    { variableId: 'kuetimer_remaining_s', name: 'KueSync Timer remaining seconds (negative = overtime; elapsed in count-up)' },
  ]
  // Per-machine slide readouts (so a confidence button can show which one drifted): slide_m1..m8.
  for (let i = 1; i <= 8; i++) defs.push({ variableId: `slide_m${i}`, name: `Slide on machine ${i} (✕ = unhealthy, — = none)` })
  // Per-machine friendly name + agent version.
  for (let i = 1; i <= 8; i++) defs.push({ variableId: `name_m${i}`, name: `Friendly name of machine ${i}` })
  for (let i = 1; i <= 8; i++) defs.push({ variableId: `agent_version_m${i}`, name: `Agent version on machine ${i}` })
  // 0.9.6: per-machine follower Active state (for button text / triggers; the feedback handles colour).
  for (let i = 1; i <= 8; i++) defs.push({ variableId: `active_m${i}`, name: `Follower ${i} — Active / Inactive` })
  return defs
}

export function updateVariables(self) {
  const p = self.primary && self.primary()
  const s = p ? p.state : { app: '', playing: false, slide: 0, total: 0 }
  const connectedCount = self.connectedCount ? self.connectedCount() : 0
  const healthyCount = self.healthyCount ? self.healthyCount() : connectedCount
  const ent = self.fleetEntitlement ? self.fleetEntitlement() : self.license.evaluate()
  const fleet = self.fleetSync ? self.fleetSync() : { inSync: true, spread: 0 }

  // Per-machine slides + a compact fleet string ("5·5·4", ✕ = configured-but-unhealthy).
  const targets = self.targets || []
  const vals = {}
  const parts = []
  const verSet = new Set()
  for (let i = 0; i < 8; i++) {
    const t = targets[i]
    let cell = '—'
    if (t) cell = self.healthy && self.healthy(t) ? String(t.state.slide || 0) : '✕'
    vals[`slide_m${i + 1}`] = cell
    vals[`name_m${i + 1}`] = t ? (t.state.label || '') : ''
    vals[`agent_version_m${i + 1}`] = t ? (t.state.agentVersion || '') : ''
    vals[`active_m${i + 1}`] = t ? (t.state.followerActive !== false ? 'Active' : 'Inactive') : '—'
    if (t && t.state.agentVersion) verSet.add(t.state.agentVersion)
    if (t) parts.push(cell)
  }
  // Fleet version awareness: MIXED if connected agents disagree, UPDATE if a newer build is published.
  const updVer = targets.map((t) => t && t.state.updateVersion).find((v) => v) || ''
  const versionStatus = updVer ? 'UPDATE' : (verSet.size > 1 ? 'MIXED' : 'OK')

  // v6 Leader/Follower — resolve the active Leader (machine number + IP) for the companion button.
  const ld = self.currentLeader ? self.currentLeader() : { present: false, machine: null, host: null }
  const ldMachine = ld.present && ld.machine ? `M${ld.machine}` : (ld.present ? 'M?' : '—')
  const ldIp = ld.present && ld.host ? ld.host : '—'
  const ldLabel = ld.present ? `${ldMachine} · ${ldIp}`.trim() : '— none —'

  // Show-running awareness: when the primary is NOT in slideshow, the readouts should say so rather
  // than showing a stale slide number or a sync verdict.
  const playing = !!s.playing
  const shownCur = (s.slideShown != null ? s.slideShown : s.slide) || 0
  const shownTot = (s.totalShown != null ? s.totalShown : s.total) || 0
  const bi = s.buildIndex != null ? s.buildIndex : -1
  const bc = s.buildCount != null ? s.buildCount : -1
  const buildReadout = (playing && bi >= 0 && bc >= 0) ? `${bi}/${bc}` : '—'
  const pmeta = (p && p.meta) || { presentationName: '', slideName: '', notes: '' }

  self.setVariableValues({
    ...vals,
    show_running: String(playing),
    slide_top: playing ? 'Slide' : 'No show',
    slide_bot: playing ? `${shownCur}/${shownTot}` : 'stopped',
    fleet_phase: !playing ? 'paused' : (fleet.inSync ? 'in sync' : 'DRIFT'),
    build_index: bi >= 0 ? bi : '—',
    build_count: bc >= 0 ? bc : '—',
    build_readout: buildReadout,
    slide_notes: pmeta.notes || '',
    slide_name: pmeta.slideName || '',
    presentation_name: pmeta.presentationName || '',
    leader_present: String(!!ld.present),
    leader_machine: ldMachine,
    leader_ip: ldIp,
    leader_label: ldLabel,
    leader_name: ld.present ? (ld.name || '—') : '—',
    fleet_versions: [...verSet].join(' · ') || '—',
    version_status: versionStatus,
    update_version: updVer,
    late_ms: Math.max(0, ...targets.map((t) => (t && t.state && t.state.firedLateMs) || 0)),
    clock_source: (targets.find((t) => t && t.state && t.state.clockSource) || {}).state?.clockSource || 'peer',
    connected: String(connectedCount > 0),
    connected_count: connectedCount,
    healthy_count: healthyCount,
    target_count: targets.length,
    app: s.app || '',
    current_slide: s.slide || 0,
    total_slides: s.total || 0,
    current_slide_shown: (s.slideShown != null ? s.slideShown : s.slide) || 0,
    total_slides_shown: (s.totalShown != null ? s.totalShown : s.total) || 0,
    playing: String(s.playing),
    fleet_in_sync: fleet.inSync ? 'in sync' : 'DRIFT',
    fleet_spread: fleet.spread || 0,
    fleet_slides: parts.join('·') || '—',
    cue_running: String(self.cue.running),
    cue_signal: self.cueState ? self.cueState() : 'idle',
    cue_go: String((self.cueState ? self.cueState() : 'idle') === 'go'),
    cue_back: String((self.cueState ? self.cueState() : 'idle') === 'back'),
    cue_dwell_remaining: self.cue.dwellRemaining || 0,
    license_state: ent.state,
    trial_days_left: ent.trialDaysLeft || 0,
    licensed_to: ent.licensedTo || '',
    license_provider: ent.provider || '',
  })
}
