// Units — the shared toggle and unit-aware formatting for every measure we show.
// The API always speaks imperial (°F, inches, mph); the unit is a pure display
// concern. Pages format through fmtTemp/fmtPrecip/fmtWind and re-render on
// `onUnitChange`, so stored and plotted values stay imperial and only the numbers
// shown flip.
//
// One flag drives all of them: picking °C also gives mm and km/h. A reader who
// wants Celsius wants the rest metric too, and a per-measure control would be
// three toggles in a header that has room for one. The button still shows °F/°C
// because temperature is what people look for.
//
// Absolute humidity (g/m³) is metric in both systems — there is no imperial unit
// for it anyone would recognise — so it is formatted here but never converted.

const UNIT_KEY = "thermograph:unit";

// First-time default: pick °F only for the countries that actually use it (the US
// plus a handful of territories/nations), otherwise °C — read off the browser
// locale's region subtag (en-US → F, de-DE → C). A stored choice always wins, so
// this only affects visitors who've never toggled. Not persisted, so an unstored
// visitor keeps re-deriving the same locale default until they pick one.
const F_REGIONS = new Set(["US", "PR", "GU", "VI", "AS", "MP", "UM", "BS", "BZ", "KY", "PW", "FM", "MH", "LR"]);
function defaultUnit() {
  const langs = (navigator.languages && navigator.languages.length)
    ? navigator.languages : [navigator.language];
  for (const l of langs) {
    const m = /-([A-Za-z]{2})\b/.exec(l || "");
    if (m) return F_REGIONS.has(m[1].toUpperCase()) ? "F" : "C";
  }
  return "F"; // no region subtag to go on → keep the historical default
}
// A climate page knows which city it is about, so it publishes that country's
// convention on <body data-unit-default>. It sits *between* the stored choice and
// the locale guess: better than the locale (a UK visitor reading about Phoenix
// wants the °F the page already rendered, and repainting it to °C would just be a
// flash), but never better than an explicit toggle. Pages with no single city —
// the homepage, the interactive views — emit no attribute and fall through.
function pageDefault() {
  const v = document.body && document.body.dataset.unitDefault;
  return (v === "C" || v === "F") ? v : null;
}
const _stored = localStorage.getItem(UNIT_KEY);
let _unit = (_stored === "C" || _stored === "F")
  ? _stored
  : (pageDefault() || defaultUnit());
const _unitCbs = [];

export function getUnit() { return _unit; }

// A Fahrenheit value as a number in the active unit.
export function toUnit(vF) { return _unit === "C" ? (vF - 32) * 5 / 9 : vF; }

// A Fahrenheit value formatted as a rounded temperature in the active unit.
// `withLetter` appends the C/F letter (off by default — the nav toggle shows it).
export function fmtTemp(vF, withLetter) {
  if (vF == null || isNaN(vF)) return "—";
  return `${Math.round(toUnit(vF))}°${withLetter ? _unit : ""}`;
}

// A Fahrenheit-degree *difference* (a span or tolerance, not an absolute
// reading) formatted in the active unit. Differences scale by 5/9 with no 32°
// offset, so they can't go through toUnit/fmtTemp.
export function fmtDelta(dF, withLetter) {
  if (dF == null || isNaN(dF)) return "—";
  const d = _unit === "C" ? dF * 5 / 9 : dF;
  return `${Math.round(d)}°${withLetter ? _unit : ""}`;
}

// --- precipitation (stored in inches) ----------------------------------------
const MM_PER_IN = 25.4;

// Inches as a number in the active unit.
export function toPrecip(vIn) { return _unit === "C" ? vIn * MM_PER_IN : vIn; }

// Millimetres are whole numbers — that is how rainfall is reported, and a tenth
// of a millimetre is below what the source resolves. Inches keep two decimals,
// since a whole inch of rain is a lot to round to.
// The suffix is " in"/" mm" to match what content.py renders server-side, so a
// climate page's repaint reproduces the text it shipped with rather than swapping
// the glyph. Compact surfaces (the tier bar) pass withUnit=false and add their own.
export function fmtPrecip(vIn, withUnit = true) {
  if (vIn == null || isNaN(vIn)) return "—";
  return _unit === "C"
    ? `${Math.round(vIn * MM_PER_IN)}${withUnit ? " mm" : ""}`
    : `${vIn.toFixed(2)}${withUnit ? " in" : ""}`;
}

