import { getCurrentAccessToken } from './auth';

const MAX_ENTRIES = 250;

/** Shared blob URLs for authenticated Matrix media (avatars, thumbs, etc.). */
const blobCache = new Map<string, string>();
const inflight = new Map<string, Promise<string>>();

export const isAuthenticatedMediaUrl = (url: string): boolean =>
  url.includes('/_matrix/client/v1/media/download') ||
  url.includes('/_matrix/client/v1/media/thumbnail') ||
  (url.includes('/_matrix/media/') &&
    (url.includes('/download/') || url.includes('/thumbnail/')));

export const getCachedAuthenticatedMediaUrl = (src: string | undefined): string | undefined => {
  if (!src) return undefined;
  return blobCache.get(src);
};

const touch = (src: string, blobUrl: string) => {
  // Re-insert for LRU ordering (Map preserves insertion order).
  if (blobCache.has(src)) blobCache.delete(src);
  blobCache.set(src, blobUrl);

  while (blobCache.size > MAX_ENTRIES) {
    const oldest = blobCache.keys().next().value as string | undefined;
    if (!oldest) break;
    const oldUrl = blobCache.get(oldest);
    blobCache.delete(oldest);
    if (oldUrl) URL.revokeObjectURL(oldUrl);
  }
};

const fetchBlobUrl = async (src: string): Promise<string> => {
  const accessToken = getCurrentAccessToken();
  let response = await fetch(src, {
    method: 'GET',
    headers: accessToken ? { Authorization: `Bearer ${accessToken}` } : undefined,
  });

  if (!response.ok && response.status === 401 && accessToken) {
    response = await fetch(src, { method: 'GET' });
  }

  if (!response.ok) {
    throw new Error(`Failed to fetch authenticated media: ${response.status}`);
  }

  const blob = await response.blob();
  return URL.createObjectURL(blob);
};

/**
 * Returns a stable blob: URL for authenticated media, reusing cache across mounts.
 * Non-auth URLs are returned as-is.
 */
export const resolveAuthenticatedMediaUrl = async (
  src: string,
  useAuthentication: boolean
): Promise<string> => {
  if (!useAuthentication || !isAuthenticatedMediaUrl(src)) {
    return src;
  }

  const cached = blobCache.get(src);
  if (cached) {
    touch(src, cached);
    return cached;
  }

  let pending = inflight.get(src);
  if (!pending) {
    pending = fetchBlobUrl(src)
      .then((blobUrl) => {
        touch(src, blobUrl);
        inflight.delete(src);
        return blobUrl;
      })
      .catch((err) => {
        inflight.delete(src);
        throw err;
      });
    inflight.set(src, pending);
  }

  return pending;
};
