/**
 * Shared geo + weather store + helpers for the bottom info bars.
 *
 *  - One source of truth for /api/public/geo and /api/public/weather
 *    so we don't double-fetch from BottomLeftBar AND BottomRightBar.
 *  - Caches in localStorage with TTLs (geo: 6h, weather: 30min).
 *  - Exposes a tiny pub/sub so each bar re-renders when data lands.
 */

export type GeoData = {
  city?: string;
  country?: string;
  countryCode?: string;
  lat?: number;
  lon?: number;
};

export type WeatherData = {
  tempC?: number;
  weatherCode?: number;
};

const GEO_KEY = "nadi_infobar_geo_v2";
const GEO_TTL_MS = 6 * 60 * 60 * 1000;
const WX_KEY = "nadi_infobar_weather_v1";
const WX_TTL_MS = 30 * 60 * 1000;

let geo: GeoData | null = null;
let weather: WeatherData | null = null;
let inflight: Promise<void> | null = null;
const subs = new Set<() => void>();

const apiBase = (): string => {
  // @ts-expect-error Vite inlines import.meta.env at build time
  return import.meta.env.VITE_BACKEND_URL
    // @ts-expect-error
    || import.meta.env.REACT_APP_BACKEND_URL
    // @ts-expect-error
    || (globalThis as any).REACT_APP_BACKEND_URL
    || "";
};

const notify = () => {
  for (const fn of subs) {
    try { fn(); } catch { /* ignore */ }
  }
};

export const subscribe = (fn: () => void): (() => void) => {
  subs.add(fn);
  return () => subs.delete(fn);
};

export const getGeo = (): GeoData | null => geo;
export const getWeather = (): WeatherData | null => weather;

const loadCached = () => {
  try {
    const raw = localStorage.getItem(GEO_KEY);
    if (raw) {
      const p = JSON.parse(raw) as { ts: number; data: GeoData };
      if (p?.ts && Date.now() - p.ts < GEO_TTL_MS && p.data) {
        geo = p.data;
      }
    }
  } catch { /* ignore */ }
  try {
    const raw = localStorage.getItem(WX_KEY);
    if (raw) {
      const p = JSON.parse(raw) as { ts: number; data: WeatherData };
      if (p?.ts && Date.now() - p.ts < WX_TTL_MS && p.data) {
        weather = p.data;
      }
    }
  } catch { /* ignore */ }
};

const fetchGeo = async (): Promise<GeoData | null> => {
  // Backend proxy first (works on web HTTPS and native).
  try {
    const res = await fetch(`${apiBase()}/api/public/geo`, { cache: "no-cache" });
    if (res.ok) {
      const j = await res.json();
      if (j.country || j.city) {
        return {
          city: j.city || undefined,
          country: j.country || undefined,
          countryCode: j.country_code || undefined,
          lat: typeof j.lat === "number" ? j.lat : undefined,
          lon: typeof j.lon === "number" ? j.lon : undefined,
        };
      }
    }
  } catch { /* ignore */ }
  // Native cleartext fallback (Capacitor).
  try {
    const res = await fetch(
      "http://ip-api.com/json/?fields=status,country,countryCode,city,lat,lon",
      { cache: "no-cache" },
    );
    if (res.ok) {
      const j = await res.json();
      if (j.status === "success") {
        return {
          city: j.city,
          country: j.country,
          countryCode: j.countryCode,
          lat: j.lat,
          lon: j.lon,
        };
      }
    }
  } catch { /* ignore */ }
  return null;
};

const fetchWeather = async (lat: number, lon: number): Promise<WeatherData | null> => {
  try {
    const res = await fetch(
      `${apiBase()}/api/public/weather?lat=${lat}&lon=${lon}`,
      { cache: "no-cache" },
    );
    if (res.ok) {
      const j = await res.json();
      if (typeof j.temp_c === "number") {
        return { tempC: j.temp_c, weatherCode: j.weather_code };
      }
    }
  } catch { /* ignore */ }
  // Direct fallback (Open-Meteo allows CORS).
  try {
    const res = await fetch(
      `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current=temperature_2m,weather_code`,
      { cache: "no-cache" },
    );
    if (res.ok) {
      const j = await res.json();
      const cur = j?.current;
      if (cur && typeof cur.temperature_2m === "number") {
        return { tempC: cur.temperature_2m, weatherCode: cur.weather_code };
      }
    }
  } catch { /* ignore */ }
  return null;
};

export const ensureLoaded = (): Promise<void> => {
  if (inflight) return inflight;
  loadCached();
  if (geo && weather) {
    notify();
    return Promise.resolve();
  }
  inflight = (async () => {
    if (!geo) {
      const g = await fetchGeo();
      if (g) {
        geo = g;
        try {
          localStorage.setItem(GEO_KEY, JSON.stringify({ ts: Date.now(), data: g }));
        } catch { /* ignore */ }
        notify();
      }
    }
    if (geo?.lat != null && geo?.lon != null && !weather) {
      const w = await fetchWeather(geo.lat, geo.lon);
      if (w) {
        weather = w;
        try {
          localStorage.setItem(WX_KEY, JSON.stringify({ ts: Date.now(), data: w }));
        } catch { /* ignore */ }
        notify();
      }
    }
  })().finally(() => {
    inflight = null;
  });
  return inflight;
};

/** Convert a 2-letter ISO country code into its emoji flag. */
export const flagEmoji = (cc?: string): string => {
  if (!cc || cc.length !== 2) return "";
  const A = 0x1f1e6;
  const a = "A".charCodeAt(0);
  const u = cc.toUpperCase();
  return String.fromCodePoint(A + (u.charCodeAt(0) - a)) + String.fromCodePoint(A + (u.charCodeAt(1) - a));
};

/** Map an Open-Meteo WMO weather code to an emoji + label. Compact. */
export const weatherIconFor = (code?: number): { icon: string; label: string } => {
  if (code == null) return { icon: "", label: "" };
  if (code === 0) return { icon: "☀️", label: "Clear" };
  if (code <= 2) return { icon: "🌤️", label: "Mostly clear" };
  if (code === 3) return { icon: "☁️", label: "Cloudy" };
  if (code <= 49) return { icon: "🌫️", label: "Fog" };
  if (code <= 59) return { icon: "🌦️", label: "Drizzle" };
  if (code <= 69) return { icon: "🌧️", label: "Rain" };
  if (code <= 79) return { icon: "🌨️", label: "Snow" };
  if (code <= 84) return { icon: "🌧️", label: "Showers" };
  if (code <= 99) return { icon: "⛈️", label: "Thunder" };
  return { icon: "🌡️", label: "" };
};
