export interface AvatarCache {
  match(key: string): Promise<Response | undefined>;
  put(key: string, response: Response): Promise<void>;
}

export function createAvatarLoader(
  cache: () => Promise<AvatarCache>,
  load: (mxc: string) => Promise<Response>
): (key: string, mxc: string) => Promise<Response> {
  const pending = new Map<string, Promise<Response>>();

  return async (key, mxc) => {
    const active = pending.get(mxc);
    if (active) return (await active).clone();

    const request = (async () => {
      let store: AvatarCache | null = null;
      try {
        store = await cache();
        const cached = await store.match(key);
        if (cached) return cached;
      } catch {
        store = null;
      }

      const response = await load(mxc);
      if (response.ok && store) {
        try {
          await store.put(key, response.clone());
        } catch {
          return response;
        }
      }
      return response;
    })();
    pending.set(mxc, request);
    try {
      return (await request).clone();
    } finally {
      if (pending.get(mxc) === request) pending.delete(mxc);
    }
  };
}
