const GATEWAY_RETRY_STATUS = new Set([502, 503, 504]);
const MEDIA_FETCH_MAX_ATTEMPTS = 3;
const MEDIA_SLOW_LOAD_WARN_MS = 8_000;
const MEDIA_STREAM_PATH = '/media/stream/';
const SAME_ORIGIN_API_PREFIX = '/idp-api';

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

function warnSlowMediaLoad(label: string, startedAt: number, error: unknown) {
  if (Date.now() - startedAt < MEDIA_SLOW_LOAD_WARN_MS) return;
  const detail = error instanceof Error ? error.message : 'неизвестная ошибка';
  console.warn(`[media] ${label}: не удалось загрузить за ${Math.round((Date.now() - startedAt) / 1000)} с (${detail})`);
}

export function resolveBrowserMediaUrl(accessUrl: string): string {
  if (typeof window === 'undefined') return accessUrl;

  try {
    const url = new URL(accessUrl, window.location.origin);
    const streamIndex = url.pathname.indexOf(MEDIA_STREAM_PATH);
    if (streamIndex === -1) {
      return accessUrl;
    }

    const streamPath = url.pathname.slice(streamIndex);
    return `${window.location.origin}${SAME_ORIGIN_API_PREFIX}${streamPath}${url.search}`;
  } catch {
    return accessUrl;
  }
}

export async function fetchMediaBlobWithRetry(
  accessUrl: string,
  token: string,
  options?: { mimeType?: string; label?: string }
): Promise<Blob> {
  const startedAt = Date.now();
  const label = options?.label ?? 'изображение';
  const mediaUrl = resolveBrowserMediaUrl(accessUrl);
  let lastError: unknown;

  for (let attempt = 0; attempt < MEDIA_FETCH_MAX_ATTEMPTS; attempt += 1) {
    try {
      const response = await fetch(mediaUrl, {
        headers: { Authorization: `Bearer ${token}` },
        cache: 'no-store'
      });

      if (GATEWAY_RETRY_STATUS.has(response.status) && attempt + 1 < MEDIA_FETCH_MAX_ATTEMPTS) {
        await sleep(350 * (attempt + 1));
        continue;
      }

      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }

      const raw = await response.blob();
      const mimeType = options?.mimeType;
      if (mimeType && (!raw.type || raw.type === 'application/octet-stream')) {
        return new Blob([await raw.arrayBuffer()], { type: mimeType });
      }
      return raw;
    } catch (error) {
      lastError = error;
      if (attempt + 1 < MEDIA_FETCH_MAX_ATTEMPTS) {
        await sleep(350 * (attempt + 1));
      }
    }
  }

  warnSlowMediaLoad(label, startedAt, lastError);
  throw lastError instanceof Error ? lastError : new Error('Не удалось загрузить файл');
}
