// hosts/door-panel/'s dashboard logic. Vanilla JS, no framework, no build step —
// same "vendored, dependency-free" choice as every other kiosk frontend in this
// project. See dashboard.html's top comment for the two-backend split.
"use strict";

const params = new URLSearchParams(location.search);
const IDENTITY_API = (params.get("identity_api") || "").replace(/\/$/, "");
const IDENTITY_TOKEN = params.get("identity_token") || "";
const PANTRY_API = (params.get("pantry_api") || "").replace(/\/$/, "");
const PANTRY_TOKEN = params.get("pantry_token") || "";

if (!IDENTITY_API || !IDENTITY_TOKEN) {
  document.body.innerHTML =
    '<p class="error" style="padding:24px">identity not configured — missing ' +
    "?identity_api=&identity_token= in the URL.</p>";
  throw new Error("dashboard: missing identity query params");
}

function identityApi(path) {
  return fetch(`${IDENTITY_API}${path}`, { headers: { Authorization: `Bearer ${IDENTITY_TOKEN}` } }).then((res) => {
    if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
    return res.json();
  });
}

function pantryApi(path) {
  return fetch(`${PANTRY_API}${path}`, { headers: { Authorization: `Bearer ${PANTRY_TOKEN}` } }).then((res) => {
    if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
    return res.json();
  });
}

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

// --- Weather + clothing --------------------------------------------------------
// A plain lookup table, not an LLM call — this is a deterministic-enough problem
// (temperature + a couple of condition keywords) that a round trip to Ollama would
// only add latency and a failure mode for no real gain. See ../README.md.
function clothingSuggestion(tempC, condition) {
  const cond = (condition || "").toLowerCase();
  const layers =
    tempC === null
      ? null
      : tempC < 0
      ? "Heavy coat, hat, gloves"
      : tempC < 10
      ? "Warm coat or jacket"
      : tempC < 16
      ? "Light jacket or sweater"
      : tempC < 21
      ? "Light layers"
      : "T-shirt weather";
  const wet = /rain|drizzle|shower|storm|snow|sleet/.test(cond) ? " — bring an umbrella/waterproofs" : "";
  return layers ? layers + wet : null;
}

function loadWeather() {
  identityApi("/weather")
    .then((data) => {
      const hero = document.getElementById("weather-hero");
      const suggestion = document.getElementById("clothing-suggestion");
      if (!data.available) {
        hero.innerHTML = '<span class="hint">No weather data yet</span>';
        suggestion.hidden = true;
        return;
      }
      hero.innerHTML =
        `<span class="weather-temp">${escapeHtml(data.temperature || "")}</span>` +
        `<span class="weather-condition">${escapeHtml(data.condition || "")}</span>`;

      const match = /(-?\d+(\.\d+)?)/.exec(data.temperature || "");
      const tempC = match ? parseFloat(match[1]) : null;
      const text = clothingSuggestion(tempC, data.condition);
      if (text) {
        suggestion.textContent = text;
        suggestion.hidden = false;
      } else {
        suggestion.hidden = true;
      }
    })
    .catch(() => {
      document.getElementById("weather-hero").innerHTML = '<span class="hint">Weather unavailable</span>';
    });
}

// --- Who's home ------------------------------------------------------------------
// Three states, not two: home / away / unknown. "Unknown" covers both an
// unreachable HA and a device-less person (grandmother, a guest) who was never
// manually marked either way — see identity/server.py's presence() docstring for
// why defaulting that case to "away" would be actively wrong, not just vague.
function statusLabel(p) {
  if (p.home === true) return p.room ? `Home — ${p.room}` : "Home";
  if (p.home === false) return "Away";
  return "Unknown";
}

function statusClass(p) {
  if (p.home === true) return "status-home";
  if (p.home === false) return "status-away";
  return "";
}

