/* global */
// =========================================================
// Real meteostanice data — fetched from
// https://ext.pocasimeteo.cz/ms/api/weather?KlicApi=...
// API key comes from the page URL (?KlicApi=...) or localStorage,
// same convention as the original meteostanice app.
// =========================================================

const NUM_RE = /^-?\d+(\.\d+)?$/;

// The API sends local time as "2026-10-20 22:25:00". Older Safari (iOS) can't
// parse the space-separated form with new Date() at all, so build it by hand.
// Strings carrying a time zone (Z / +02:00) are left to the browser.
function parseDatum(s) {
  if (s instanceof Date) return s;
  if (typeof s !== 'string') return new Date(s);
  if (/(?:[zZ]|[+-]\d\d:?\d\d)$/.test(s)) return new Date(s);
  const m = /^(\d{4})-(\d\d)-(\d\d)[ T](\d\d):(\d\d)(?::(\d\d))?/.exec(s);
  return m ? new Date(+m[1], m[2] - 1, +m[3], +m[4], +m[5], +(m[6] || 0)) : new Date(s);
}

// Site-level config (webcam URL) — set from ?webcam= or pm_webcam localStorage.
window.SiteConfig = window.SiteConfig || {};

const FIELDS = {
  TeplotaVnejsi:  { label: 'Venkovní teplota', unit: '°C',   precision: 1, color: '#ef4444' },
  VlhkostVnejsi:  { label: 'Vlhkost vzduchu',  unit: '%',    precision: 0, color: '#22c55e' },
  TlakRel:        { label: 'Tlak',             unit: 'hPa',  precision: 1, color: '#8b5cf6' },
  Vitr:           { label: 'Vítr',             unit: 'm/s',  precision: 1, color: '#14b8a6' },
  VitrNarazy:     { label: 'Nárazy větru',     unit: 'm/s',  precision: 1, color: '#0ea5e9' },
  VitrSmer:       { label: 'Směr větru',       unit: '°',    precision: 0, color: '#0d3894' },
  SlunZareni:     { label: 'Sluneční záření',  unit: 'W/m²', precision: 0, color: '#f59e0b' },
  UVindex:        { label: 'UV index',         unit: '',     precision: 1, color: '#f59e0b' },
  SrazkyDen:      { label: 'Denní srážky',     unit: 'mm',   precision: 1, color: '#3b82f6' },
  calculatedRain: { label: 'Akt. srážky',      unit: 'mm/5min', precision: 2, color: '#3b82f6' },
  TeplotaVnitrni: { label: 'Teplota vnitřní',  unit: '°C',   precision: 1, color: '#af4444' },
  VlhkostVnitrni: { label: 'Vlhkost vnitřní',  unit: '%',    precision: 0, color: '#02c55e' },
  Te1: { label: 'Teplota 1', unit: '°C', precision: 1, color: '#ef4444' },
  Te2: { label: 'Teplota 2', unit: '°C', precision: 1, color: '#f97316' },
  Te3: { label: 'Teplota 3', unit: '°C', precision: 1, color: '#f59e0b' },
  TeVody: { label: 'Teplota vody', unit: '°C', precision: 1, color: '#0ea5e9' },
  Vl1: { label: 'Vlhkost 1', unit: '%', precision: 0, color: '#22c55e' },
  VlP: { label: 'Vlhkost půdní', unit: '%', precision: 0, color: '#3b82f6' },
  Co2: { label: 'CO₂', unit: 'ppm', precision: 0, color: '#ef4444' },
  Pm2: { label: 'PM 2.5', unit: 'μg/m³', precision: 1, color: '#f43f5e' },
};

