// İstemci tarafı GET istekleri için modül seviyesi in-flight dedup + TTL
// cache. SWR bağımlılığı yerine minimal el yapımı katman: /api/facetler beş
// farklı bileşenden aynı parametrelerle çağrılıyor, katalog araması aynı
// sorguyu tekrar tekrar atıyordu.
//
// - Aynı URL uçuştayken ikinci çağrı aynı promise'i bekler (dedup).
// - Başarılı yanıt ttlMs boyunca bellekte kalır; ttlMs: 0 = yalnız dedup
//   (kullanıcıya özel, mutasyon sonrası tazelenmesi gereken uçlar için).
// - Paylaşılan fetch'e caller signal'ı GEÇİLMEZ: bir abonenin iptali
//   diğerlerinin isteğini düşürmesin. İptal yalnızca o abonenin bekleyişini
//   AbortError ile sonlandırır (katalog aramasının debounce/abort akışı
//   davranış değiştirmeden buna bağlanır).

type Kayit = { zaman: number; veri: unknown };

const bellek = new Map<string, Kayit>();
const ucusta = new Map<string, Promise<unknown>>();
const MAX_KAYIT = 50;
const VARSAYILAN_TTL_MS = 5 * 60_000;

export async function onbellekliJsonGetir<T>(
  url: string,
  {
    ttlMs = VARSAYILAN_TTL_MS,
    signal,
  }: { ttlMs?: number; signal?: AbortSignal } = {},
): Promise<T> {
  const kayit = bellek.get(url);
  if (kayit && ttlMs > 0 && Date.now() - kayit.zaman < ttlMs) {
    return kayit.veri as T;
  }

  let soz = ucusta.get(url);
  if (!soz) {
    soz = fetch(url)
      .then(async (res) => {
        if (!res.ok) throw new Error(`istek-cache: ${res.status} (${url})`);
        const veri: unknown = await res.json();
        if (ttlMs > 0) {
          bellek.set(url, { zaman: Date.now(), veri });
          // Basit LRU kırpması: en eski kayıt düşer (insertion order)
          if (bellek.size > MAX_KAYIT) {
            const enEski = bellek.keys().next().value;
            if (enEski !== undefined) bellek.delete(enEski);
          }
        }
        return veri;
      })
      .finally(() => ucusta.delete(url));
    ucusta.set(url, soz);
  }

  if (!signal) return soz as Promise<T>;
  return new Promise<T>((cozul, reddet) => {
    const iptalEt = () => reddet(new DOMException("Aborted", "AbortError"));
    if (signal.aborted) return iptalEt();
    signal.addEventListener("abort", iptalEt, { once: true });
    (soz as Promise<T>).then(cozul, reddet).finally(() => {
      signal.removeEventListener("abort", iptalEt);
    });
  });
}
