// Configuration tab: frame settings + firmware card + take control.
// Frame name/mode live in the page header now (frame_header.js), Calendar
// settings have their own tab (frame_calendar.js), and Order/Display mode
// live on the Photos tab (frame_photos.js) -- photos-specific settings,
// not device-wide configuration. window.FRAME_API is set by the template.
// Checkboxes are always sent explicitly as "true"/"false" -- the server
// treats absent fields as "leave unchanged", so a checkbox must never be
// simply omitted.

async function saveConfig() {
  const minutes = parseInt(document.getElementById('refresh_interval_minutes').value, 10) || 60;
  const body = new URLSearchParams({
    orientation: document.getElementById('orientation').value,
    refresh_interval_s: String(minutes * 60),
    quiet_hours_enabled: String(document.getElementById('quiet_hours_enabled').checked),
    quiet_hours_start: document.getElementById('quiet_hours_start').value || '22:00',
    quiet_hours_end: document.getElementById('quiet_hours_end').value || '07:00',
    timezone: document.getElementById('timezone').value || 'UTC',
  });
  const resp = await fetch(`${window.FRAME_API}/config`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body,
  });
  if (!resp.ok) {
    throw new Error(await apiError(resp));
  }
}

document.getElementById('config-form').addEventListener('submit', async (e) => {
  e.preventDefault();
  try {
    await saveConfig();
    showStatus(true, 'Saved.');
  } catch (e) {
    showStatus(false, e.message);
  }
});

async function takeControl() {
  try {
    const resp = await fetch(`${window.FRAME_API}/take-control`, { method: 'POST' });
    if (!resp.ok) throw new Error(await apiError(resp));
    showStatus(true, 'You have control now.');
    loadControl();
  } catch (e) {
    showStatus(false, e.message);
  }
}

async function loadControl() {
  const banner = document.getElementById('control-banner');
  try {
    const resp = await fetch(`${window.FRAME_API}/queue`);
    if (!resp.ok) return; // unconfigured frame: control still works via 409s
    const data = await resp.json();
    if (data.control && !data.control.you) {
      banner.style.display = 'flex';
      document.getElementById('control-holder').textContent = data.control.controller
        ? `${data.control.controller} currently has control of this frame.`
        : 'Nobody has control of this frame yet.';
    } else {
      banner.style.display = 'none';
    }
  } catch (e) { /* banner is best-effort */ }
}

document.getElementById('take-control').addEventListener('click', takeControl);

// ---- Advanced configuration: color palette ----
//
// Hex field and R/G/B number fields are kept in sync live, both
// directions -- editing either updates the other plus the preview
// swatch. Hex stays the field actually read at save time (it's what
// the server already validates as #rrggbb); the R/G/B fields are purely
// an alternate, more precise way to arrive at the same value than
// eyeballing a color-picker swatch.

function paletteHexInputs() {
  return Array.from(document.querySelectorAll('.palette-hex'))
    .sort((a, b) => Number(a.dataset.index) - Number(b.dataset.index));
}

function hexFromRgb(r, g, b) {
  const clamp = (v) => Math.max(0, Math.min(255, Math.round(Number(v) || 0)));
  return '#' + [r, g, b].map((v) => clamp(v).toString(16).padStart(2, '0')).join('');
}

function rgbFromHex(hex) {
  const m = /^#?([0-9a-f]{6})$/i.exec((hex || '').trim());
  if (!m) return null;
  const n = parseInt(m[1], 16);
  return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
}

function paletteFieldsFor(index) {
  const at = (cls) => document.querySelector(`.${cls}[data-index="${index}"]`);
  return { hex: at('palette-hex'), r: at('palette-r'), g: at('palette-g'), b: at('palette-b'), swatch: at('palette-swatch-preview') };
}

function syncPaletteFromHex(index) {
  const f = paletteFieldsFor(index);
  const rgb = rgbFromHex(f.hex.value);
  if (!rgb) return;
  [f.r.value, f.g.value, f.b.value] = rgb;
  f.swatch.style.background = f.hex.value;
}

