/**
 * Shared ExpoImage prefetch helpers for chat attachments.
 *
 * Both the `ConversationMediaPrefetcher` (top-N conversations warm-up) and
 * the `OrchestratorUpdaterService.processMessage` socket-receive path
 * need to compute "what sized URLs should we warm for this attachment"
 * and then send the resulting URL list to ExpoImage with the same auth
 * headers. Centralising both pieces here keeps the renderer-vs-prefetch
 * sizing in lockstep (one source of truth for tile widths) and avoids
 * duplicating the auth-header + cachePolicy plumbing.
 *
 * No FileHandler involvement — images go through ExpoImage's cache and
 * video posters are the only video bytes we prefetch (the video itself
 * is streamed + cached by expo-video at playback time).
 */
import {Image as ExpoImage} from 'expo-image';
import {getApiHeaders} from '@config/api';
import {tokenManager} from '@config/tokens';
import {Asset, VideoAsset} from '@modules/chats/interfaces';

import {getChatImage} from './getChatImage';
import {
  MESSAGE_VIDEO_THUMBNAIL_WIDTH,
  getMessageImageTileSizing,
} from './imageSizes';
import {
  isPictureAsset,
  isVideoAsset,
  mapAssetToUrl,
} from './mappers/assetMapper';

/**
 * Low-level: feed `ExpoImage.prefetch` with a list of FULLY-SIZED URLs
 * and the auth headers it needs to fetch them. Best-effort; failures
 * fall back to lazy load at render time.
 */
export const prefetchImageUrls = async (
  urls: readonly string[],
): Promise<void> => {
  if (urls.length === 0) return;
  try {
    const token = await tokenManager.getValidAccessToken();
    if (!token) return;
    await ExpoImage.prefetch(urls as string[], {
      cachePolicy: 'disk',
      headers: getApiHeaders(token),
    });
  } catch {
    /* best-effort — UI lazy-loads anything that didn't warm */
  }
};

/**
 * Sized URLs ExpoImage should warm for a single asset to match what
 * the renderer will request. Empty array for non-image, non-video
 * assets (audio + documents go through FileHandler).
 *
 * `attachmentCount` drives the tile sizing rules — 1 attachment uses
 * the big-single placeholder + upgrade pair, 2+ use the grid SMALL.
 */
export const getPrefetchUrlsForAsset = (
  asset: Asset,
  attachmentCount: number,
): string[] => {
  if (isVideoAsset(asset)) {
    const thumb = (asset as VideoAsset).thumbnail_url;
    return thumb
      ? [getChatImage.addSize(thumb, MESSAGE_VIDEO_THUMBNAIL_WIDTH)]
      : [];
  }
  if (!isPictureAsset(asset)) return [];
  const url = mapAssetToUrl(asset);
  if (!url) return [];
  const {prefetchWidths} = getMessageImageTileSizing(attachmentCount);
  return prefetchWidths.map(w => getChatImage.addSize(url, w));
};

/**
 * High-level: warm a single message's image attachments + video poster
 * thumbnails. Used by the socket-receive path so a freshly-arrived
 * message has its bytes ready by the time the chat window scrolls to
 * the new row.
 *
 * Fire-and-forget — `processMessage` shouldn't block on a CDN
 * round-trip before persisting the message.
 */
export const prefetchMessageImages = (attachments: Asset[]): void => {
  if (!attachments?.length) return;
  const count = attachments.length;
  const urls: string[] = [];
  for (const asset of attachments) {
    const assetUrls = getPrefetchUrlsForAsset(asset, count);
    for (const u of assetUrls) urls.push(u);
  }
  void prefetchImageUrls(urls);
};