// Station data source — mirrors the original predpoved app (api.js):
// the page derives a siteId from its URL directory and POSTs to a
// server-side proxy that holds the real API key.
//
// Resolution order for siteId:
//   1) URL query param ?siteId=...   (persists to localStorage)
//   2) first non-empty path segment of window.location.pathname
//      (e.g. /praha-6-ruzyne/predpoved/  → "praha-6-ruzyne")
//   3) localStorage  pm_site_id  (only when the path carries no site — a
//      stale value must never override the station the URL points at)
//
// Resolution order for proxy URL:
//   1) URL query param ?stationApi=...
//   2) http://localhost:5001 on localhost / 127.0.0.1
//   3) `${origin}/weather-api`  on any other host
//
// Optional legacy direct mode (?KlicApi=...) still works — the loader
// will call ext.pocasimeteo.cz/ms/api/weather?KlicApi= directly if a
// key is present.
let SITE_ID = null;
let API_URL = null;
let LEGACY_KEY = null;
let READY = null;

function pathSiteId() {
  // First non-empty path segment — e.g. /praha-6-ruzyne/predpoved/ → "praha-6-ruzyne"
  const segs = window.location.pathname.split('/').filter(s => s && !/\.html?$/i.test(s));
  return segs.length ? segs[0] : null;
}

function defaultApiUrl() {
  const h = window.location.hostname;
  if (h === 'localhost' || h === '127.0.0.1') return 'http://localhost:5001';
  return `${window.location.protocol}//${window.location.host}/weather-api`;
}

async function resolveConfig() {
  const params = new URLSearchParams(window.location.search);

  // Legacy direct-API key (kept for backward compat) — URL only. It used to
  // persist to localStorage, which made EVERY station's forecast page on the
  // same domain show the data of whichever station was once opened with
  // ?KlicApi=. The proxy mode below covers all real deployments.
  const fromUrlKey = params.get('KlicApi');
  LEGACY_KEY = fromUrlKey || null;

  // siteId
  const fromUrlSite = params.get('siteId') || params.get('site');
  if (fromUrlSite) {
    try { localStorage.setItem('pm_site_id', fromUrlSite); } catch {}
    SITE_ID = fromUrlSite;
  } else {
    // index2.html: z ?<lokalita> na /lokalita/pocasi/, jinak z cesty
    SITE_ID = window.PM_LOKALITA !== undefined ? window.PM_LOKALITA : pathSiteId();
    if (!SITE_ID) { try { SITE_ID = localStorage.getItem('pm_site_id') || null; } catch {} }
  }

  // proxy url override via URL
  const fromUrlApi = params.get('stationApi') || params.get('apiUrl');
  if (fromUrlApi) API_URL = fromUrlApi;

  if (!API_URL) API_URL = defaultApiUrl();
}

function loadSiteConfig() {
  // Webcam URL: ?webcam= override, else pm_webcam from localStorage
  const params = new URLSearchParams(window.location.search);
  const wc = params.get('webcam');
  if (wc) {
    window.SiteConfig.webcam = wc;
    try { localStorage.setItem('pm_webcam', wc); } catch {}
  } else {
    try {
      const stored = localStorage.getItem('pm_webcam');
      if (stored && !window.SiteConfig.webcam) window.SiteConfig.webcam = stored;
    } catch {}
  }
}

READY = Promise.all([resolveConfig(), loadSiteConfig()]);

const LEGACY_BASE = '/ms/api/weather';