function syncPaletteFromRgb(index) {
  const f = paletteFieldsFor(index);
  const hex = hexFromRgb(f.r.value, f.g.value, f.b.value);
  f.hex.value = hex;
  f.swatch.style.background = hex;
}

const palettePickerCount = paletteHexInputs().length;
for (let i = 0; i < palettePickerCount; i++) {
  const f = paletteFieldsFor(i);
  f.hex.addEventListener('input', () => syncPaletteFromHex(i));
  [f.r, f.g, f.b].forEach((el) => el.addEventListener('input', () => syncPaletteFromRgb(i)));
}

// Sliders: live numeric readout next to each, no save until the button
// below is clicked.
['color_boost', 'contrast_boost', 'dither_strength'].forEach((id) => {
  const input = document.getElementById(id);
  const readout = document.getElementById(`${id}_value`);
  input.addEventListener('input', () => { readout.textContent = Number(input.value).toFixed(2); });
});

async function savePalette(extra) {
  const body = new URLSearchParams(extra || {});
  for (const input of paletteHexInputs()) {
    body.append('palette', input.value);
  }
  try {
    const resp = await fetch(`${window.FRAME_API}/config`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body,
    });
    if (!resp.ok) throw new Error(await apiError(resp));
    showStatus(true, 'Saved.');
    loadPreview();
  } catch (e) {
    showStatus(false, e.message);
  }
}

document.getElementById('palette-save').addEventListener('click', () => {
  savePalette({
    color_boost: document.getElementById('color_boost').value,
    contrast_boost: document.getElementById('contrast_boost').value,
    dither_strength: document.getElementById('dither_strength').value,
  });
});

document.getElementById('palette-reset').addEventListener('click', () => {
  const inputs = paletteHexInputs();
  window.DEFAULT_PALETTE_HEX.forEach((hex, i) => {
    inputs[i].value = hex;
    syncPaletteFromHex(i);
  });
  ['color_boost', 'contrast_boost', 'dither_strength'].forEach((id) => {
    document.getElementById(id).value = '1';
    document.getElementById(`${id}_value`).textContent = '1.00';
  });
  savePalette({ palette_reset: 'true', color_boost: '1', contrast_boost: '1', dither_strength: '1' });
});

// ---- Preview: current photo vs. how it renders with saved settings ----

function loadPreview() {
  const bust = Date.now(); // avoid a stale cached image after settings change
  document.getElementById('preview-original').src = `${window.FRAME_API}/preview/original?_=${bust}`;
  document.getElementById('preview-rendered').src = `${window.FRAME_API}/preview/rendered?_=${bust}`;
}

document.getElementById('preview-refresh').addEventListener('click', loadPreview);
loadPreview();

// ---- Battery alerts card ----

document.getElementById('battery-alert-save').addEventListener('click', async () => {
  const raw = document.getElementById('battery_alert_threshold_pct').value.trim();
  const body = new URLSearchParams({
    battery_alert_threshold_pct: raw === '' ? '-1' : raw,
  });
  try {
    const resp = await fetch(`${window.FRAME_API}/config`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body,
    });
    if (!resp.ok) throw new Error(await apiError(resp));
    showStatus(true, 'Saved.');
  } catch (e) {
    showStatus(false, e.message);
  }
});

// ---- Firmware card ----

document.getElementById('firmware-upload').addEventListener('click', async () => {
  const input = document.getElementById('firmware-file');
  if (!input.files.length) {
    showStatus(false, 'Pick a firmware .bin first.');
    return;
  }
  const form = new FormData();
  form.append('file', input.files[0]);
  try {
    const resp = await fetch(`${window.FRAME_API}/firmware`, { method: 'POST', body: form });
    if (!resp.ok) {
      throw new Error(await apiError(resp));
    }
    const result = await resp.json();
    document.getElementById('firmware-available').textContent =
      `Uploaded: v${result.version} -- the frame updates itself on its next wake if it's running something else.`;
    showStatus(true, `Firmware v${result.version} uploaded (${result.size} bytes).`);
  } catch (e) {
    showStatus(false, e.message);
  }
});

