// Solkart — live forecast from SunPoint API
// Replaces sim.jsx. Fetches real PV production forecasts per fylke.
// Exports window.SOLKART_SIM with the same interface as the old simulation.

(function () {
  const API_BASE = "https://sunpoint-forecast-api.karriere.workers.dev";
  const REFRESH_INTERVAL_MS = 15 * 60 * 1000; // 15 minutes

  // Representative centroid coordinates (lat, lon) for each fylke
  const FYLKE_COORDS = {
    agder:     { lat: 58.4,  lon: 7.8  },
    akershus:  { lat: 59.9,  lon: 11.2 },
    buskerud:  { lat: 60.0,  lon: 9.5  },
    finnmark:  { lat: 70.5,  lon: 25.5 },
    innlandet: { lat: 61.3,  lon: 10.5 },
    more:      { lat: 62.7,  lon: 7.0  },
    nordland:  { lat: 67.0,  lon: 15.0 },
    oslo:      { lat: 59.91, lon: 10.75 },
    ostfold:   { lat: 59.3,  lon: 11.3 },
    rogaland:  { lat: 58.9,  lon: 5.7  },
    telemark:  { lat: 59.2,  lon: 8.5  },
    troms:     { lat: 69.5,  lon: 19.0 },
    trondelag: { lat: 63.8,  lon: 11.5 },
    vestfold:  { lat: 59.3,  lon: 10.2 },
    vestland:  { lat: 60.8,  lon: 6.0  },
  };

  // Cached forecast data: { [fylkeId]: { entries: [...], fetchedAt: Date } }
  // Each entry: { timestamp: "ISO string", total_W, total_kWh }
  let forecastCache = {};
  let _isLoading = true;
  let _error = null;
  let _refreshTimer = null;

  // Build the POST body for a fylke's forecast request
  function buildRequestBody(fylkeId, daily) {
    const coords = FYLKE_COORDS[fylkeId];
    const nve = window.NVE_CAPACITY.fylker[fylkeId];
    if (!coords || !nve) return null;

    const hushKWp = nve.husholdning_kW;  // already in kWp
    const andreKWp = nve.andre_kW;        // already in kWp
    const arrays = [];

    if (hushKWp > 0) {
      arrays.push({
        name: "Husholdning",
        kwp: hushKWp,
        tilt_deg: 25,
        azimuth_deg: 180,
      });
    }
    if (andreKWp > 0) {
      arrays.push({
        name: "Andre_E",
        kwp: andreKWp / 2,
        tilt_deg: 10,
        azimuth_deg: 90,
      });
      arrays.push({
        name: "Andre_W",
        kwp: andreKWp / 2,
        tilt_deg: 10,
        azimuth_deg: 270,
      });
    }

    if (arrays.length === 0) return null;

    const body = { latitude: coords.lat, longitude: coords.lon, performance_ratio: 0.9, arrays };
    if (daily) body.daily = true;
    return body;
  }

  // Merge timestamps from ghi_timeseries into pv_timeseries entries
  function mergeTimestamps(ghiTs, pvTs) {
    const entries = [];
    for (let i = 0; i < pvTs.length; i++) {
      const pv = pvTs[i];
      const ghi = ghiTs[i];
      entries.push({
        timestamp: ghi ? ghi.timestamp : null,
        hour: pv.hour,
        total_W: pv.total_W || 0,
        total_kWh: pv.total_kWh || 0,
      });
    }
    return entries;
  }

  // Fetch forecast for a single fylke
  // Tries daily=true first, falls back to latest cycle
  async function fetchFylkeForecast(fylkeId) {
    // Try daily first
    let body = buildRequestBody(fylkeId, true);
    if (!body) return { entries: [], fetchedAt: new Date() };

    let resp = await fetch(API_BASE + "/api/forecast", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(body),
    });

    // If daily fails (503 / error), retry without daily
    if (!resp.ok) {
      body = buildRequestBody(fylkeId, false);
      resp = await fetch(API_BASE + "/api/forecast", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
    }

    if (!resp.ok) {
      throw new Error(`API error for ${fylkeId}: ${resp.status}`);
    }

    const data = await resp.json();
    const ghiTs = data.ghi_timeseries || [];
    const pvTs = data.pv_timeseries || [];
    const entries = mergeTimestamps(ghiTs, pvTs);

    return { entries, fetchedAt: new Date() };
  }

  // Fetch all fylker in parallel
  async function fetchAllForecasts() {
    const fylkeIds = Object.keys(FYLKE_COORDS);
    const results = await Promise.allSettled(
      fylkeIds.map((id) => fetchFylkeForecast(id).then((r) => ({ id, ...r })))
    );

    let anySuccess = false;
    for (const result of results) {
      if (result.status === "fulfilled") {
        const { id, entries, fetchedAt } = result.value;
        forecastCache[id] = { entries, fetchedAt };
        anySuccess = true;
      } else {
        console.warn("Forecast fetch failed:", result.reason);
      }
    }

    if (!anySuccess && Object.keys(forecastCache).length === 0) {
      _error = "Kunne ikke hente prognosedata";
    } else {
      _error = null;
    }
  }

  // Find the entry matching a given Date (by UTC hour of the same day)
  function findEntry(entries, date) {
    if (!entries || entries.length === 0) return null;
    const targetMs = date.getTime();

    for (const entry of entries) {
      if (!entry.timestamp) continue;
      const entryMs = new Date(entry.timestamp).getTime();
      // Entry covers [timestamp, timestamp+1h)
      if (targetMs >= entryMs && targetMs < entryMs + 3600000) {
        return entry;
      }
    }
    return null;
  }

  // Get local midnight in UTC (CEST = UTC+2)
  function localMidnightUTC(date) {
    // Midnight CEST = 22:00 UTC previous day
    const d = new Date(date);
    // Local date in CEST
    const localHours = d.getUTCHours() + 2;
    const localDate = new Date(Date.UTC(
      d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()
    ));
    if (localHours >= 24) {
      localDate.setUTCDate(localDate.getUTCDate() + 1);
    }
    // Midnight of that local date in UTC
    return new Date(localDate.getTime() - 2 * 3600000);
  }

  // ── Public API ────────────────────────────────────────────────

  // snapshot(date) → { perFylke: { id: kW }, totalKW, isNight }
  function snapshot(date) {
    const perFylke = {};
    let total = 0;
    let anyDaylight = false;

    for (const fylkeId of Object.keys(FYLKE_COORDS)) {
      const cached = forecastCache[fylkeId];
      if (!cached) {
        perFylke[fylkeId] = 0;
        continue;
      }
      const entry = findEntry(cached.entries, date);
      if (!entry || !entry.total_W) {
        perFylke[fylkeId] = 0;
        continue;
      }
      const kW = entry.total_W / 1000;
      perFylke[fylkeId] = kW;
      total += kW;
      if (kW > 0) anyDaylight = true;
    }

    return { perFylke, totalKW: total, isNight: !anyDaylight };
  }

  // cumulativeTodayKWh(date) → number
  // Sums completed hours from midnight CEST + prorates current hour
  function cumulativeTodayKWh(date) {
    const midnight = localMidnightUTC(date);
    if (date <= midnight) return 0;

    let totalKWh = 0;
    const nowMs = date.getTime();
    const midnightMs = midnight.getTime();

    for (const fylkeId of Object.keys(FYLKE_COORDS)) {
      const cached = forecastCache[fylkeId];
      if (!cached) continue;

      for (const entry of cached.entries) {
        if (!entry.timestamp) continue;
        const entryMs = new Date(entry.timestamp).getTime();
        const entryEndMs = entryMs + 3600000;

        // Skip entries outside today's range
        if (entryEndMs <= midnightMs) continue;
        if (entryMs >= nowMs) continue;

        const kWh = entry.total_kWh || 0;
        if (kWh === 0) continue;

        if (entryEndMs <= nowMs) {
          // Completed hour
          totalKWh += kWh;
        } else {
          // Current (partial) hour — prorate
          const elapsed = (nowMs - entryMs) / 1000;
          totalKWh += kWh * (elapsed / 3600);
        }
      }
    }

    return totalKWh;
  }

  // init() → Promise
  async function init() {
    _isLoading = true;
    _error = null;
    try {
      await fetchAllForecasts();
    } catch (e) {
      console.error("Forecast init failed:", e);
      _error = "Kunne ikke hente prognosedata";
    }
    _isLoading = false;

    if (_refreshTimer) clearInterval(_refreshTimer);
    _refreshTimer = setInterval(async () => {
      try {
        await fetchAllForecasts();
      } catch (e) {
        console.warn("Forecast refresh failed:", e);
      }
    }, REFRESH_INTERVAL_MS);
  }

  window.SOLKART_SIM = {
    snapshot,
    cumulativeTodayKWh,
    init,
    get isLoading() { return _isLoading; },
    get error() { return _error; },
    FYLKE_COORDS,
  };
})();
