import { useEffect, useMemo, useRef, useState } from 'react';
import type { LiveShowLayoutMode, LiveShowPhoto, LiveShowSettings } from '../services/liveShowApi';

const MIN_FIXED_SECONDS = 3;
const MAX_FIXED_SECONDS = 20;

function resolveApprovedAt(photo: LiveShowPhoto): number {
  if (!photo.approved_at) {
    return 0;
  }
  const parsed = Date.parse(photo.approved_at);
  return Number.isNaN(parsed) ? 0 : parsed;
}

function resolvePriority(photo: LiveShowPhoto): number {
  return Number.isFinite(photo.live_priority) ? photo.live_priority : 0;
}

export function resolveItemsPerFrame(layout: LiveShowLayoutMode): number {
  switch (layout) {
    case 'split':
      return 2;
    case 'grid_burst':
      return 4;
    case 'single':
    default:
      return 1;
  }
}

export function resolveIntervalMs(settings: LiveShowSettings, totalCount: number): number {
  if (settings.pace_mode === 'fixed') {
    const safeSeconds = Math.min(MAX_FIXED_SECONDS, Math.max(MIN_FIXED_SECONDS, settings.fixed_interval_seconds));
    return safeSeconds * 1000;
  }

  if (totalCount >= 60) return 4500;
  if (totalCount >= 30) return 5500;
  if (totalCount >= 15) return 6500;
  if (totalCount >= 6) return 7500;
  return 9000;
}

export function resolvePlaybackQueue(photos: LiveShowPhoto[], settings: LiveShowSettings): LiveShowPhoto[] {
  if (photos.length === 0) {
    return [];
  }

  const newestFirst = [...photos].sort((a, b) => {
    const timeDiff = resolveApprovedAt(b) - resolveApprovedAt(a);
    if (timeDiff !== 0) return timeDiff;
    return b.id - a.id;
  });

  if (settings.playback_mode === 'newest_first') {
    return newestFirst;
  }

  if (settings.playback_mode === 'curated') {
    const curated = photos.filter((photo) => photo.is_featured || resolvePriority(photo) > 0);
    const base = curated.length > 0 ? curated : photos;
    return [...base].sort((a, b) => {
      const priorityDiff = resolvePriority(b) - resolvePriority(a);
      if (priorityDiff !== 0) return priorityDiff;
      const timeDiff = resolveApprovedAt(b) - resolveApprovedAt(a);
      if (timeDiff !== 0) return timeDiff;
      return b.id - a.id;
    });
  }

  const oldestFirst = [...photos].sort((a, b) => {
    const timeDiff = resolveApprovedAt(a) - resolveApprovedAt(b);
    if (timeDiff !== 0) return timeDiff;
    return a.id - b.id;
  });

  const balanced: LiveShowPhoto[] = [];
  const seen = new Set<number>();
  let newestIndex = 0;
  let oldestIndex = 0;
  let newestStreak = 0;

  while (balanced.length < photos.length) {
    let added = false;

    if (newestIndex < newestFirst.length && newestStreak < 2) {
      const candidate = newestFirst[newestIndex++];
      if (!seen.has(candidate.id)) {
        balanced.push(candidate);
        seen.add(candidate.id);
        newestStreak += 1;
        added = true;
      }
    }

    if (!added) {
      while (oldestIndex < oldestFirst.length && seen.has(oldestFirst[oldestIndex].id)) {
        oldestIndex += 1;
      }
      if (oldestIndex < oldestFirst.length) {
        const candidate = oldestFirst[oldestIndex++];
        balanced.push(candidate);
        seen.add(candidate.id);
        newestStreak = 0;
        added = true;
      }
    }

    if (!added) {
      while (newestIndex < newestFirst.length && seen.has(newestFirst[newestIndex].id)) {
        newestIndex += 1;
      }
      if (newestIndex < newestFirst.length) {
        const candidate = newestFirst[newestIndex++];
        balanced.push(candidate);
        seen.add(candidate.id);
        newestStreak += 1;
        added = true;
      }
    }

    if (!added) {
      break;
    }
  }

  return balanced;
}

export function buildFramePhotos(
  queue: LiveShowPhoto[],
  startIndex: number,
  itemsPerFrame: number
): LiveShowPhoto[] {
  if (queue.length === 0) {
    return [];
  }

  const safeCount = Math.min(itemsPerFrame, queue.length);
  const result: LiveShowPhoto[] = [];
  for (let offset = 0; offset < safeCount; offset += 1) {
    const idx = (startIndex + offset) % queue.length;
    result.push(queue[idx]);
  }

  return result;
}

export type LiveShowPlaybackState = {
  frame: LiveShowPhoto[];
  layout: LiveShowLayoutMode;
  intervalMs: number;
  frameKey: string;
  nextFrame: LiveShowPhoto[];
};

export function useLiveShowPlayback(
  photos: LiveShowPhoto[],
  settings: LiveShowSettings,
  options: { paused?: boolean } = {}
): LiveShowPlaybackState {
  const queue = useMemo(() => resolvePlaybackQueue(photos, settings), [photos, settings]);
  const layout = settings.layout_mode;
  const itemsPerFrame = resolveItemsPerFrame(layout);
  const [index, setIndex] = useState(0);
  const currentIdRef = useRef<number | null>(null);
  const paused = Boolean(options.paused);

  useEffect(() => {
    if (queue.length === 0) {
      setIndex(0);
      currentIdRef.current = null;
      return;
    }

    if (currentIdRef.current !== null) {
      const existingIndex = queue.findIndex((photo) => photo.id === currentIdRef.current);
      if (existingIndex >= 0) {
        setIndex(existingIndex);
        return;
      }
    }

    setIndex((prev) => prev % queue.length);
  }, [queue]);

  const frame = useMemo(() => {
    const framePhotos = buildFramePhotos(queue, index, itemsPerFrame);
    currentIdRef.current = framePhotos[0]?.id ?? null;
    return framePhotos;
  }, [queue, index, itemsPerFrame]);

  const frameKey = useMemo(() => {
    if (frame.length === 0) {
      return `empty-${layout}`;
    }

    return frame.map((photo) => photo.id).join('-');
  }, [frame, layout]);

  const nextFrame = useMemo(() => {
    if (queue.length === 0) {
      return [];
    }
    return buildFramePhotos(queue, index + itemsPerFrame, itemsPerFrame);
  }, [index, itemsPerFrame, queue]);

  const intervalMs = resolveIntervalMs(settings, queue.length);

  useEffect(() => {
    if (queue.length === 0 || paused) {
      return undefined;
    }

    const timer = window.setInterval(() => {
      setIndex((prev) => (prev + itemsPerFrame) % queue.length);
    }, intervalMs);

    return () => window.clearInterval(timer);
  }, [intervalMs, itemsPerFrame, queue.length]);

  return {
    frame,
    layout,
    intervalMs,
    frameKey,
    nextFrame,
  };
}