function showRepoDisplayMode(url) {
  document.getElementById('firmware-repo-text').textContent = url;
  document.getElementById('firmware-repo-display').style.display = url ? 'block' : 'none';
  document.getElementById('firmware-repo-edit').style.display = url ? 'none' : 'block';
}

document.getElementById('firmware-repo-edit-btn').addEventListener('click', () => {
  document.getElementById('firmware-repo-display').style.display = 'none';
  document.getElementById('firmware-repo-edit').style.display = 'block';
  document.getElementById('firmware_update_repo_url').focus();
});

document.getElementById('firmware-settings-save').addEventListener('click', async () => {
  try {
    const body = new URLSearchParams({
      firmware_update_repo_url: document.getElementById('firmware_update_repo_url').value || '',
      firmware_auto_update: String(document.getElementById('firmware_auto_update').checked),
    });
    const resp = await fetch(`${window.FRAME_API}/config`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body,
    });
    if (!resp.ok) throw new Error(await apiError(resp));
    showStatus(true, 'Saved.');
    showRepoDisplayMode(document.getElementById('firmware_update_repo_url').value.trim());
    loadFirmwareCheck();
  } catch (e) {
    showStatus(false, e.message);
  }
});

async function loadFirmwareCheck(force) {
  const statusEl = document.getElementById('firmware-gitea-status');
  const btn = document.getElementById('firmware-update-btn');
  const boardEl = document.getElementById('firmware-board');
  try {
    const resp = await fetch(`${window.FRAME_API}/firmware/check` + (force ? '?force=true' : ''), { method: 'POST' });
    if (!resp.ok) {
      if (force) {
        showStatus(false, await apiError(resp));
      }
      return;
    }
    const data = await resp.json();
    if (data.board) {
      boardEl.textContent = `Detected board: ${data.board}`;
    }
    if (!data.enabled) {
      statusEl.style.display = 'none';
      btn.style.display = 'none';
      if (force) {
        showStatus(false, 'No Gitea repo URL configured.');
      }
      return;
    }
    statusEl.style.display = 'block';
    if (!data.board) {
      statusEl.textContent = 'Waiting for the frame to check in before it can look up the right build.';
      btn.style.display = 'none';
    } else if (data.update_available) {
      statusEl.textContent = `Update available: v${data.latest_version}.`;
      btn.style.display = 'inline-block';
    } else if (data.latest_version) {
      statusEl.textContent = `Up to date (v${data.latest_version}).`;
      btn.style.display = 'none';
    } else {
      statusEl.textContent = 'No releases found yet.';
      btn.style.display = 'none';
    }
    if (force) {
      showStatus(true, 'Checked.');
    }
  } catch (e) {
    // A failed passive poll is silent; an explicit "Check now" click
    // still surfaces the error.
    if (force) {
      showStatus(false, e.message);
    }
  }
}

document.getElementById('firmware-check-now').addEventListener('click', () => loadFirmwareCheck(true));

document.getElementById('firmware-update-btn').addEventListener('click', async () => {
  const btn = document.getElementById('firmware-update-btn');
  btn.disabled = true;
  try {
    const resp = await fetch(`${window.FRAME_API}/firmware/apply-latest`, { method: 'POST' });
    if (!resp.ok) {
      throw new Error(await apiError(resp));
    }
    const result = await resp.json();
    document.getElementById('firmware-available').textContent =
      `Uploaded: v${result.version} -- the frame updates itself on its next wake if it's running something else.`;
    showStatus(true, `Firmware v${result.version} staged from Gitea.`);
    loadFirmwareCheck();
  } catch (e) {
    showStatus(false, e.message);
  } finally {
    btn.disabled = false;
  }
});

loadControl();
loadFirmwareCheck();
// The server throttles actual Gitea API calls itself, so this poll is
// cheap either way.
setInterval(loadFirmwareCheck, 60000);
