<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Plane Tracker — Config</title>
<style>
  @import url('https://fonts.googleapis.com/css2?family=VT323&family=Share+Tech+Mono&display=swap');
  :root {
    --bg: #00205b; --panel: #005587; --panel-strong: #005587;
    --amber: #b7c9d3; --amber-dim: #6399ae;
    --amber-glow: rgba(183,201,211,0.12); --amber-bright: #ffffff;
    --text: #ffffff; --muted: #6399ae; --green: #0085ad; --red: #b7c9d3;
    --accent: #000000; --row-border: rgba(99,153,174,0.30);
    --board: rgba(183,201,211,0.08);
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body {
    background: var(--bg); color: var(--text);
    font-family: 'Share Tech Mono', monospace; min-height: 100vh; padding-bottom: 40px;
  }
  body::before {
    content: ''; position: fixed; inset: 0;
    background: repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255,255,255,0.012) 2px, rgba(255,255,255,0.012) 4px);
    pointer-events: none; z-index: 1000;
  }
  .board-header {
    background: linear-gradient(90deg, #b7c9d3, #0085ad); color: #00205b;
    padding: 10px 20px; display: flex; align-items: center; gap: 12px; margin-bottom: 0;
  }
  .board-title { font-family: 'VT323', monospace; font-size: 28px; letter-spacing: 4px; text-transform: uppercase; }
  .board-sub { font-size: 11px; letter-spacing: 3px; text-transform: uppercase; opacity: 0.82; }
  .nav-links { margin-left: auto; display: flex; gap: 12px; }
  .nav-links a {
    font-family: 'VT323', monospace; font-size: 20px; letter-spacing: 2px;
    background: #00205b; color: #ffffff; padding: 2px 12px; text-decoration: none;
  }
  .nav-links a:hover { background: #ffffff; color: #00205b; }

  .ticker {
    background: rgba(183,201,211,0.12); border-bottom: 1px solid var(--row-border);
    padding: 6px 20px; font-size: 12px; letter-spacing: 2px; color: #b7c9d3;
  }

  .content { padding: 20px 16px; }

  .section {
    border: 1px solid var(--row-border); margin-bottom: 16px;
    background: var(--panel);
  }
  .section-header {
    font-family: 'VT323', monospace; font-size: 20px; letter-spacing: 3px;
    text-transform: uppercase; color: var(--amber-bright);
    padding: 10px 16px; border-bottom: 1px solid var(--row-border);
    background: var(--panel-strong);
    display: flex; align-items: center; gap: 8px; cursor: pointer;
    user-select: none;
  }
  .section-header:hover { background: rgba(183,201,211,0.14); }
  .section-body { padding: 16px; display: grid; grid-template-columns: 1fr 1fr; gap: 12px 24px; }
  .section-body.single { grid-template-columns: 1fr; }
  @media (max-width: 600px) {
    .section-body { grid-template-columns: 1fr; }
    #api-sources-list { flex-direction: column !important; }
  }

  .field { display: flex; flex-direction: column; gap: 4px; }
  .field label {
    font-size: 11px; letter-spacing: 2px; text-transform: uppercase;
    color: var(--muted);
  }
  .field input, .field select {
    background: #000000; border: 1px solid #6399ae; color: var(--text);
    font-family: 'Share Tech Mono', monospace; font-size: 14px;
    padding: 7px 10px; outline: none; width: 100%;
    transition: border-color 0.2s;
  }
  .field input:focus, .field select:focus { border-color: var(--amber); box-shadow: 0 0 0 2px rgba(183,201,211,0.20); }
  .field input[type="checkbox"] {
    width: auto; height: 16px; cursor: pointer; accent-color: var(--amber);
  }
  .field.checkbox { flex-direction: row; align-items: center; gap: 10px; }
  .field.checkbox label { font-size: 13px; letter-spacing: 1px; margin: 0; color: var(--text); }
  .field .hint { font-size: 11px; color: var(--muted); margin-top: 2px; }

  .keys-list { display: flex; flex-direction: column; gap: 6px; }
  .key-row { display: flex; gap: 8px; align-items: center; }
  .key-row input { flex: 1; }
  .key-btn {
    background: transparent; border: 1px solid #6399ae; color: var(--muted);
    font-family: 'Share Tech Mono', monospace; font-size: 18px;
    padding: 4px 10px; cursor: pointer; line-height: 1;
  }
  .key-btn:hover { border-color: var(--amber-bright); color: var(--amber-bright); }

  .actions {
    display: flex; gap: 12px; align-items: center;
    padding: 16px; border-top: 1px solid var(--row-border);
    position: sticky; bottom: 0; background: rgba(0,32,91,0.96); z-index: 10;
  }
  .btn {
    font-family: 'VT323', monospace; font-size: 20px; letter-spacing: 2px;
    padding: 6px 20px; cursor: pointer; border: none; text-transform: uppercase;
    transition: opacity 0.2s;
  }
  .btn:disabled { opacity: 0.4; cursor: not-allowed; }
  .btn-save { background: var(--amber); color: #00205b; }
  .btn-save:hover:not(:disabled) { background: var(--amber-bright); }
  .btn-reset { background: transparent; border: 1px solid #6399ae; color: var(--muted); }
  .btn-reset:hover { border-color: var(--amber-bright); color: var(--amber-bright); }

  #status {
    font-size: 13px; letter-spacing: 1px;
    transition: opacity 0.5s;
  }
  #status.ok { color: var(--green); }
  #status.err { color: var(--red); }

  .zone-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .zone-label {
    font-size: 10px; letter-spacing: 1px; text-transform: uppercase;
    color: var(--muted); margin-bottom: 4px;
  }

  .loading { text-align: center; padding: 40px; color: var(--muted);
    font-family: 'VT323', monospace; font-size: 24px; animation: pulse 1.5s infinite; }
  @keyframes pulse { 0%,100%{opacity:0.3} 50%{opacity:1} }
</style>
</head>
<body>

<div class="board-header">
  <span style="font-size:22px">✈</span>
  <div>
    <div class="board-title">Configuration</div>
    <div class="board-sub">Plane Tracker Settings</div>
  </div>
  <div class="nav-links">
    <a href="/">← Board</a>
    <a href="/bounding-box">Zone Map</a>
    <a href="/display-fields">Fields</a>
    <a href="/stats">Stats</a>
  </div>
</div>

<div class="ticker" id="ticker">Loading configuration...</div>

<div class="content" id="main-content">
  <div class="loading">Loading config...</div>
</div>

<div class="content" style="padding-top:0;padding-bottom:80px">
  <div class="section">
    <div class="section-header" onclick="toggleSection(this)">v Wi-Fi Connection</div>
    <div class="section-body single" style="padding:16px">
      <div id="wifi-setup-box" style="display:none;margin-bottom:12px;border:1px solid var(--airbus-silver);padding:12px;background:rgba(183,201,211,0.12);color:var(--text)">
        <div style="font-size:15px;color:var(--amber-bright);letter-spacing:2px">SETUP ACCESS POINT ACTIVE</div>
        <div class="hint" id="wifi-setup-text" style="margin-top:6px"></div>
      </div>
      <div id="wifi-status" style="color:var(--muted);font-size:12px;letter-spacing:2px">Loading Wi-Fi status...</div>
      <div class="hint" style="margin-top:6px">Use Deep Scan if normal scanning only shows the currently connected network.</div>
      <div id="wifi-errors" class="hint" style="display:none;margin-top:6px"></div>
      <div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:12px">
        <button class="btn btn-reset" type="button" onclick="scanWifi()">Scan Networks</button>
        <button class="btn btn-reset" type="button" onclick="deepScanWifi()">Deep Scan</button>
        <button class="btn btn-reset" type="button" onclick="loadWifiStatus()">Refresh Status</button>
      </div>
      <div id="wifi-list" style="display:flex;flex-direction:column;gap:6px;margin-top:12px"></div>
      <div id="wifi-connect-panel" style="display:none;margin-top:12px;border:1px solid var(--row-border);padding:12px;background:rgba(183,201,211,0.08)">
        <div class="field">
          <label>Selected Network</label>
          <input id="wifi_ssid" readonly>
        </div>
        <div class="field" style="margin-top:8px">
          <label>Password</label>
          <input id="wifi_password" type="password" autocomplete="current-password" placeholder="Leave blank for open networks">
        </div>
        <div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:10px">
          <button class="btn btn-save" type="button" onclick="connectWifi()">Connect</button>
          <button class="btn btn-reset" type="button" onclick="hideWifiConnect()">Cancel</button>
        </div>
        <div class="field">
          <div class="hint">If this Pi switches networks successfully, this page may stop responding until you open the Pi at its new address.</div>
        </div>
      </div>
    </div>
  </div>

  <div class="section">
    <div class="section-header" onclick="toggleSection(this)">v System Status &amp; API Usage</div>
    <div class="section-body single" style="padding:16px">
      <div id="system-status" style="color:var(--muted);font-size:12px;letter-spacing:2px">Loading...</div>
    </div>
  </div>

  <div class="section">
    <div class="section-header" onclick="toggleSection(this)">v Update Status</div>
    <div class="section-body single" style="padding:16px">
      <div id="update-status" style="color:var(--muted);font-size:12px;letter-spacing:1px">Loading update status...</div>
      <div style="height:5px;background:#000;margin-top:12px">
        <div id="update-progress" style="height:5px;width:0;background:#0085ad"></div>
      </div>
      <div style="display:flex;gap:8px;flex-wrap:wrap;margin-top:12px">
        <button class="btn btn-reset" type="button" onclick="checkForUpdate()">Check Now</button>
        <button class="btn btn-save" id="install-update" type="button" onclick="installUpdate()" disabled>Install Update</button>
        <button class="btn btn-reset" id="rollback-update" type="button" onclick="rollbackUpdate()" disabled>Roll Back</button>
      </div>
    </div>
  </div>
</div>

<div class="actions">
  <button class="btn btn-save" id="btn-save" onclick="saveConfig()" disabled>Save &amp; Apply</button>
  <button class="btn btn-reset" onclick="loadConfig()">Reload</button>
  <button class="btn btn-reset" onclick="restartApp()" style="border-color:#b7c9d3;color:#b7c9d3">Restart App</button>
  <span id="status"></span>
</div>

<script>
let _cfg = {};
let _sec = {};

const _ALL_SOURCES = [
  {id:"FlightStats", label:"FlightStats",  hint:"Free scraper — no key needed, best for scheduled airlines"},
  {id:"AirLabs",     label:"AirLabs",      hint:"Free tier 1,000 calls/month — requires API key"},
  {id:"FlightAware", label:"FlightAware",  hint:"Free $5 credit ~1,000 calls/month — requires API key"},
  {id:"FR24Unofficial", label:"FR24 Unofficial", hint:"Unofficial FR24 web SDK — requires: pip install FlightRadarAPI"},
  {id:"FR24",        label:"FR24",         hint:"Paid — 1 credit per flight, most reliable fallback"},
];

function renderApiSources(order, enabled) {
  const container = document.getElementById('api-sources-list');
  // Show in saved order, then any not in order
  const ordered = [...order];
  _ALL_SOURCES.forEach(s => { if (!ordered.includes(s.id)) ordered.push(s.id); });

  container.innerHTML = ordered.map((sid, i) => {
    const src = _ALL_SOURCES.find(s => s.id === sid) || {id:sid, label:sid, hint:''};
    const isEnabled = enabled[sid] !== false;
    return `
      <div class="key-row" id="src-row-${sid}" data-sid="${sid}" style="align-items:center;gap:10px;padding:6px 8px;border:1px solid var(--row-border);background:rgba(183,201,211,0.08)">
        <span class="src-num" style="color:var(--amber-bright);font-family:'VT323',monospace;font-size:20px;min-width:16px;text-align:center">${i+1}</span>
        <input type="checkbox" id="src-en-${sid}" ${isEnabled?'checked':''} style="width:auto;accent-color:var(--amber)">
        <div style="flex:1">
          <div style="font-size:13px;letter-spacing:1px">${src.label}</div>
          <div style="font-size:10px;color:var(--muted);letter-spacing:1px">${src.hint}</div>
        </div>
        <div style="display:flex;gap:4px">
          <button class="key-btn" onclick="moveSource('${sid}',-1)" title="Up">↑</button>
          <button class="key-btn" onclick="moveSource('${sid}',1)" title="Down">↓</button>
        </div>
      </div>`;
  }).join('');
}

function moveSource(sid, dir) {
  const rows = [...document.querySelectorAll('[id^="src-row-"]')];
  const idx  = rows.findIndex(r => r.dataset.sid === sid);
  const newIdx = idx + dir;
  if (newIdx < 0 || newIdx >= rows.length) return;
  const container = document.getElementById('api-sources-list');
  if (dir === -1) container.insertBefore(rows[idx], rows[newIdx]);
  else            container.insertBefore(rows[newIdx], rows[idx]);
  // Re-number all
  [...document.querySelectorAll('[id^="src-row-"]')].forEach((r, i) => {
    const num = r.querySelector('.src-num');
    if (num) num.textContent = i + 1;
  });
}

function getApiSources() {
  const order = [];
  const enabled = {};
  document.querySelectorAll('[id^="src-row-"]').forEach(row => {
    const sid = row.dataset.sid;
    order.push(sid);
    enabled[sid] = document.getElementById(`src-en-${sid}`)?.checked ?? true;
  });
  return { order, enabled };
}

async function loadConfig() {
  setStatus('', '');
  document.getElementById('btn-save').disabled = true;
  document.getElementById('ticker').textContent = 'Loading configuration...';
  try {
    const r = await fetch(`/api/config?_=${Date.now()}`, {cache: 'no-store'});
    const d = await r.json();
    _cfg = d.config || {};
    _sec = d.secrets || {};
    renderForm();
    document.getElementById('ticker').textContent =
      `Config loaded — ${new Date().toLocaleTimeString()}  |  Edit any field and click Save & Apply`;
    document.getElementById('btn-save').disabled = false;
  } catch(e) {
    document.getElementById('ticker').textContent = 'Failed to load config: ' + e.message;
  }
}

function renderForm() {
  const loc  = _cfg.location  || {};
  const disp = _cfg.display   || {};
  const flt  = _cfg.flights   || {};
  const ms   = _cfg.master_slave || {};
  const quiet = _cfg.quiet_time || {};
  const updates = _cfg.updates || {};

  document.getElementById('main-content').innerHTML = `

    <!-- Location & Zone -->
    <div class="section">
      <div class="section-header" onclick="toggleSection(this)">v Location &amp; Zone</div>
      <div class="section-body">
        <div class="field">
          <label>Home Airport Code</label>
          <input id="journey_code" value="${loc.journey_code || 'ORD'}" maxlength="4" style="text-transform:uppercase">
          <div class="hint">Your nearest major airport ICAO code</div>
        </div>
        <div class="field">
          <label>Clock Format</label>
          <select id="clock_format">
            <option value="12hr" ${(loc.clock_format||'12hr')==='12hr'?'selected':''}>12hr</option>
            <option value="24hr" ${(loc.clock_format||'')==='24hr'?'selected':''}>24hr</option>
          </select>
        </div>
        <div class="field">
          <label>Temperature Units</label>
          <select id="temperature_units">
            <option value="imperial" ${(loc.temperature_units||'imperial')==='imperial'?'selected':''}>Imperial (°F)</option>
            <option value="metric" ${(loc.temperature_units||'')==='metric'?'selected':''}>Metric (°C)</option>
          </select>
        </div>
        <div class="field">
          <label>Distance Units</label>
          <select id="distance_units">
            <option value="imperial" ${(loc.distance_units||'imperial')==='imperial'?'selected':''}>Imperial (mi)</option>
            <option value="metric" ${(loc.distance_units||'')==='metric'?'selected':''}>Metric (km)</option>
            <option value="nautical" ${(loc.distance_units||'')==='nautical'?'selected':''}>Nautical (nm)</option>
          </select>
        </div>
        <div class="field">
          <label>Speed Units</label>
          <select id="speed_units">
            <option value="imperial" ${(loc.speed_units||'imperial')==='imperial'?'selected':''}>Imperial (mph)</option>
            <option value="metric" ${(loc.speed_units||'')==='metric'?'selected':''}>Metric (km/h)</option>
            <option value="knots" ${(loc.speed_units||'')==='knots'?'selected':''}>Knots (kts)</option>
          </select>
        </div>
        <div></div>
        <div class="field">
          <label>Home Lat, Lon</label>
          <input id="location_home" value="${(loc.location_home||[]).join(', ')}">
          <div class="hint">e.g. 41.882734,-87.623299</div>
        </div>
        <div class="field">
          <label>Temperature Location (Lat, Lon)</label>
          <input id="temperature_location" value="${loc.temperature_location || ''}">
          <div class="hint">Same as home lat,lon usually</div>
        </div>
        <div class="field">
          <label>Top-Left Zone (Lat, Lon)</label>
          <input id="zone_tl" value="${(loc.zone_home||{}).tl_y||''}, ${(loc.zone_home||{}).tl_x||''}" placeholder="41.921086, -87.694973">
        </div>
        <div class="field">
          <label>Bottom-Right Zone (Lat, Lon)</label>
          <input id="zone_br" value="${(loc.zone_home||{}).br_y||''}, ${(loc.zone_home||{}).br_x||''}" placeholder="41.849068, -87.548980">
        </div>
      </div>
    </div>

    <!-- Display -->
    <div class="section">
      <div class="section-header" onclick="toggleSection(this)">v Display</div>
      <div class="section-body">
        <div class="field">
          <label>RGB Panel Count</label>
          <select id="panel_count">
            <option value="2" ${(disp.panel_count||2)===2?'selected':''}>2 Panels (128x32)</option>
            <option value="1" ${(disp.panel_count||2)===1?'selected':''}>1 Panel (64x32)</option>
          </select>
          <div class="hint">Two panels keeps your current layout. One panel uses the compact layout from the older code.</div>
        </div>
        <div></div>
        <div class="field">
          <label>Brightness (day) %</label>
          <input id="brightness" type="number" min="0" max="100" value="${disp.brightness??100}">
        </div>
        <div class="field">
          <label>Brightness (night) %</label>
          <input id="brightness_night" type="number" min="0" max="100" value="${disp.brightness_night??50}">
        </div>
        <div class="field checkbox">
          <input id="night_brightness" type="checkbox" ${disp.night_brightness?'checked':''}>
          <label for="night_brightness">Enable Night Dimming</label>
        </div>
        <div class="field">
          <label>Night Start (HH:MM)</label>
          <input id="night_start" value="${disp.night_start||'22:00'}">
        </div>
        <div class="field">
          <label>Night End (HH:MM)</label>
          <input id="night_end" value="${disp.night_end||'06:00'}">
        </div>
        <div class="field">
          <label>GPIO Slowdown</label>
          <select id="gpio_slowdown">
            <option value="1" ${(disp.gpio_slowdown||2)===1?'selected':''}>1 — Pi Zero / Pi 4</option>
            <option value="2" ${(disp.gpio_slowdown||2)===2?'selected':''}>2 — Pi 3</option>
            <option value="3" ${(disp.gpio_slowdown||2)===3?'selected':''}>3 — Pi 5</option>
          </select>
        </div>
        <div class="field checkbox">
          <input id="hat_pwm_enabled" type="checkbox" ${disp.hat_pwm_enabled?'checked':''}>
          <label for="hat_pwm_enabled">HAT PWM Enabled (soldered bridge)</label>
        </div>
        <div class="field">
          <label>Forecast Days</label>
          <input id="forecast_days" type="number" min="1" max="5" value="${disp.forecast_days??3}">
        </div>
      </div>
    </div>

    <!-- Software Updates -->
    <div class="section">
      <div class="section-header" onclick="toggleSection(this)">v Software Updates</div>
      <div class="section-body">
        <div class="field checkbox">
          <input id="updates_enabled" type="checkbox" ${updates.enabled?'checked':''}>
          <label for="updates_enabled">Enable Remote Updates</label>
        </div>
        <div class="field checkbox">
          <input id="updates_check_on_start" type="checkbox" ${updates.check_on_start!==false?'checked':''}>
          <label for="updates_check_on_start">Check When Device Starts</label>
        </div>
        <div class="field checkbox">
          <input id="updates_auto_install" type="checkbox" ${updates.auto_install!==false?'checked':''}>
          <label for="updates_auto_install">Automatically Install New Versions</label>
        </div>
        <div class="field">
          <label>Update Manifest URL</label>
          <input id="updates_manifest_url" type="url" value="${updates.manifest_url||''}" placeholder="https://example.com/plane-tracker/manifest.json">
          <div class="hint">HTTPS address of the remote manifest.json file.</div>
        </div>
        <div class="field">
          <label>Network Timeout (seconds)</label>
          <input id="updates_timeout_seconds" type="number" min="5" max="120" value="${updates.timeout_seconds??20}">
        </div>
        <div class="field">
          <label>Rollback Backups To Keep</label>
          <input id="updates_keep_backups" type="number" min="1" max="10" value="${updates.keep_backups??2}">
        </div>
      </div>
    </div>

    <!-- Quiet Time -->
    <div class="section">
      <div class="section-header" onclick="toggleSection(this)">v Quiet Time</div>
      <div class="section-body">
        <div class="field checkbox">
          <input id="quiet_enabled" type="checkbox" ${quiet.enabled?'checked':''}>
          <label for="quiet_enabled">Enable Quiet Time</label>
        </div>
        <div class="field">
          <label>Quiet Start (HH:MM)</label>
          <input id="quiet_start" value="${quiet.start||'23:00'}">
          <div class="hint">During quiet time the display shows only time and weather.</div>
        </div>
        <div class="field">
          <label>Quiet End (HH:MM)</label>
          <input id="quiet_end" value="${quiet.end||'06:00'}">
        </div>
        <div class="field">
          <label>Weather Refresh During Quiet Time (minutes)</label>
          <input id="quiet_weather_interval_minutes" type="number" min="5" max="240" value="${Math.round((quiet.weather_interval_seconds||3600)/60)}">
          <div class="hint">Flight APIs are not called during quiet time.</div>
        </div>
      </div>
    </div>

    <!-- Flights -->
    <div class="section">
      <div class="section-header" onclick="toggleSection(this)">v Flights</div>
      <div class="section-body">
        <div class="field">
          <label>Min Altitude (ft)</label>
          <input id="min_altitude" type="number" value="${flt.min_altitude??2600}">
          <div class="hint">Filter out low GA traffic</div>
        </div>
        <div class="field">
          <label>Max Altitude (ft)</label>
          <input id="max_altitude" type="number" value="${flt.max_altitude??100000}">
          <div class="hint">Only show flights at or below this altitude</div>
        </div>
        <div class="field">
          <label>Live Flights To Rotate</label>
          <input id="max_display_flights" type="number" min="1" max="5" value="${flt.max_display_flights??5}">
          <div class="hint">Show the nearest 1 to 5 flights inside the bounding box</div>
        </div>
        <div class="field">
          <label>Max Farthest Flights</label>
          <input id="max_farthest" type="number" min="1" max="10" value="${flt.max_farthest??3}">
        </div>
        <div class="field">
          <label>Max Closest Flights</label>
          <input id="max_closest" type="number" min="1" max="10" value="${flt.max_closest??3}">
        </div>
        <div class="field">
          <label>Alert Email</label>
          <input id="email" type="email" value="${flt.email||''}">
          <div class="hint">Leave blank to disable email alerts</div>
        </div>
      </div>
    </div>

    <!-- Stats -->
    <div class="section">
      <div class="section-header" onclick="toggleSection(this)">v Stats Logging</div>
      <div class="section-body">
        <div class="field">
          <label>Flight Log Retention (days)</label>
          <input id="stats_log_days" type="number" min="0" value="${(_cfg.stats||{}).stats_log_days||''}">
          <div class="hint">Days of flight history to keep. Leave blank to keep forever.</div>
        </div>
      </div>
    </div>

    <!-- Master / Slave -->
    <div class="section">
      <div class="section-header" onclick="toggleSection(this)">v Master / Slave</div>
      <div class="section-body">
        <div class="field">
          <label>Master Tracker Hostname</label>
          <input id="master_tracker" value="${ms.master_tracker||''}">
          <div class="hint">Leave blank if THIS is the master</div>
        </div>
        <div class="field">
          <label>Other Tracker Hostnames</label>
          <input id="other_tracker_hostnames" value="${(ms.other_tracker_hostnames||[]).join(', ')}">
          <div class="hint">Comma-separated slave hostnames</div>
        </div>
      </div>
    </div>

    <!-- API Sources & Keys — side by side -->
    <div class="section">
      <div class="section-header" onclick="toggleSection(this)">v API Sources &amp; Order</div>
      <div class="section-body">
        <div>
          <div class="hint" style="margin-bottom:8px">Enable sources and set priority order. FlightStats is free (no key). OpenSky provides position data only.</div>
          <div id="api-sources-list" style="display:flex;flex-direction:column;gap:6px"></div>
          <div class="field" style="margin-top:14px">
            <label>Bounding Box Polling Time</label>
            <select id="position_poll_seconds">
              ${[30,45,60,85,120].map(seconds =>
                `<option value="${seconds}" ${Number((_cfg.position_sources||{}).poll_seconds||120)===seconds?'selected':''}>${seconds} seconds</option>`
              ).join('')}
            </select>
            <div class="hint">How often normal flights inside the bounding box are refreshed. Restart the tracker after saving.</div>
          </div>
        </div>
        <div style="display:flex;flex-direction:column;gap:12px">
          <div class="field">
            <label>Tomorrow.io Weather Key</label>
            <input id="tomorrow_api_key" value="${_sec.tomorrow_api_key||''}">
          </div>
          <div class="field">
            <label>FR24 API Key (Official/Paid)</label>
            <input id="flightradar24_key" value="${_sec.flightradar24_key||''}">
          </div>
          <div class="field">
            <label>FR24 Unofficial Key (optional)</label>
            <input id="fr24_unofficial_key" value="${_sec.fr24_unofficial_key||''}">
            <div class="hint">Format: subscription_key|jwt_token — leave blank for anonymous</div>
          </div>
          <div class="field">
            <label>OpenSky Client ID</label>
            <input id="opensky_client_id" value="${_sec.opensky_client_id||''}">
          </div>
          <div class="field">
            <label>OpenSky Client Secret</label>
            <input id="opensky_client_secret" value="${_sec.opensky_client_secret||''}">
          </div>
          <div class="field">
            <label>AirLabs API Keys</label>
            <div class="keys-list" id="airlabs-keys">
              ${renderKeysList('airlabs', _sec.airlabs_api_keys||[''])}
            </div>
          </div>
          <div class="field">
            <label>FlightAware API Keys</label>
            <div class="keys-list" id="flightaware-keys">
              ${renderKeysList('flightaware', _sec.flightaware_api_keys||[''])}
            </div>
          </div>
          <div class="field">
            <label>FlightAware Monthly $ Limit per Key</label>
            <input id="flightaware_monthly_limit" type="number" step="0.5" value="${_sec.flightaware_monthly_limit??4.00}">
          </div>
        </div>
      </div>
    </div>
  `;

  // Render API sources
  const apis = _cfg.api_sources || {};
  renderApiSources(apis.order || ["FlightStats","AirLabs","FlightAware","FR24Unofficial","FR24"], apis.enabled || {});
}

function renderKeysList(name, keys) {
  return keys.map((k, i) => `
    <div class="key-row" id="${name}-key-${i}">
      <input id="${name}_key_${i}" value="${k}" placeholder="Key ${i+1}">
      <button class="key-btn" onclick="removeKey('${name}', ${i})" title="Remove">−</button>
    </div>
  `).join('') + `<button class="key-btn" onclick="addKey('${name}')" style="margin-top:4px;width:100%">+ Add Key</button>`;
}

function addKey(name) {
  const container = document.getElementById(`${name}-keys`);
  const rows = container.querySelectorAll('.key-row');
  const i = rows.length;
  const div = document.createElement('div');
  div.className = 'key-row';
  div.id = `${name}-key-${i}`;
  div.innerHTML = `<input id="${name}_key_${i}" value="" placeholder="Key ${i+1}"><button class="key-btn" onclick="removeKey('${name}', ${i})" title="Remove">−</button>`;
  container.insertBefore(div, container.lastElementChild);
}

function removeKey(name, i) {
  const row = document.getElementById(`${name}-key-${i}`);
  if (row) row.remove();
}

function getKeys(name) {
  const keys = [];
  document.querySelectorAll(`[id^="${name}_key_"]`).forEach(el => {
    if (el.value.trim()) keys.push(el.value.trim());
  });
  return keys;
}

function toggleSection(header) {
  const body = header.nextElementSibling;
  const isHidden = body.style.display === 'none';
  body.style.display = isHidden ? '' : 'none';
  header.textContent = header.textContent.replace(isHidden ? '>' : 'v', isHidden ? 'v' : '>');
}

function g(id) { return document.getElementById(id); }
function gv(id) { return g(id)?.value ?? ''; }
function gc(id) { return g(id)?.checked ?? false; }
function gi(id) { return parseInt(gv(id)) || 0; }
function gf(id) { return parseFloat(gv(id)) || 0; }

function collectConfig() {
  const homeParts = gv('location_home').split(',').map(s => parseFloat(s.trim()));
  const tlParts   = gv('zone_tl').split(',').map(s => parseFloat(s.trim()));
  const brParts   = gv('zone_br').split(',').map(s => parseFloat(s.trim()));

  return {
    config: {
      location: {
        journey_code:         gv('journey_code').toUpperCase(),
        clock_format:         gv('clock_format'),
        temperature_units:    gv('temperature_units'),
        distance_units:       gv('distance_units'),
        speed_units:          gv('speed_units'),
        location_home:        homeParts,
        temperature_location: gv('temperature_location'),
        journey_blank_filler: ' ? ',
        zone_home: {
          tl_y: tlParts[0] || 0,
          tl_x: tlParts[1] || 0,
          br_y: brParts[0] || 0,
          br_x: brParts[1] || 0,
        },
      },
      display: {
        brightness:       gi('brightness'),
        brightness_night: gi('brightness_night'),
        night_brightness: gc('night_brightness'),
        panel_count:      gi('panel_count') === 1 ? 1 : 2,
        night_start:      gv('night_start'),
        night_end:        gv('night_end'),
        gpio_slowdown:    gi('gpio_slowdown'),
        hat_pwm_enabled:  gc('hat_pwm_enabled'),
        forecast_days:    gi('forecast_days'),
      },
      flights: {
        min_altitude: gi('min_altitude'),
        max_altitude: gi('max_altitude') || 100000,
        max_display_flights: Math.max(1, Math.min(5, gi('max_display_flights') || 5)),
        max_farthest: gi('max_farthest'),
        max_closest:  gi('max_closest'),
        email:        gv('email'),
      },
        stats: {
        stats_log_days: gi('stats_log_days') || 0,
      },
      master_slave: {
        master_tracker:          gv('master_tracker'),
        other_tracker_hostnames: gv('other_tracker_hostnames').split(',').map(s=>s.trim()).filter(Boolean),
      },
      quiet_time: {
        enabled: gc('quiet_enabled'),
        start: gv('quiet_start') || '23:00',
        end: gv('quiet_end') || '06:00',
        weather_interval_seconds: Math.max(5, gi('quiet_weather_interval_minutes') || 60) * 60,
      },
      updates: {
        enabled: gc('updates_enabled'),
        manifest_url: gv('updates_manifest_url').trim(),
        check_on_start: gc('updates_check_on_start'),
        auto_install: gc('updates_auto_install'),
        timeout_seconds: Math.max(5, Math.min(120, gi('updates_timeout_seconds') || 20)),
        keep_backups: Math.max(1, Math.min(10, gi('updates_keep_backups') || 2)),
      },
      route_cache: _cfg.route_cache || { enabled: true, days: 365 },
      display_fields: _cfg.display_fields,
      sports: _cfg.sports,
      api_sources: (() => {
        const a = getApiSources();
        return { order: a.order, enabled: a.enabled };
      })(),
      position_sources: {
        ...(_cfg.position_sources || {}),
        poll_seconds: [30,45,60,85,120].includes(gi('position_poll_seconds'))
          ? gi('position_poll_seconds')
          : 120,
      },
    },
    secrets: {
      tomorrow_api_key:          gv('tomorrow_api_key'),
      opensky_client_id:         gv('opensky_client_id'),
      opensky_client_secret:     gv('opensky_client_secret'),
      airlabs_api_keys:          getKeys('airlabs'),
      flightaware_api_keys:      getKeys('flightaware'),
      flightaware_monthly_limit: gf('flightaware_monthly_limit'),
      flightradar24_key:         gv('flightradar24_key'),
      fr24_unofficial_key:       gv('fr24_unofficial_key'),
    }
  };
}

async function saveConfig() {
  const btn = document.getElementById('btn-save');
  btn.disabled = true;
  btn.textContent = 'Saving...';
  setStatus('', '');

  try {
    const payload = collectConfig();
    const r = await fetch('/api/config', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify(payload),
    });
    const d = await r.json();
    if (d.ok) {
      setStatus(d.home_location_changed ? '✓ Config saved; closest/farthest records reset' : '✓ Config saved and applied', 'ok');
      document.getElementById('ticker').textContent =
        d.home_location_changed
          ? `Saved — ${new Date().toLocaleTimeString()}  |  Home moved, closest/farthest records were reset`
          : `Saved — ${new Date().toLocaleTimeString()}  |  Changes are live immediately`;
      // Reload to show updated masked values
      setTimeout(() => {
        loadConfig();
        loadUpdateStatus();
      }, 1000);
    } else {
      setStatus('✗ ' + (d.errors||[]).join(', '), 'err');
    }
  } catch(e) {
    setStatus('✗ ' + e.message, 'err');
  }

  btn.textContent = 'Save & Apply';
  btn.disabled = false;
}

async function restartApp() {
  if (!confirm('Restart the app? Display will go dark for a few seconds.')) return;
  setStatus('Restarting...', '');
  try {
    await fetch('/api/restart', {method: 'POST'});
    setStatus('✓ Restarting — reconnecting in 8s...', 'ok');
    setTimeout(() => window.location.reload(), 8000);
  } catch(e) {
    setStatus('✗ ' + e.message, 'err');
  }
}

function setStatus(msg, cls) {
  const el = document.getElementById('status');
  el.textContent = msg;
  el.className = cls;
}

loadConfig();
loadSystemStatus();
loadUpdateStatus();
loadWifiStatus();
loadWifiConnectResult();

async function loadUpdateStatus() {
  const el = document.getElementById('update-status');
  const bar = document.getElementById('update-progress');
  try {
    const response = await fetch(`/api/update/status?_=${Date.now()}`, {cache:'no-store'});
    const data = await response.json();
    if (!data.ok) throw new Error(data.error || 'Status request failed');
    const u = data.update;
    const latest = u.latest_version ? ` | Latest: ${u.latest_version}` : '';
    const checked = u.last_checked ? ` | Checked: ${u.last_checked}` : '';
    el.textContent = `Installed: ${u.installed_version}${latest} | ${u.last_status||'ready'}${checked}`;
    bar.style.width = `${u.progress_percent||0}%`;
    document.getElementById('install-update').disabled = !u.enabled;
    document.getElementById('rollback-update').disabled = !u.rollback_available;
  } catch(e) {
    el.textContent = `Could not load update status: ${e.message}`;
  }
}

async function checkForUpdate() {
  const el = document.getElementById('update-status');
  el.textContent = 'Checking for updates...';
  try {
    const response = await fetch('/api/update/check', {method:'POST'});
    const data = await response.json();
    if (!data.ok) throw new Error(data.error || 'Update check failed');
    el.textContent = data.update_available
      ? `Version ${data.latest_version} is available. Installed: ${data.installed_version}.`
      : `Already current at version ${data.installed_version}.`;
    loadUpdateStatus();
  } catch(e) {
    el.textContent = `Update check failed: ${e.message}`;
  }
}

async function installUpdate() {
  if (!confirm('Download and install the latest update? The tracker will restart when complete.')) return;
  const el = document.getElementById('update-status');
  el.textContent = 'Starting update...';
  try {
    const response = await fetch('/api/update/install', {method:'POST'});
    const data = await response.json();
    if (!data.ok) throw new Error(data.error || 'Could not start update');
    const poll = setInterval(loadUpdateStatus, 1500);
    setTimeout(() => {
      clearInterval(poll);
      window.location.reload();
    }, 30000);
  } catch(e) {
    el.textContent = `Update failed: ${e.message}`;
  }
}

async function rollbackUpdate() {
  if (!confirm('Restore the previous code version? The tracker will restart.')) return;
  const el = document.getElementById('update-status');
  el.textContent = 'Starting rollback...';
  try {
    const response = await fetch('/api/update/rollback', {method:'POST'});
    const data = await response.json();
    if (!data.ok) throw new Error(data.error || 'Could not start rollback');
    setTimeout(() => window.location.reload(), 12000);
  } catch(e) {
    el.textContent = `Rollback failed: ${e.message}`;
  }
}

async function loadWifiConnectResult() {
  try {
    const data = await fetch('/api/wifi/connect-result').then(r => r.json());
    if (data.state === 'failed') {
      const status = document.getElementById('wifi-status');
      status.textContent = `Last connection to ${data.ssid || 'Wi-Fi'} failed: ${data.message || 'Unknown error'}`;
      status.style.color = 'var(--airbus-silver)';
    }
  } catch (e) {
    // The result is optional and may be unavailable during network handoff.
  }
}

async function loadWifiStatus() {
  const status = document.getElementById('wifi-status');
  if (!status) return;
  status.textContent = 'Loading Wi-Fi status...';
  try {
    const data = await fetch('/api/wifi/status').then(r => r.json());
    if (!data.available) {
      status.textContent = data.ssid
        ? `Connected to ${data.ssid}. Web Wi-Fi switching needs NetworkManager/nmcli.`
        : (data.message || 'Web Wi-Fi switching needs NetworkManager/nmcli.');
      return;
    }
    const bits = [];
    bits.push(data.ssid ? `Connected: ${data.ssid}` : 'Not connected to Wi-Fi');
    if (data.device) bits.push(`Device: ${data.device}`);
    if (data.signal) bits.push(`Signal: ${data.signal}%`);
    status.textContent = bits.join('  |  ');
    const setupBox = document.getElementById('wifi-setup-box');
    const setupText = document.getElementById('wifi-setup-text');
    if (setupBox && setupText && data.setup && data.setup.active) {
      setupBox.style.display = '';
      setupText.innerHTML = `Connected to the temporary network ${escapeHtml(data.setup.setup_ssid || data.ssid)}. <a href="/wifi-setup" style="color:var(--amber-bright)">Open the simplified Wi-Fi Setup page</a>.`;
    } else if (setupBox) {
      setupBox.style.display = 'none';
    }
  } catch (e) {
    status.textContent = 'Could not load Wi-Fi status: ' + e.message;
  }
}

async function scanWifi() {
  await renderWifiScan('/api/wifi/scan', 'Scanning Wi-Fi networks...');
}

async function deepScanWifi() {
  if (!confirm('Deep scan briefly disconnects Wi-Fi, then tries to reconnect. Continue?')) return;
  await renderWifiScan('/api/wifi/deep-scan', 'Running deep Wi-Fi scan...');
}

async function renderWifiScan(url, loadingText) {
  const list = document.getElementById('wifi-list');
  const status = document.getElementById('wifi-status');
  const errorsBox = document.getElementById('wifi-errors');
  list.innerHTML = '';
  errorsBox.style.display = 'none';
  errorsBox.textContent = '';
  status.textContent = loadingText;
  try {
    const data = await fetch(url).then(r => r.json());
    if (!data.available) {
      status.textContent = data.message || 'NetworkManager/nmcli is not available.';
      return;
    }
    status.textContent = data.message || `Found ${data.networks.length} Wi-Fi networks${data.device ? ' on ' + data.device : ''}`;
    if (data.errors && data.errors.length) {
      errorsBox.style.display = '';
      errorsBox.textContent = 'Scan diagnostics: ' + data.errors.join(' | ');
    }
    if (!data.networks.length) {
      list.innerHTML = '<div class="hint">No networks found.</div>';
      return;
    }
    list.innerHTML = data.networks.map(net => `
      <div class="key-row" style="align-items:center;gap:10px;padding:8px;border:1px solid var(--row-border);background:rgba(183,201,211,0.08)">
        <div style="flex:1">
          <div style="font-size:13px;letter-spacing:1px;color:var(--amber-bright)">${escapeHtml(net.ssid)}${net.active ? ' (connected)' : ''}</div>
          <div style="font-size:10px;color:var(--muted);letter-spacing:1px">Signal ${net.signal}%  |  ${escapeHtml(net.security || 'Open')}  |  ${escapeHtml(net.source || 'scan')}</div>
        </div>
        <button class="key-btn" type="button" data-ssid="${escapeHtml(net.ssid)}" onclick="showWifiConnectFromButton(this)">Select</button>
      </div>
    `).join('');
  } catch (e) {
    status.textContent = 'Wi-Fi scan failed: ' + e.message;
  }
}

function showWifiConnect(ssid) {
  document.getElementById('wifi_ssid').value = ssid;
  document.getElementById('wifi_password').value = '';
  document.getElementById('wifi-connect-panel').style.display = '';
}

function showWifiConnectFromButton(button) {
  showWifiConnect(button.dataset.ssid || '');
}

function hideWifiConnect() {
  document.getElementById('wifi-connect-panel').style.display = 'none';
}

async function connectWifi() {
  const ssid = gv('wifi_ssid');
  const password = gv('wifi_password');
  if (!ssid) return;
  const status = document.getElementById('wifi-status');
  status.textContent = `Connecting to ${ssid}...`;
  try {
    const res = await fetch('/api/wifi/connect', {
      method: 'POST',
      headers: {'Content-Type': 'application/json'},
      body: JSON.stringify({ssid, password}),
    });
    const data = await res.json();
    status.textContent = data.message || (data.ok ? 'Connection started.' : 'Connection failed.');
    if (data.ok) {
      hideWifiConnect();
      status.textContent = data.message || `Connecting to ${ssid}...`;
    }
  } catch (e) {
    status.textContent = 'Connection request failed: ' + e.message;
  }
}

function escapeHtml(value) {
  return String(value).replace(/[&<>"']/g, ch => ({
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#39;',
  }[ch]));
}

async function loadSystemStatus() {
  try {
    const [sys, usage] = await Promise.all([
      fetch('/api/system').then(r=>r.json()),
      fetch('/api/usage').then(r=>r.json()),
    ]);

    function fmt_dur(secs) {
      if (!secs) return '—';
      const d = Math.floor(secs / 86400);
      const h = Math.floor((secs % 86400) / 3600);
      const m = Math.floor((secs % 3600) / 60);
      return d > 0 ? `${d}d ${h}h ${m}m` : h > 0 ? `${h}h ${m}m` : `${m}m`;
    }

    const tempC = sys.cpu_temp_c;
    const tempF = tempC != null ? (tempC * 9/5 + 32).toFixed(1) : null;
    const tempStr = tempC != null ? `${tempC}°C / ${tempF}°F` : '—';
    const tempWarn = tempC != null && tempC >= 70;

    const fr  = usage.FR24Unofficial ?? 0;
    const fs  = usage.FlightStats    ?? 0;
    const al  = usage.AirLabs        ?? 0;
    const fa  = usage.FlightAware     ?? 0;
    const mon = usage.month           ?? '';

    document.getElementById('system-status').innerHTML = `
      <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:8px">

        <div style="border:1px solid var(--row-border);border-top:2px solid var(--amber);padding:10px 12px">
          <div style="font-size:10px;letter-spacing:2px;color:var(--muted);margin-bottom:4px">PI UPTIME</div>
          <div style="font-family:'VT323',monospace;font-size:24px;color:var(--amber-bright)">${fmt_dur(sys.uptime_secs)}</div>
        </div>

        <div style="border:1px solid var(--row-border);border-top:2px solid var(--amber);padding:10px 12px">
          <div style="font-size:10px;letter-spacing:2px;color:var(--muted);margin-bottom:4px">SERVICE UPTIME</div>
          <div style="font-family:'VT323',monospace;font-size:24px;color:var(--amber-bright)">${fmt_dur(sys.service_uptime_secs)}</div>
        </div>

        <div style="border:1px solid var(--row-border);border-top:2px solid ${tempWarn?'var(--red)':'var(--amber)'};padding:10px 12px">
          <div style="font-size:10px;letter-spacing:2px;color:var(--muted);margin-bottom:4px">CPU TEMP</div>
          <div style="font-family:'VT323',monospace;font-size:24px;color:${tempWarn?'var(--red)':'var(--amber-bright)'}">${tempStr}${tempWarn?' ⚠':''}</div>
        </div>

        <div style="border:1px solid var(--row-border);border-top:2px solid var(--amber);padding:10px 12px">
          <div style="font-size:10px;letter-spacing:2px;color:var(--muted);margin-bottom:4px">FR24 UNOFFICIAL ${mon}</div>
          <div style="font-family:'VT323',monospace;font-size:24px;color:var(--amber-bright)">${fr} <span style="font-size:13px;color:var(--muted)">calls</span></div>
        </div>

        <div style="border:1px solid var(--row-border);border-top:2px solid var(--amber);padding:10px 12px">
          <div style="font-size:10px;letter-spacing:2px;color:var(--muted);margin-bottom:4px">FLIGHTSTATS ${mon}</div>
          <div style="font-family:'VT323',monospace;font-size:24px;color:var(--amber-bright)">${fs} <span style="font-size:13px;color:var(--muted)">calls</span></div>
        </div>

        <div style="border:1px solid var(--row-border);border-top:2px solid ${al>=900?'var(--red)':'var(--amber)'};padding:10px 12px">
          <div style="font-size:10px;letter-spacing:2px;color:var(--muted);margin-bottom:4px">AIRLABS ${mon}</div>
          <div style="font-family:'VT323',monospace;font-size:24px;color:${al>=900?'var(--red)':'var(--amber-bright)'}">${al}<span style="font-size:13px;color:var(--muted)">/1,000${al>=900?' ⚠':''}</span></div>
        </div>

        <div style="border:1px solid var(--row-border);border-top:2px solid ${fa>=4.5?'var(--red)':'var(--amber)'};padding:10px 12px">
          <div style="font-size:10px;letter-spacing:2px;color:var(--muted);margin-bottom:4px">FLIGHTAWARE ${mon}</div>
          <div style="font-family:'VT323',monospace;font-size:24px;color:${fa>=4.5?'var(--red)':'var(--amber-bright)'}">$${fa.toFixed(2)}<span style="font-size:13px;color:var(--muted)">/$5.00${fa>=4.5?' ⚠':''}</span></div>
        </div>

      </div>
    `;
  } catch(e) {
    document.getElementById('system-status').textContent = 'Could not load system status.';
  }
}
</script>
</body>
</html>