// /lokalita/api/weather = the same app and cache as the station page (/lokalita/stanice/);
// /mstest/api/weather answers 400 „Chybějící API klíč“ for localities since 22. 10. 2026
// (accounts of station owners), the page then fell back to the weather-api POST.
const STATION_API = '/lokalita/api/weather';
async function fetchStation() {
  if (READY) await READY;

  // ── Legacy direct API (?KlicApi=...)  ─────────────────────────────
  if (LEGACY_KEY) {
    try {
      const url = `${LEGACY_BASE}?KlicApi=${encodeURIComponent(LEGACY_KEY)}&_=${Date.now()}`;
      const resp = await fetch(url, { method: 'GET', cache: 'no-store' });
      if (!resp.ok) return { ok: false, reason: 'http-' + resp.status };
      let data = await resp.json();
      if (data && data.Zprava === 'Prekrocena frekvence volani') return { ok: false, reason: 'rate-limit' };
      if (!Array.isArray(data)) {
        if (data && data.data) data = data.data;
        else if (data && data.Zprava === 'Posilame data') data = [data];
        else return { ok: false, reason: 'bad-format' };
      }
      return { ok: true, data };
    } catch (err) {
      return { ok: false, reason: 'network', err };
    }
  }

  if (!SITE_ID) return { ok: false, reason: 'no-site' };
  // ── Stejný zdroj jako stránka stanice: její API s cache podle pětiminutovek
  //    (nový záznam hned, jak ho stanice pošle) — aktuální hodnoty se tak na
  //    obou stránkách shodují. Při chybě záloha přes weather-api níže.
  //    STATION_API se mění spolu se STATION_PAGE v app.jsx (/mstest → /ms).
  try {
    const resp = await fetch(`${STATION_API}?stanice=${encodeURIComponent(SITE_ID)}&_=${Date.now()}`, {
      cache: 'no-store',
      signal: typeof AbortSignal !== 'undefined' && AbortSignal.timeout ? AbortSignal.timeout(8000) : undefined,
    });
    if (resp.ok) {
      const data = await resp.json();
      if (Array.isArray(data) && data.length >= 2) return { ok: true, data };
      // {"Data": null} = lokalita je jen předpověď (stanice neposílá data)
      if (data && typeof data === 'object' && 'Data' in data && data.Data == null) return { ok: false, reason: 'no-data' };
    }
  } catch (err) { /* záloha níže */ }

  // ── Proxy mode (same convention as original api.js)  ──────────────
  try {
    const url = `${API_URL.replace(/\/$/, '')}/api/weather-station/${encodeURIComponent(SITE_ID)}`;
    const resp = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      signal: typeof AbortSignal !== 'undefined' && AbortSignal.timeout ? AbortSignal.timeout(8000) : undefined,
    });
    if (!resp.ok) return { ok: false, reason: 'http-' + resp.status };
    const result = await resp.json();
    if (result && result.status && result.status !== 'success') return { ok: false, reason: result.message || 'api-error' };
    const data = result && result.data ? result.data : result;
    if (!Array.isArray(data) || data.length < 2) return { ok: false, reason: 'bad-format' };
    return { ok: true, data };
  } catch (err) {
    return { ok: false, reason: 'network', err };
  }
}

