/**
 * useMediaDurationProbe Hook
 *
 * Probes media durations with caching and deduplication.
 * Used in constructor.tsx for displaying video/audio duration info.
 */

import { useState, useEffect, useRef, useCallback, useMemo } from 'react';
import {
  formatDurationNote,
  resolveDurationWithFallback,
} from '../lib/mediaHelpers';
import { logger } from '../lib/logger';

interface DurationProbeTarget {
  source: string;
  mediaType: 'video' | 'audio';
}

interface UseMediaDurationProbeOptions {
  /** Array of media sources to probe */
  targets: DurationProbeTarget[];
}

interface UseMediaDurationProbeResult {
  /** Map of source URL to resolved duration (seconds) or null */
  durationBySource: Record<string, number | null>;
  /** Get known duration for a source (or null if unknown/pending) */
  getDuration: (source: string) => number | null;
  /** Get formatted duration note for a source */
  getDurationNote: (source: string) => string;
  /** Whether any probes are in progress */
  isProbing: boolean;
}

/**
 * Hook for probing and caching media durations.
 * Automatically deduplicates in-flight requests and caches results.
 *
 * @example
 * const { getDurationNote, getDuration } = useMediaDurationProbe({
 *   targets: [
 *     { source: backgroundVideoUrl, mediaType: 'video' },
 *     { source: backgroundAudioUrl, mediaType: 'audio' },
 *   ],
 * });
 *
 * return <p>{getDurationNote(backgroundVideoUrl)}</p>;
 */
export function useMediaDurationProbe({
  targets,
}: UseMediaDurationProbeOptions): UseMediaDurationProbeResult {
  const [durationBySource, setDurationBySource] = useState<
    Record<string, number | null>
  >({});
  const [isProbing, setIsProbing] = useState(false);
  const inFlightRef = useRef<Set<string>>(new Set());

  // Probe targets for duration
  useEffect(() => {
    if (typeof window === 'undefined') return;

    let isCancelled = false;
    let activeProbes = 0;

    targets.forEach(({ source, mediaType }) => {
      const normalizedSource = String(source || '').trim();
      if (!normalizedSource) return;

      // Skip if already resolved
      if (durationBySource[normalizedSource] !== undefined) return;

      // Skip if already in flight
      const probeKey = `${mediaType}:${normalizedSource}`;
      if (inFlightRef.current.has(probeKey)) return;

      inFlightRef.current.add(probeKey);
      activeProbes++;
      setIsProbing(true);

      resolveDurationWithFallback(normalizedSource, mediaType)
        .then((duration) => {
          if (isCancelled) return;
          setDurationBySource((prev) => ({
            ...prev,
            [normalizedSource]:
              Number.isFinite(duration) && Number(duration) > 0
                ? Number(duration)
                : null,
          }));
        })
        .catch((error) => {
          logger.error(
            'Failed to resolve media duration:',
            error instanceof Error ? error : { error },
          );
          if (isCancelled) return;
          setDurationBySource((prev) => ({
            ...prev,
            [normalizedSource]: null,
          }));
        })
        .finally(() => {
          inFlightRef.current.delete(probeKey);
          activeProbes--;
          if (activeProbes === 0) {
            setIsProbing(false);
          }
        });
    });

    return () => {
      isCancelled = true;
    };
  }, [targets, durationBySource]);

  const getDuration = useCallback(
    (source: string): number | null => {
      const normalizedSource = String(source || '').trim();
      if (!normalizedSource) return null;

      const duration = durationBySource[normalizedSource];
      if (Number.isFinite(duration) && Number(duration) > 0) {
        return Number(duration);
      }

      return null;
    },
    [durationBySource],
  );

  const getDurationNote = useCallback(
    (source: string): string => {
      return formatDurationNote(getDuration(source));
    },
    [getDuration],
  );

  return {
    durationBySource,
    getDuration,
    getDurationNote,
    isProbing,
  };
}

/**
 * Build duration probe targets from constructor state.
 * Utility to simplify target array creation in constructor.tsx.
 */
export function buildDurationProbeTargets({
  backgroundVideoUrl,
  backgroundAudioUrl,
  selectedElement,
  newTransitionVideoUrl,
  elements,
  isMediaElementType,
  isVideoPlayerElementType,
  isNavigationElementType,
}: {
  backgroundVideoUrl?: string;
  backgroundAudioUrl?: string;
  selectedElement?: {
    type: string;
    mediaUrl?: string;
  } | null;
  newTransitionVideoUrl?: string;
  elements?: Array<{
    type: string;
    transitionVideoUrl?: string;
    reverseVideoUrl?: string;
  }>;
  isMediaElementType: (type: string) => boolean;
  isVideoPlayerElementType: (type: string) => boolean;
  isNavigationElementType: (type: string) => boolean;
}): DurationProbeTarget[] {
  const targets: DurationProbeTarget[] = [];

  if (backgroundVideoUrl) {
    targets.push({ source: backgroundVideoUrl, mediaType: 'video' });
  }

  if (backgroundAudioUrl) {
    targets.push({ source: backgroundAudioUrl, mediaType: 'audio' });
  }

  if (
    selectedElement &&
    isMediaElementType(selectedElement.type) &&
    selectedElement.mediaUrl
  ) {
    targets.push({
      source: selectedElement.mediaUrl,
      mediaType: isVideoPlayerElementType(selectedElement.type)
        ? 'video'
        : 'audio',
    });
  }

  if (newTransitionVideoUrl) {
    targets.push({ source: newTransitionVideoUrl, mediaType: 'video' });
  }

  elements?.forEach((element) => {
    if (!isNavigationElementType(element.type)) return;
    if (element.transitionVideoUrl) {
      targets.push({ source: element.transitionVideoUrl, mediaType: 'video' });
    }
    if (element.reverseVideoUrl) {
      targets.push({ source: element.reverseVideoUrl, mediaType: 'video' });
    }
  });

  return targets;
}

export default useMediaDurationProbe;