export const precipUnit = () => (_unit === "C" ? "mm" : "in");

// --- wind (stored in mph) ----------------------------------------------------
const KMH_PER_MPH = 1.609344;

export function toWind(vMph) { return _unit === "C" ? vMph * KMH_PER_MPH : vMph; }

export function fmtWind(vMph, withUnit = true) {
  if (vMph == null || isNaN(vMph)) return "—";
  const v = Math.round(toWind(vMph));
  return `${v}${withUnit ? (_unit === "C" ? " km/h" : " mph") : ""}`;
}

export const windUnit = () => (_unit === "C" ? "km/h" : "mph");

// --- absolute humidity (g/m³ in both systems) --------------------------------
export function fmtHumid(v, withUnit = true) {
  if (v == null || isNaN(v)) return "—";
  return `${v.toFixed(1)}${withUnit ? " g/m³" : ""}`;
}

export const humidUnit = () => "g/m³";

export function onUnitChange(cb) { _unitCbs.push(cb); }

// --- unit symbols in prose ---------------------------------------------------
// Static copy that names a unit ("wind speed (mph)") marks it up as
// <span data-unit-label="wind">mph</span>. The HTML ships the imperial symbol so
// there's something sensible with no JS; this keeps it honest once a unit is
// known. Runs on load and on every toggle, wherever units.js is imported.
const UNIT_LABEL = {
  temp: () => `°${_unit}`,
  precip: () => (_unit === "C" ? "mm" : "inches"),
  wind: () => windUnit(),
  humid: () => "g/m³",
};

function paintUnitLabels() {
  for (const el of document.querySelectorAll("[data-unit-label]")) {
    const f = UNIT_LABEL[el.dataset.unitLabel];
    if (f) el.textContent = f();
  }
}

export function setUnit(u) {
  const nu = (u === "C") ? "C" : "F";
  if (nu === _unit) return;
  _unit = nu;
  try { localStorage.setItem(UNIT_KEY, _unit); } catch (e) {}
  document.querySelectorAll(".unit-toggle").forEach(syncUnitToggle);
  paintUnitLabels();
  _unitCbs.forEach((cb) => { try { cb(_unit); } catch (e) {} });
}

function syncUnitToggle(wrap) {
  wrap.querySelectorAll("button[data-unit]").forEach((b) => {
    const on = b.dataset.unit === _unit;
    b.classList.toggle("active", on);
    b.setAttribute("aria-pressed", on ? "true" : "false");
  });
}

// Drop a segmented °F/°C control into the header's top-right, ahead of the view
// switcher. A page opts out with data-no-unit-toggle on <body>.
(function buildUnitToggle() {
  const brand = document.querySelector(".brand");
  if (!brand || brand.querySelector(".unit-toggle")) return;
  if (document.body.dataset.noUnitToggle != null) return;
  const wrap = document.createElement("div");
  wrap.className = "unit-toggle";
  wrap.setAttribute("role", "group");
  // Labelled for what it actually switches now — temperature, precipitation and
  // wind — even though the buttons read °F/°C.
  wrap.setAttribute("aria-label", "Units");
  wrap.innerHTML = '<button type="button" data-unit="F">°F</button>'
    + '<button type="button" data-unit="C">°C</button>';
  wrap.addEventListener("click", (e) => {
    const b = e.target.closest("button[data-unit]");
    if (b) setUnit(b.dataset.unit);
  });
  // Lives inside the nav menu panel: display:contents keeps it inline top-right
  // on desktop; on phones it stacks inside the hamburger dropdown. Fall back to a
  // brand sibling if the panel isn't present.
  const panel = brand.querySelector(".nav-panel");
  if (panel) panel.appendChild(wrap);
  else brand.insertBefore(wrap, brand.querySelector(".view-nav"));
  syncUnitToggle(wrap);
})();

paintUnitLabels();