function buildStation(rawRecords) {
  // First row may be metadata (LokalitaStanice / DoplCidlaJson) — strip it.
  let meta = null;
  let body = rawRecords;
  if (rawRecords.length && (rawRecords[0].LokalitaStanice || rawRecords[0].DoplCidlaJson)) {
    meta = rawRecords[0];
    body = rawRecords.slice(1);
  }
  const sorted = [...body].sort((a, b) => parseDatum(a.Datum) - parseDatum(b.Datum));
  const records = sorted.map((curr, i) => {
    const obj = {};
    for (const k in curr) {
      const v = curr[k];
      if (v === null || v === undefined || v === '' || v === 'null') obj[k] = null;
      else if (typeof v === 'string') obj[k] = NUM_RE.test(v.trim()) ? parseFloat(v) : v;
      else obj[k] = v;
    }
    // derived rainfall increment (mm in this interval)
    if (i === 0) {
      obj.calculatedRain = 0;
    } else {
      const prev = sorted[i - 1];
      const cd = parseDatum(curr.Datum), pd = parseDatum(prev.Datum);
      const sameDay = cd.getDate() === pd.getDate() && cd.getMonth() === pd.getMonth() && cd.getFullYear() === pd.getFullYear();
      const cur = parseFloat(curr.SrazkyDen) || 0;
      const pre = parseFloat(prev.SrazkyDen) || 0;
      obj.calculatedRain = sameDay ? Math.max(0, cur - pre) : cur;
    }
    return obj;
  });
  const latest = records[records.length - 1] || {};
  const prev = records[records.length - 2] || null;
  const first = records[0] || {};
  // Locality from metadata or first row
  const locality = (meta && meta.LokalitaStanice) || first.LokalitaStanice || first.Lokalita || 'Meteostanice';

  // Webkamera — two shapes from API:
  //   { IdWebcam: 681,  UrlWebcam: null }     → station snapshot, async fetched via /app/
  //   { IdWebcam: null, UrlWebcam: "<url>" }  → static Windy preview URL, /preview/ → /normal/ for modal
  // Legacy shapes (string at meta.WebKameraURL / SiteConfig.webcam) are still respected
  // as a fallback when the new shape isn't present.
  let webcam = null;
  let webcamSnapshot = null;
  const wk = meta && meta.Webkamera;
  if (wk && typeof wk === 'object' && wk.IdWebcam) {
    webcam = { idWebcam: wk.IdWebcam, urlWebcam: wk.UrlWebcam || null };
    // Snapshot URL/detail will be filled in by loadStation() after async fetch.
  } else if (wk && typeof wk === 'object' && wk.UrlWebcam) {
    const ts = Date.now();
    const bust = (u) => u + (u.includes('?') ? '&' : '?') + '_=' + ts;
    const previewUrl = wk.UrlWebcam;
    const detailUrl = previewUrl.replace('/preview/', '/normal/');
    webcam = { idWebcam: null, urlWebcam: previewUrl };
    webcamSnapshot = { url: bust(previewUrl), detailUrl: bust(detailUrl), snimek_cas: null };
  }

  // Legacy string fallback — older API/config shapes
  const legacyWebcamUrl = (typeof wk === 'string' ? wk : null)
    || (meta && (meta.WebKameraURL || meta.WebkameraURL || meta.KameraURL || meta.Webcam || meta.WebCam))
    || (typeof window !== 'undefined' && window.SiteConfig?.webcam) || null;

  // Rolling 24h min/max — uses every record the API returns in the last
  // 24 hours, not "since midnight". The previous behaviour reset at 00:00
  // local time, which made the displayed extremes useless right after
  // midnight (when "today" had only a single sample).
  const now = Date.now();
  const cutoff24h = now - 24 * 3600 * 1000;
  const last24hRecords = records.filter(r => {
    const t = r.Datum ? parseDatum(r.Datum).getTime() : 0;
    return t >= cutoff24h;
  });
  const dayMinMax = (key) => {
    const vals = last24hRecords.map(r => r[key]).filter(v => v != null && !isNaN(v));
    if (!vals.length) return { min: null, max: null };
    return { min: Math.min(...vals), max: Math.max(...vals) };
  };

  // 24h history per key — for mini-sparklines (already 24h, same window)
  const measurements = records.map(r => ({
    time: r.Datum ? parseDatum(r.Datum) : null,
    temp: r.TeplotaVnejsi, humidity: r.VlhkostVnejsi, pressure: r.TlakRel,
    wind: r.Vitr, gust: r.VitrNarazy, rain: r.calculatedRain, dayRain: r.SrazkyDen,
    uv: r.UVindex, solar: r.SlunZareni,
  })).filter(m => m.time && m.time.getTime() >= cutoff24h);

  return {
    raw: records,
    measurements,
    config: { webcam: legacyWebcamUrl },
    webcam,
    webcamSnapshot,
    latest,
    prev,
    locality,
    timestamp: latest.Datum ? parseDatum(latest.Datum) : null,
    dayMinMax,
    doplCidlaJson: meta ? meta.DoplCidlaJson : null,
    fields: { ...FIELDS },
    isFresh(maxAgeMs = 10 * 60 * 1000) {
      if (!this.timestamp) return false;
      return (Date.now() - this.timestamp.getTime()) <= maxAgeMs;
    }
  };
}

