/**
 * Avatar loading that survives the webview's mixed-content/self-signed-cert
 * restrictions.
 *
 * A plain <img src="https://host/api/avatars/…"> is fetched BY THE WEBVIEW,
 * and WKWebView (macOS) / WebView2 (Windows) treat the tauri:// origin as
 * secure — they silently block plain-http image loads from any non-localhost
 * server (mixed content), and separately can't complete TLS against the
 * server's self-signed, fingerprint-pinned certificate at all. So on desktop,
 * remote avatars download through apiFetch's pinned channel as base64 and
 * render as data: URLs; on web (no pinning, no mixed-content wall to climb)
 * they still ride apiFetch (== window.fetch there) as blob object URLs.
 *
 * Avatar URLs are mtime-versioned by the server (?v=…), so a given URL's
 * bytes never change — successful downloads cache forever.
 */
import { apiFetch, isTauri } from '../platform';

// Only http(s) sources need the apiFetch detour; data:/blob:/bundled-asset
// sources load fine directly in the webview.
export function isRemoteAvatarUrl(src) {
  return /^https?:\/\//i.test(src ?? '');
}

// Decode a data: URL into raw bytes in pure JS — `fetch(dataUrl)` would go
// through the webview's fetch, which the avatar upload must avoid.
export function dataUrlToBytes(dataUrl) {
  const comma = typeof dataUrl === 'string' ? dataUrl.indexOf(',') : -1;
  if (!/^data:/i.test(dataUrl ?? '') || comma < 0) {
    throw new Error('Not a data URL.');
  }
  const meta = dataUrl.slice(0, comma);
  const payload = dataUrl.slice(comma + 1);
  if (/;base64$/i.test(meta)) {
    const binary = atob(payload);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
    return bytes;
  }
  return new TextEncoder().encode(decodeURIComponent(payload));
}

const resolved = new Map(); // url -> object URL (successful downloads only)
const inflight = new Map(); // url -> Promise<string|null>

// Synchronous cache lookup. undefined = not downloaded (yet).
export function peekAvatarUrl(url) {
  return resolved.get(url);
}

// Download an avatar through apiFetch and cache it as a data: URL (desktop)
// or object URL (web). Cached forever under `url` (already ?v=-versioned by
// the server) — resolves to null on failure, and a later mount retries
// (failures are not cached).
export function resolveAvatarUrl(url) {
  const hit = resolved.get(url);
  if (hit !== undefined) return Promise.resolve(hit);
  let pending = inflight.get(url);
  if (!pending) {
    pending = (async () => {
      try {
        if (isTauri) {
          // The webview can't complete pinned TLS itself, and a plain
          // (non-binary) apiFetch would UTF-8-decode the PNG bytes and
          // corrupt them — binary:true round-trips them as base64 instead.
          const response = await apiFetch(url, { binary: true });
          if (!response.ok) return null;
          const dataUrl = `data:image/png;base64,${await response.text()}`;
          resolved.set(url, dataUrl);
          return dataUrl;
        }
        const response = await apiFetch(url);
        if (!response.ok) return null;
        const objectUrl = URL.createObjectURL(await response.blob());
        resolved.set(url, objectUrl);
        return objectUrl;
      } catch {
        return null;
      } finally {
        inflight.delete(url);
      }
    })();
    inflight.set(url, pending);
  }
  return pending;
}
