(() => {
  const KEY = (window.OPENWEATHER_API_KEY || "").trim();
  if (!KEY) alert("Missing API key. Create config.js (or edit config.example.js) and paste your OpenWeather key.");

  const cityNameEl = document.getElementById("cityName");
  const qEl = document.getElementById("q");
  const goEl = document.getElementById("go");
  const locateBtn = document.getElementById("locate");
  const toggleLabelsBtn = document.getElementById("toggleLabels");

  const wxCard = document.getElementById("wxCard");
  const wxTemp = document.getElementById("wxTemp");
  const wxUnit = document.getElementById("wxUnit");
  const wxIcon = document.getElementById("wxIcon");
  const wxFeel = document.getElementById("wxFeel");
  const wxPrec = document.getElementById("wxPrec");
  const wxWind = document.getElementById("wxWind");
  const wxDir  = document.getElementById("wxDir");
  const wxHum  = document.getElementById("wxHum");
  const wxCloud= document.getElementById("wxCloud");
  const wxPress= document.getElementById("wxPress");

  const slider = document.getElementById("slider");
  const ticks = document.getElementById("ticks");
  const stamp = document.getElementById("stamp");
  const prev = document.getElementById("prev");
  const next = document.getElementById("next");

  const tableHeadRow = document.getElementById("tableHeadRow");
  const rowWeather = document.getElementById("rowWeather");
  const rowAlert = document.getElementById("rowAlert");
  const rowTemp = document.getElementById("rowTemp");
  const rowHum = document.getElementById("rowHum");

  const rpDate = document.getElementById("rpDate");
  const rpList = document.getElementById("rpList");
  const tabAll = document.getElementById("tabAll");
  const tabAlerts = document.getElementById("tabAlerts");

  const toast = document.getElementById("toast");

  const units = "metric"; // °C
  let map, overlay, markersLayer;
  let showLabels = true;

  let current = { name: "London", lat: 51.5072, lon: -0.1276 };
  let forecastList = [];
  let hourlyPoints = [];
  let selectedIdx = 0;

  let ctrl = null;
  let activeTab = "all";

  const iconUrl = (code) => `https://openweathermap.org/img/wn/${code}@2x.png`;
  const fmt2 = (n) => (Number.isFinite(n) ? n.toFixed(2) : "—");
  const fmtTime = (d) => d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
  const fmtDateLong = (d) => d.toLocaleDateString([], { day: "2-digit", month: "long", year: "numeric" });
  const clamp = (x, a, b) => Math.max(a, Math.min(b, x));

  const toastMsg = (msg) => {
    toast.textContent = msg;
    toast.hidden = false;
    clearTimeout(toastMsg._t);
    toastMsg._t = setTimeout(() => (toast.hidden = true), 2200);
  };

  async function fetchJSON(url, signal) {
    const res = await fetch(url, { signal });
    const data = await res.json().catch(() => null);
    if (!res.ok) throw new Error((data && data.message) ? data.message : `Request failed (${res.status})`);
    return data;
  }

  async function geocode(q, signal) {
    const url = `https://api.openweathermap.org/geo/1.0/direct?q=${encodeURIComponent(q)}&limit=1&appid=${encodeURIComponent(KEY)}`;
    const arr = await fetchJSON(url, signal);
    if (!Array.isArray(arr) || !arr.length) throw new Error("City not found. Try: London,GB");
    return arr[0];
  }

  async function currentWeather(lat, lon, signal) {
    const url = `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&units=${units}&appid=${encodeURIComponent(KEY)}`;
    return await fetchJSON(url, signal);
  }

  async function forecast5(lat, lon, signal) {
    const url = `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&units=${units}&appid=${encodeURIComponent(KEY)}`;
    return await fetchJSON(url, signal);
  }

  async function nearbyCities(lat, lon, signal) {
    const url = `https://api.openweathermap.org/data/2.5/find?lat=${lat}&lon=${lon}&cnt=14&units=${units}&appid=${encodeURIComponent(KEY)}`;
    return await fetchJSON(url, signal);
  }

  async function oneCallAlerts(lat, lon, signal) {
    const url = `https://api.openweathermap.org/data/2.5/onecall?lat=${lat}&lon=${lon}&exclude=minutely,hourly,daily&appid=${encodeURIComponent(KEY)}`;
    return await fetchJSON(url, signal);
  }

  const LAYERS = {
    temperature: "temp_new",
    precipitation: "precipitation_new",
    pressure: "pressure_new",
    wind: "wind_new",
    clouds: "clouds_new",
  };

  function overlayTile(layerKey) {
    const layer = LAYERS[layerKey] || LAYERS.precipitation;
    return L.tileLayer(`https://tile.openweathermap.org/map/${layer}/{z}/{x}/{y}.png?appid=${encodeURIComponent(KEY)}`, {
      opacity: 0.86
    });
  }

  function setPillsActive(layerKey) {
    document.querySelectorAll(".pill").forEach(btn => {
      const isActive = btn.dataset.layer === layerKey;
      btn.classList.toggle("active", isActive);
      btn.setAttribute("aria-selected", String(isActive));
    });
  }

  function setOverlay(layerKey) {
    if (!map) return;
    if (overlay) map.removeLayer(overlay);
    overlay = overlayTile(layerKey).addTo(map);
    setPillsActive(layerKey);
  }

  function labelColor(tempC) {
    if (!Number.isFinite(tempC)) return "labelBlue";
    if (tempC <= 8) return "labelBlue";
    if (tempC <= 16) return "labelTeal";
    if (tempC <= 24) return "labelYellow";
    return "labelOrange";
  }

  function makeLabelHTML(name, temp) {
    const cls = labelColor(temp);
    const t = Number.isFinite(temp) ? Math.round(temp) : "";
    return `
      <div class="cityLabel ${cls}">
        <span class="cityNum">${t}</span>
        <span class="cityText">${name}</span>
      </div>
    `;
  }

  function ensureLabelCSS() {
    if (document.getElementById("labelCss")) return;
    const css = document.createElement("style");
    css.id = "labelCss";
    css.textContent = `
      .cityLabel{
        display:inline-flex; align-items:center; gap:8px;
        padding:6px 10px; border-radius:8px;
        border:1px solid rgba(255,255,255,.10);
        font-weight:900; font-size:12px;
        box-shadow: 0 10px 18px rgba(0,0,0,.25);
      }
      .cityNum{
        width:22px; height:22px; border-radius:6px;
        display:grid; place-items:center;
        background: rgba(0,0,0,.22);
      }
      .labelBlue{ background: rgba(59,130,246,.65); }
      .labelTeal{ background: rgba(45,212,191,.55); }
      .labelYellow{ background: rgba(250,204,21,.55); color:#111827; }
      .labelOrange{ background: rgba(255,106,42,.55); }
    `;
    document.head.appendChild(css);
  }

  function buildHourlyFromForecast(list) {
    const out = [];
    if (!Array.isArray(list) || list.length < 2) return out;

    const now = new Date();
    const start = new Date(now);
    start.setMinutes(0, 0, 0);
    start.setHours(start.getHours() + 1);

    for (let i = 0; i < 6; i++) {
      const t = new Date(start);
      t.setHours(start.getHours() + i);

      const ts = t.getTime();
      let a = null, b = null;

      for (let j = 0; j < list.length - 1; j++) {
        const ta = list[j].dt * 1000;
        const tb = list[j + 1].dt * 1000;
        if (ts >= ta && ts <= tb) { a = list[j]; b = list[j + 1]; break; }
      }
      if (!a) { a = list[0]; b = list[1]; }

      const ta = a.dt * 1000;
      const tb = b.dt * 1000;
      const ratio = tb === ta ? 0 : clamp((ts - ta) / (tb - ta), 0, 1);

      const lerp = (x, y) => (typeof x === "number" && typeof y === "number") ? (x + (y - x) * ratio) : (typeof x === "number" ? x : y);

      const temp = lerp(a.main?.temp, b.main?.temp);
      const hum = lerp(a.main?.humidity, b.main?.humidity);
      const pop = lerp(a.pop ?? 0, b.pop ?? 0);
      const icon = (ratio < 0.5 ? a.weather?.[0]?.icon : b.weather?.[0]?.icon) || a.weather?.[0]?.icon || "";

      out.push({ at: t, temp, hum, pop, icon, main: (ratio < 0.5 ? a.weather?.[0]?.main : b.weather?.[0]?.main) || "" });
    }
    return out;
  }

  function renderTable(hourly) {
    tableHeadRow.innerHTML = `<div></div>`;
    rowWeather.innerHTML = "";
    rowAlert.innerHTML = "";
    rowTemp.innerHTML = "";
    rowHum.innerHTML = "";

    for (const h of hourly) {
      const th = document.createElement("div");
      th.className = "th";
      th.textContent = fmtTime(h.at);
      tableHeadRow.appendChild(th);

      const cW = document.createElement("div");
      cW.className = "cell";
      cW.innerHTML = h.icon ? `<img alt="" src="${iconUrl(h.icon)}">` : "—";
      rowWeather.appendChild(cW);

      const cA = document.createElement("div");
      cA.className = "cell mutedCell";
      cA.textContent = "—";
      rowAlert.appendChild(cA);

      const cT = document.createElement("div");
      cT.className = "cell";
      cT.textContent = Number.isFinite(h.temp) ? String(Math.round(h.temp)) : "—";
      rowTemp.appendChild(cT);

      const cH = document.createElement("div");
      cH.className = "cell";
      cH.textContent = Number.isFinite(h.hum) ? String(Math.round(h.hum)) : "—";
      rowHum.appendChild(cH);
    }
  }

  function renderTicks(hourly) {
    ticks.innerHTML = "";
    hourly.forEach((h, idx) => {
      const s = document.createElement("span");
      s.textContent = fmtTime(h.at);
      s.style.opacity = idx === selectedIdx ? "1" : ".75";
      ticks.appendChild(s);
    });
    stamp.textContent = hourly[selectedIdx]
      ? hourly[selectedIdx].at.toLocaleString([], { day: "2-digit", month: "long", hour: "2-digit", minute: "2-digit" })
      : "—";
  }

  function renderWxCard(wx, selectedHour) {
    wxUnit.textContent = "C";
    wxTemp.textContent = Number.isFinite(wx?.main?.temp) ? Math.round(wx.main.temp) : "—";

    const icon = wx?.weather?.[0]?.icon || "";
    if (icon) { wxIcon.src = iconUrl(icon); wxIcon.alt = wx?.weather?.[0]?.main || "Weather"; }

    wxFeel.textContent = Number.isFinite(wx?.main?.feels_like) ? `${Math.round(wx.main.feels_like)}°C` : "—";

    const pop = selectedHour && Number.isFinite(selectedHour.pop) ? `${Math.round(selectedHour.pop * 100)}%` : "0%";
    wxPrec.textContent = pop;

    wxWind.textContent = (typeof wx?.wind?.speed === "number") ? `${fmt2(wx.wind.speed)} m/s` : "—";
    wxDir.textContent = (typeof wx?.wind?.deg === "number") ? `${wx.wind.deg} deg` : "—";
    wxHum.textContent = (typeof wx?.main?.humidity === "number") ? `${wx.main.humidity}%` : "—";
    wxCloud.textContent = (typeof wx?.clouds?.all === "number") ? `${wx.clouds.all}%` : "—";
    wxPress.textContent = (typeof wx?.main?.pressure === "number") ? `${wx.main.pressure} hPa` : "—";

    wxCard.hidden = false;
  }

  function buildTriggerEvents(hourly, city) {
    const ev = [];
    const cold = 15;
    const popRain = 0.6;

    hourly.forEach(h => {
      if (typeof h.temp === "number" && h.temp < cold) {
        ev.push({ tag: "Trigger Event", tagCls: "tagTrigger", title: `Temperature falls below ${cold}°C at ${city}`, time: fmtTime(h.at) });
      }
      if (typeof h.pop === "number" && h.pop >= popRain) {
        ev.push({ tag: "Alert", tagCls: "tagAlert", title: `Moderate Rain likely`, time: fmtTime(h.at) });
      }
    });

    ev.push({ tag: "Report", tagCls: "tagReport", title: `Weather Forecast For ${city}`, time: new Date().toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) });

    const seen = new Set();
    return ev.filter(x => { const k = x.tag + x.title + x.time; if (seen.has(k)) return false; seen.add(k); return true; }).slice(0, 8);
  }

  function renderRightPanel(events) {
    rpList.innerHTML = "";
    if (!events.length) {
      rpList.innerHTML = `<div class="eventCard"><div class="muted">No events.</div></div>`;
      return;
    }
    events.forEach(e => {
      const card = document.createElement("div");
      card.className = "eventCard";
      card.innerHTML = `
        <div class="eventTag ${e.tagCls}">${e.tag}</div>
        <div class="eventTitle">${e.title}</div>
        <div class="eventMeta">
          <span class="muted">${new Date().toLocaleDateString([], { day:"2-digit", month:"short", year:"numeric" })}</span>
          <span>${e.time}</span>
        </div>
      `;
      rpList.appendChild(card);
    });
  }

  async function loadCityByQuery(q) {
    if (ctrl) ctrl.abort();
    ctrl = new AbortController();

    try {
      toastMsg("Loading…");
      const g = await geocode(q, ctrl.signal);
      await loadCity({ name: g.name, lat: g.lat, lon: g.lon });
      toastMsg(`Loaded: ${g.name}`);
    } catch (e) {
      toastMsg(e?.message ? `Error: ${e.message}` : "Error");
    }
  }

  async function loadCity(place) {
    current = place;
    cityNameEl.textContent = place.name;

    map.setView([place.lat, place.lon], 6.5);

    const [wx, fc, near] = await Promise.all([
      currentWeather(place.lat, place.lon, ctrl?.signal),
      forecast5(place.lat, place.lon, ctrl?.signal),
      nearbyCities(place.lat, place.lon, ctrl?.signal),
    ]);

    forecastList = Array.isArray(fc?.list) ? fc.list : [];
    hourlyPoints = buildHourlyFromForecast(forecastList);
    selectedIdx = 0;

    slider.min = "0";
    slider.max = String(Math.max(0, hourlyPoints.length - 1));
    slider.value = "0";

    renderTable(hourlyPoints);
    renderTicks(hourlyPoints);
    renderWxCard(wx, hourlyPoints[selectedIdx]);

    rpDate.textContent = fmtDateLong(new Date());

    const events = buildTriggerEvents(hourlyPoints, place.name);
    window.__allEvents = events;

    try {
      const oc = await oneCallAlerts(place.lat, place.lon, ctrl?.signal);
      const alerts = Array.isArray(oc?.alerts) ? oc.alerts : [];
      window.__alertEvents = alerts.slice(0, 6).map(a => ({
        tag: "Alert",
        tagCls: "tagAlert",
        title: a.event || "Weather Alert",
        time: new Date((a.start || Date.now()/1000) * 1000).toLocaleTimeString([], { hour:"2-digit", minute:"2-digit" })
      }));
    } catch { window.__alertEvents = []; }

    renderRightPanel(activeTab === "alerts" ? (window.__alertEvents || []) : (window.__allEvents || []));

    ensureLabelCSS();
    markersLayer.clearLayers();

    const items = Array.isArray(near?.list) ? near.list : [];
    items.forEach(item => {
      const name = item?.name || "";
      const lat = item?.coord?.lat;
      const lon = item?.coord?.lon;
      const temp = item?.main?.temp;

      if (!name || typeof lat !== "number" || typeof lon !== "number") return;

      const icon = L.divIcon({ className: "", html: makeLabelHTML(name, temp) });
      const m = L.marker([lat, lon], { icon });
      m.on("click", () => loadCity({ name, lat, lon }));
      if (showLabels) markersLayer.addLayer(m);
    });
  }

  function setTab(t) {
    activeTab = t;
    tabAll.classList.toggle("active", t === "all");
    tabAlerts.classList.toggle("active", t === "alerts");
    const list = t === "alerts" ? (window.__alertEvents || []) : (window.__allEvents || []);
    renderRightPanel(list);
  }

  function init() {
    map = L.map("map", { zoomControl: true }).setView([51.5072, -0.1276], 6);

    L.tileLayer("https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png", { maxZoom: 19 }).addTo(map);

    markersLayer = L.layerGroup().addTo(map);

    setOverlay("precipitation");

    document.querySelectorAll(".pill").forEach(btn => btn.addEventListener("click", () => setOverlay(btn.dataset.layer)));

    goEl.addEventListener("click", () => { const q = qEl.value.trim(); if (q) loadCityByQuery(q); });
    qEl.addEventListener("keydown", (e) => { if (e.key === "Enter") { const q = qEl.value.trim(); if (q) loadCityByQuery(q); } });

    slider.addEventListener("input", () => {
      selectedIdx = clamp(parseInt(slider.value, 10), 0, hourlyPoints.length - 1);
      renderTicks(hourlyPoints);
      if (hourlyPoints[selectedIdx]) wxPrec.textContent = `${Math.round((hourlyPoints[selectedIdx].pop || 0) * 100)}%`;
    });

    prev.addEventListener("click", () => { selectedIdx = clamp(selectedIdx - 1, 0, hourlyPoints.length - 1); slider.value = String(selectedIdx); slider.dispatchEvent(new Event("input")); });
    next.addEventListener("click", () => { selectedIdx = clamp(selectedIdx + 1, 0, hourlyPoints.length - 1); slider.value = String(selectedIdx); slider.dispatchEvent(new Event("input")); });

    locateBtn.addEventListener("click", () => {
      if (!navigator.geolocation) return toastMsg("Geolocation not supported");
      navigator.geolocation.getCurrentPosition(
        (pos) => loadCity({ name: "My Location", lat: pos.coords.latitude, lon: pos.coords.longitude }),
        () => toastMsg("Location permission denied"),
        { enableHighAccuracy: true, timeout: 8000 }
      );
    });

    toggleLabelsBtn.addEventListener("click", () => {
      showLabels = !showLabels;
      markersLayer.clearLayers();
      loadCity(current);
      toastMsg(showLabels ? "Labels ON" : "Labels OFF");
    });

    tabAll.addEventListener("click", () => setTab("all"));
    tabAlerts.addEventListener("click", () => setTab("alerts"));

    setTab("all");
    loadCity(current);
  }

  init();
})();