type ReverseResult = {
  label: string;
  fetchedAt: number;
};

const reverseCache = new Map<string, ReverseResult>();
const CACHE_TTL_MS = 1000 * 60 * 30;
const CACHE_MAX_ENTRIES = 2_000;
const REVERSE_FETCH_TIMEOUT_MS = 8_000;

const pruneReverseCache = (now: number) => {
  for (const [key, value] of reverseCache) {
    if (now - value.fetchedAt >= CACHE_TTL_MS) {
      reverseCache.delete(key);
    }
  }

  while (reverseCache.size >= CACHE_MAX_ENTRIES) {
    const oldestKey = reverseCache.keys().next().value;
    if (!oldestKey) {
      break;
    }
    reverseCache.delete(oldestKey);
  }
};

const buildLocationLabel = (address: Record<string, string | undefined>) => {
  const street = address.road
    ?? address.pedestrian
    ?? address.footway
    ?? address.cycleway
    ?? address.path
    ?? address.residential
    ?? address.neighbourhood
    ?? address.suburb
    ?? address.quarter;

  const locality = address.house_number
    ? `${street ?? "Brak ulicy"} ${address.house_number}`
    : street
      ?? address.neighbourhood
      ?? address.suburb
      ?? address.city_district
      ?? address.village
      ?? address.town
      ?? address.city
      ?? "Brak adresu";

  const area = address.suburb
    ?? address.city_district
    ?? address.village
    ?? address.town
    ?? address.city
    ?? address.county;

  if (!area || locality.includes(area)) {
    return locality;
  }

  return `${locality}, ${area}`;
};

export const reverseGeocodeLabel = async (lat: number, lon: number) => {
  if (!Number.isFinite(lat) || !Number.isFinite(lon)) {
    return "Brak adresu";
  }

  const now = Date.now();
  const cacheKey = `${lat.toFixed(5)}:${lon.toFixed(5)}`;
  const cached = reverseCache.get(cacheKey);
  if (cached && now - cached.fetchedAt < CACHE_TTL_MS) {
    return cached.label;
  }
  pruneReverseCache(now);

  const url = new URL("https://nominatim.openstreetmap.org/reverse");
  url.searchParams.set("format", "jsonv2");
  url.searchParams.set("lat", String(lat));
  url.searchParams.set("lon", String(lon));
  url.searchParams.set("zoom", "18");
  url.searchParams.set("addressdetails", "1");

  const reverseResponse = await fetch(url, {
    signal: AbortSignal.timeout(REVERSE_FETCH_TIMEOUT_MS),
    headers: {
      "User-Agent": "VisauTracker/1.0 (track.visau.duckdns.org)",
      Accept: "application/json",
    },
  });

  if (!reverseResponse.ok) {
    throw new Error("Reverse geocoding failed");
  }

  const payload = (await reverseResponse.json()) as {
    display_name?: string;
    address?: Record<string, string | undefined>;
  };

  const label = payload.address ? buildLocationLabel(payload.address) : payload.display_name ?? "Brak adresu";
  reverseCache.set(cacheKey, { label, fetchedAt: now });
  return label;
};
