import { MatrixClient } from 'matrix-js-sdk';
import { downloadMedia, mxcUrlToHttp } from './matrix';

// [P5-15 v2] Shared media helpers for the soundboard. Clip storage/metadata now
// lives in the soundboard pack plugin (plugins/soundboard); this module only
// handles resolving an mxc clip for playback + local preview.

export const SOUNDBOARD_NAME_MAX = 24;
/** Keep clips short: they publish to every peer and hold a track open. */
export const SOUNDBOARD_MAX_CLIP_BYTES = 1024 * 1024; // 1 MB
export const SOUNDBOARD_MAX_CLIPS = 40;
export const SOUNDBOARD_ACCEPT = 'audio/mpeg,audio/ogg,audio/wav,audio/webm,audio/mp4,audio/aac';

// Cache resolved object URLs per mxc so re-triggering a clip doesn't re-download
// it. Object URLs live for the page session; the set is tiny (<= MAX_CLIPS).
const objectUrlCache = new Map<string, string>();

/**
 * Resolve an mxc clip to a `blob:` object URL the Element Call widget can fetch
 * without credentials. Authenticated media (MSC3916) can't be fetched from the
 * widget's realm, so the host downloads it (auth handled by the service worker)
 * and hands the widget a same-session blob URL instead.
 */
export const resolveClipObjectUrl = async (mx: MatrixClient, mxcUrl: string): Promise<string> => {
  const cached = objectUrlCache.get(mxcUrl);
  if (cached) return cached;

  const httpUrl = mxcUrlToHttp(mx, mxcUrl, true);
  if (!httpUrl) throw new Error('invalid mxc url');
  const blob = await downloadMedia(httpUrl);
  const objectUrl = URL.createObjectURL(blob);
  objectUrlCache.set(mxcUrl, objectUrl);
  return objectUrl;
};

/**
 * Play a resolved clip locally so the person who pressed it gets immediate
 * feedback — LiveKit doesn't loop a participant's own published track back to
 * them, so without this the presser would hear nothing. `volume` is 0–1.
 * Returns the audio element so callers can track when it ends (or undefined if
 * playback couldn't start).
 */
export const playClipLocally = (
  objectUrl: string,
  volume: number,
): HTMLAudioElement | undefined => {
  try {
    const audio = new Audio(objectUrl);
    audio.volume = Math.max(0, Math.min(1, volume));
    audio.play().catch(() => undefined);
    return audio;
  } catch {
    return undefined;
  }
};

/** Read an audio file's duration in milliseconds from its metadata (no playback). */
export const getAudioDurationMs = (file: Blob): Promise<number | undefined> =>
  new Promise((resolve) => {
    const url = URL.createObjectURL(file);
    const audio = new Audio();
    audio.preload = 'metadata';
    const done = (ms: number | undefined) => {
      URL.revokeObjectURL(url);
      resolve(ms);
    };
    audio.addEventListener('loadedmetadata', () =>
      done(Number.isFinite(audio.duration) ? Math.round(audio.duration * 1000) : undefined),
    );
    audio.addEventListener('error', () => done(undefined));
    audio.src = url;
  });
