/**
 * Media Helpers
 *
 * Utilities for media duration probing and formatting.
 * Used by constructor.tsx for displaying video/audio duration info.
 */

import axios from 'axios';
import { resolveAssetPlaybackUrl } from './assetUrl';
import { logger } from './logger';

/**
 * Format duration in seconds to human-readable string.
 *
 * @param durationSec - Duration in seconds (or null/undefined)
 * @returns Formatted string like "Duration: 1:30" or "Duration: unknown"
 *
 * @example
 * formatDurationNote(90); // "Duration: 1:30"
 * formatDurationNote(45); // "Duration: 45s"
 * formatDurationNote(null); // "Duration: unknown"
 */
export const formatDurationNote = (
  durationSec?: number | string | null,
): string => {
  const parsed = Number(durationSec);
  if (!Number.isFinite(parsed) || parsed <= 0) return 'Duration: unknown';

  const totalSeconds = Math.round(parsed);
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;

  if (minutes <= 0) return `Duration: ${seconds}s`;
  return `Duration: ${minutes}:${String(seconds).padStart(2, '0')}`;
};

/**
 * Read media duration from a URL by loading metadata.
 *
 * Creates a temporary video/audio element to probe the duration.
 * Times out after 12 seconds if metadata doesn't load.
 *
 * @param playbackUrl - URL to the media file
 * @param mediaType - Type of media ('video' or 'audio')
 * @returns Promise resolving to duration in seconds, or null if failed
 *
 * @example
 * const duration = await readMediaDuration('https://example.com/video.mp4', 'video');
 */
export const readMediaDuration = (
  playbackUrl: string,
  mediaType: 'video' | 'audio',
): Promise<number | null> =>
  new Promise((resolve) => {
    const mediaElement =
      mediaType === 'video'
        ? document.createElement('video')
        : document.createElement('audio');

    let timeoutId: ReturnType<typeof setTimeout> | null = null;

    const cleanup = () => {
      mediaElement.removeEventListener('loadedmetadata', onLoadedMetadata);
      mediaElement.removeEventListener('error', onError);
      mediaElement.removeEventListener('abort', onError);
      if (timeoutId) clearTimeout(timeoutId);
      mediaElement.pause();
      mediaElement.removeAttribute('src');
      mediaElement.load();
    };

    const onLoadedMetadata = () => {
      const duration = Number(mediaElement.duration);
      cleanup();
      if (Number.isFinite(duration) && duration > 0) {
        resolve(duration);
        return;
      }
      resolve(null);
    };

    const onError = () => {
      cleanup();
      resolve(null);
    };

    timeoutId = setTimeout(() => {
      cleanup();
      resolve(null);
    }, 12000);

    mediaElement.preload = 'metadata';
    mediaElement.crossOrigin = 'anonymous';
    mediaElement.addEventListener('loadedmetadata', onLoadedMetadata);
    mediaElement.addEventListener('error', onError);
    mediaElement.addEventListener('abort', onError);
    mediaElement.src = playbackUrl;
    mediaElement.load();
  });

/**
 * Resolve media duration with blob fallback.
 *
 * First tries to load duration directly from the URL.
 * If that fails (e.g., CORS issues), downloads as blob and retries.
 *
 * @param source - Storage path or URL to the media
 * @param mediaType - Type of media ('video' or 'audio')
 * @returns Promise resolving to duration in seconds, or null if failed
 *
 * @example
 * const duration = await resolveDurationWithFallback('assets/video.mp4', 'video');
 */
export const resolveDurationWithFallback = async (
  source: string,
  mediaType: 'video' | 'audio',
): Promise<number | null> => {
  const playbackUrl = resolveAssetPlaybackUrl(source);
  if (!playbackUrl) return null;

  // Try direct metadata loading first
  const directDuration = await readMediaDuration(playbackUrl, mediaType);
  if (Number.isFinite(directDuration) && Number(directDuration) > 0) {
    return Number(directDuration);
  }

  // Fallback: download as blob and probe
  try {
    const requestUrl =
      playbackUrl.startsWith('http://') || playbackUrl.startsWith('https://')
        ? playbackUrl
        : playbackUrl.replace(/^\/api(?=\/)/, '');

    const token =
      typeof window !== 'undefined' ? localStorage.getItem('token') || '' : '';
    const response = await axios.get(requestUrl, {
      responseType: 'blob',
      headers: token ? { Authorization: `Bearer ${token}` } : undefined,
    });

    const blobUrl = URL.createObjectURL(response.data);
    try {
      const blobDuration = await readMediaDuration(blobUrl, mediaType);
      if (Number.isFinite(blobDuration) && Number(blobDuration) > 0) {
        return Number(blobDuration);
      }
      return null;
    } finally {
      URL.revokeObjectURL(blobUrl);
    }
  } catch (error) {
    logger.error(
      'Failed to fetch media for duration probing:',
      error instanceof Error ? error : { error },
    );
    return null;
  }
};