function markPresenceRequest(personId, home) {
  return fetch(`${IDENTITY_API}/presence/manual`, {
    method: "POST",
    headers: { Authorization: `Bearer ${IDENTITY_TOKEN}`, "Content-Type": "application/json" },
    body: JSON.stringify({ person_id: personId, home }),
  }).then(loadPresence);
}

// Same blob-fetch approach as register.js's loadAvatar — see that file's comment
// for why a plain <img src="..."> can't be used against a bearer-token-gated endpoint.
function loadAvatar(imgContainer, personId) {
  fetch(`${IDENTITY_API}/people/${personId}/photo`, { headers: { Authorization: `Bearer ${IDENTITY_TOKEN}` } })
    .then((res) => (res.ok ? res.blob() : Promise.reject()))
    .then((blob) => {
      const img = document.createElement("img");
      img.src = URL.createObjectURL(blob);
      imgContainer.replaceChildren(img);
    })
    .catch(() => {});
}

function loadPresence() {
  const el = document.getElementById("presence-list");
  identityApi("/presence")
    .then((data) => {
      const people = data.people || [];
      if (!people.length) {
        el.innerHTML = '<p class="hint">Nobody registered yet.</p>';
        return;
      }
      el.innerHTML = people
        .map((p) => {
          // Device-less people (has_device === false) get manual Home/Away buttons
          // right here — the only way their presence is ever set, since there's no
          // phone to track. See identity/README.md's "no device" section.
          const manualButtons = p.has_device
            ? ""
            : `<button type="button" class="big-btn secondary" data-mark="${p.id}" data-home="1" style="min-height:40px;min-width:0;padding:0 12px;font-size:13px;margin-left:6px">Home</button>
               <button type="button" class="big-btn secondary" data-mark="${p.id}" data-home="0" style="min-height:40px;min-width:0;padding:0 12px;font-size:13px;margin-left:6px">Away</button>`;
          return `<div class="card">
              <span class="avatar" data-person="${p.id}">👤</span>
              <span class="card-name">${escapeHtml(p.name)}</span>
              <span class="card-meta ${statusClass(p)}">${escapeHtml(statusLabel(p))}</span>
              ${manualButtons}
            </div>`;
        })
        .join("");
      el.querySelectorAll("[data-mark]").forEach((btn) => {
        btn.addEventListener("click", () => markPresenceRequest(Number(btn.dataset.mark), btn.dataset.home === "1"));
      });
      people.forEach((p) => {
        if (p.has_photo) {
          const avatarEl = el.querySelector(`.avatar[data-person="${p.id}"]`);
          if (avatarEl) loadAvatar(avatarEl, p.id);
        }
      });
    })
    .catch((err) => {
      el.innerHTML = `<p class="error">Could not load presence: ${escapeHtml(err.message)}</p>`;
    });
}

// --- Running low -----------------------------------------------------------------
function loadShoppingList() {
  const el = document.getElementById("shopping-list");
  if (!PANTRY_API || !PANTRY_TOKEN) {
    el.innerHTML = '<p class="hint">pantry-vision not configured for this display.</p>';
    return;
  }
  pantryApi("/shopping-list")
    .then((data) => {
      const items = data.items || [];
      if (!items.length) {
        el.innerHTML = '<p class="hint">Nothing running low.</p>';
        return;
      }
      el.innerHTML = items
        .map(
          (item) =>
            `<div class="card">
              <span class="card-name">${escapeHtml(item.name)}</span>
              <span class="card-meta">need ${escapeHtml(String(item.amount_missing ?? ""))}</span>
            </div>`
        )
        .join("");
    })
    .catch((err) => {
      el.innerHTML = `<p class="error">Could not load shopping list: ${escapeHtml(err.message)}</p>`;
    });
}

function refreshAll() {
  loadWeather();
  loadPresence();
  loadShoppingList();
}

refreshAll();
// An ambient always-on display, not an interactive app — poll rather than needing a
// tap to refresh. 60s matches the weather MQTT topic's own realistic update cadence
// (an HA automation on a state-change trigger, not a fast poll itself).
setInterval(refreshAll, 60000);