// Fetches the latest webcam snapshot for stations identified by IdWebcam.
// Endpoint shape: /app/?webcam_id=<id>&posledni_snimek=ano  → { snimek_cas, url };
// &noc=ano also after sunset (without it the API keeps the last DAY picture) —
// the „Noční snímky webkamery“ setting, shared with the station page
// (pm_tweaks_v1.webcamNoc, default from window.TWEAK_DEFAULTS).
// Same server as the page (ext.pocasimeteo.cz, kamerypocasi.cz, www…), so a
// relative URL — the absolute ext.pocasimeteo.cz one failed on CORS elsewhere.
const WEBCAM_BASE = '/app/';
async function fetchWebcamSnapshot(idWebcam) {
  if (!idWebcam) return null;
  try {
    let noc = window.TWEAK_DEFAULTS?.webcamNoc;
    try { const t = JSON.parse(localStorage.getItem('pm_tweaks_v1') || 'null'); if (t && typeof t.webcamNoc === 'boolean') noc = t.webcamNoc; } catch (e) {}
    const url = `${WEBCAM_BASE}?webcam_id=${encodeURIComponent(idWebcam)}&posledni_snimek=ano${noc ? '&noc=ano' : ''}&_=${Date.now()}`;
    const resp = await fetch(url, { method: 'GET', cache: 'no-store' });
    if (!resp.ok) throw new Error('HTTP ' + resp.status);
    const snap = await resp.json();
    if (snap && snap.url) return { url: snap.url, detailUrl: snap.url, snimek_cas: snap.snimek_cas || null };
  } catch (err) {
    console.warn('PŘEDPOVĚĎ: webcam fetch failed —', err.message);
  }
  return null;
}

// Applies user-customized sensor names from DoplCidlaJson. Falls back to
// inserting a fresh field entry when the channel (Te4, Vl3, …) isn't part of
// the static FIELDS dict so IndoorWidget can still label it correctly.
function applyDoplCidlaLabels(station) {
  const meta = station.doplCidlaJson;
  if (!Array.isArray(meta)) return;
  meta.forEach((item) => {
    const cidlo = item.cidlo, kanal = item.kanal, nazev = item.nazev;
    if (!nazev) return;
    let key = null;
    let defaults = null;
    if (cidlo === 'Teplota' && kanal && kanal.startsWith('CH-')) {
      key = 'Te' + kanal.split('-')[1];
      defaults = { unit: '°C', precision: 1, color: '#ef4444' };
    } else if (cidlo === 'Vlhkost' && kanal && kanal.startsWith('CH-')) {
      key = 'Vl' + kanal.split('-')[1];
      defaults = { unit: '%', precision: 0, color: '#22c55e' };
    } else if (cidlo === 'Teplota Voda') {
      key = 'TeVody';
      defaults = { unit: '°C', precision: 1, color: '#0ea5e9' };
    } else if (cidlo === 'Co2') {
      key = 'Co2';
      defaults = { unit: 'ppm', precision: 0, color: '#ef4444' };
    }
    if (!key) return;
    station.fields[key] = station.fields[key]
      ? { ...station.fields[key], label: nazev }
      : { ...defaults, label: nazev };
  });
}

async function loadStation() {
  const result = await fetchStation();
  if (!result.ok) return { ok: false, reason: result.reason };
  const station = buildStation(result.data);
  applyDoplCidlaLabels(station);
  // Resolve the station-snapshot URL up front so the hero renders with the
  // thumbnail on first paint instead of flashing in later.
  if (station.webcam && station.webcam.idWebcam && !station.webcamSnapshot) {
    const snap = await fetchWebcamSnapshot(station.webcam.idWebcam);
    if (snap) station.webcamSnapshot = snap;
  }
  return { ok: true, station };
}

window.StationData = {
  loadStation,
  parseDatum,
  ready: READY,
  get hasApiKey() { return !!(LEGACY_KEY || SITE_ID); },
  get siteId() { return SITE_ID; },
  get apiUrl() { return API_URL; },
  FIELDS,
};
