"use client";

import { useEffect, useState } from "react";

const cache = new Map<string, unknown>();
const inflight = new Map<string, Promise<unknown>>();

export async function apiGet<T>(path: string): Promise<T> {
  if (cache.has(path)) return cache.get(path) as T;
  let p = inflight.get(path);
  if (!p) {
    p = fetch(path)
      .then(async (r) => {
        if (!r.ok) throw new Error(`${r.status} ${r.statusText}`);
        const json = await r.json();
        cache.set(path, json);
        inflight.delete(path);
        return json;
      })
      .catch((e) => {
        inflight.delete(path);
        throw e;
      });
    inflight.set(path, p);
  }
  return (await p) as T;
}

export async function apiPost<T>(path: string, body: unknown): Promise<T> {
  const r = await fetch(path, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body ?? {}),
  });
  if (!r.ok) {
    let msg = `${r.status}`;
    try { const j = await r.json(); msg = j.error ?? msg; } catch {}
    throw new Error(msg);
  }
  return r.json();
}

export function useApi<T>(path: string | null) {
  const [data, setData] = useState<T | null>(() => path ? (cache.get(path) as T) ?? null : null);
  const [loading, setLoading] = useState<boolean>(() => path ? !cache.has(path) : false);
  const [error, setError] = useState<string | null>(null);
  const [trackedPath, setTrackedPath] = useState<string | null>(path);

  // Reset derived state when `path` changes (render-phase adjustment, allowed by React).
  if (trackedPath !== path) {
    setTrackedPath(path);
    setData(path ? (cache.get(path) as T) ?? null : null);
    setLoading(path ? !cache.has(path) : false);
    setError(null);
  }

  useEffect(() => {
    if (!path) return;
    if (cache.has(path)) return;
    let active = true;
    apiGet<T>(path)
      .then((d) => { if (active) { setData(d); setLoading(false); setError(null); } })
      .catch((e) => { if (active) { setError(String(e?.message ?? e)); setLoading(false); } });
    return () => { active = false; };
  }, [path]);

  return { data, loading, error };
}

export function invalidate(pathPrefix: string) {
  for (const key of cache.keys()) {
    if (key.startsWith(pathPrefix)) cache.delete(key);
  }
}
