/**
 * usePreloadProgress Hook
 *
 * Tracks preload job progress via DownloadEventBus.
 * Adapted from hoboken's useVideoOptimizationProgress pattern.
 */

import { useState, useEffect, useCallback, useMemo } from 'react';
import { downloadEventBus } from '../lib/offline/DownloadEventBus';
import { OFFLINE_CONFIG } from '../config/offline.config';
import { PRELOAD_CONFIG } from '../config/preload.config';
import type {
  PreloadJob,
  PreloadStartEvent,
  PreloadProgressEvent,
  PreloadCompleteEvent,
  PreloadErrorEvent,
} from '../types/offline';

interface UsePreloadProgressResult {
  jobs: PreloadJob[];
  activeCount: number;
  completedCount: number;
  errorCount: number;
  totalProgress: number;
  isActive: boolean;
  clearJob: (id: string) => void;
  clearAllCompleted: () => void;
  clearAllErrors: () => void;
}

// Helper to update a job in the array
const updateJob = (
  jobs: PreloadJob[],
  jobId: string,
  updates: Partial<PreloadJob>,
): PreloadJob[] => {
  return jobs.map((job) => (job.id === jobId ? { ...job, ...updates } : job));
};

// Helper to calculate overall progress
const calculateOverallProgress = (jobs: PreloadJob[]): number => {
  const activeJobs = jobs.filter(
    (j) => j.status !== 'completed' && j.status !== 'error',
  );
  if (activeJobs.length === 0) return jobs.length > 0 ? 100 : 0;

  const totalBytes = activeJobs.reduce(
    (sum, j) => sum + (j.totalBytes || 0),
    0,
  );
  const loadedBytes = activeJobs.reduce(
    (sum, j) => sum + (j.bytesLoaded || 0),
    0,
  );

  if (totalBytes === 0) {
    // Fallback to average progress
    const avgProgress =
      activeJobs.reduce((sum, j) => sum + j.progress, 0) / activeJobs.length;
    return Math.round(avgProgress);
  }

  return Math.round((loadedBytes / totalBytes) * 100);
};

export function usePreloadProgress(): UsePreloadProgressResult {
  const [jobs, setJobs] = useState<PreloadJob[]>([]);

  // Handle preload start
  useEffect(() => {
    const handleStart = (data: PreloadStartEvent) => {
      setJobs((prev) => {
        // Avoid duplicates
        if (prev.some((j) => j.id === data.jobId)) {
          return prev;
        }

        const newJob: PreloadJob = {
          id: data.jobId,
          assetId: data.assetId,
          url: data.url,
          filename: data.url.split('/').pop() || 'unknown',
          progress: 0,
          status: 'downloading',
          bytesLoaded: 0,
          totalBytes: 0,
          addedAt: Date.now(),
          startedAt: Date.now(),
        };

        return [...prev, newJob];
      });
    };

    return downloadEventBus.on(
      OFFLINE_CONFIG.events.preloadStart as Parameters<
        typeof downloadEventBus.on
      >[0],
      handleStart as Parameters<typeof downloadEventBus.on>[1],
    );
  }, []);

  // Handle preload progress
  useEffect(() => {
    const handleProgress = (data: PreloadProgressEvent) => {
      setJobs((prev) =>
        updateJob(prev, data.jobId, {
          progress: data.progress,
          bytesLoaded: data.bytesLoaded,
          totalBytes: data.totalBytes,
          status: 'downloading',
        }),
      );
    };

    return downloadEventBus.on(
      OFFLINE_CONFIG.events.preloadProgress as Parameters<
        typeof downloadEventBus.on
      >[0],
      handleProgress as Parameters<typeof downloadEventBus.on>[1],
    );
  }, []);

  // Handle preload complete
  useEffect(() => {
    const handleComplete = (data: PreloadCompleteEvent) => {
      setJobs((prev) =>
        updateJob(prev, data.jobId, {
          status: 'completed',
          progress: 100,
          completedAt: Date.now(),
        }),
      );

      // Auto-remove completed jobs after delay (same pattern as hoboken)
      setTimeout(() => {
        setJobs((prev) => prev.filter((j) => j.id !== data.jobId));
      }, PRELOAD_CONFIG.autoRemove.completedMs);
    };

    return downloadEventBus.on(
      OFFLINE_CONFIG.events.preloadComplete as Parameters<
        typeof downloadEventBus.on
      >[0],
      handleComplete as Parameters<typeof downloadEventBus.on>[1],
    );
  }, []);

  // Handle preload error
  useEffect(() => {
    const handleError = (data: PreloadErrorEvent) => {
      setJobs((prev) =>
        updateJob(prev, data.jobId, {
          status: 'error',
          error: data.error,
        }),
      );

      // Auto-remove error jobs after longer delay (same pattern as hoboken)
      setTimeout(() => {
        setJobs((prev) => prev.filter((j) => j.id !== data.jobId));
      }, PRELOAD_CONFIG.autoRemove.errorMs);
    };

    return downloadEventBus.on(
      OFFLINE_CONFIG.events.preloadError as Parameters<
        typeof downloadEventBus.on
      >[0],
      handleError as Parameters<typeof downloadEventBus.on>[1],
    );
  }, []);

  // Actions
  const clearJob = useCallback((id: string) => {
    setJobs((prev) => prev.filter((j) => j.id !== id));
  }, []);

  const clearAllCompleted = useCallback(() => {
    setJobs((prev) => prev.filter((j) => j.status !== 'completed'));
  }, []);

  const clearAllErrors = useCallback(() => {
    setJobs((prev) => prev.filter((j) => j.status !== 'error'));
  }, []);

  // Computed values
  const activeCount = useMemo(
    () =>
      jobs.filter((j) => j.status === 'downloading' || j.status === 'queued')
        .length,
    [jobs],
  );

  const completedCount = useMemo(
    () => jobs.filter((j) => j.status === 'completed').length,
    [jobs],
  );

  const errorCount = useMemo(
    () => jobs.filter((j) => j.status === 'error').length,
    [jobs],
  );

  const totalProgress = useMemo(() => calculateOverallProgress(jobs), [jobs]);

  const isActive = useMemo(() => activeCount > 0, [activeCount]);

  return {
    jobs,
    activeCount,
    completedCount,
    errorCount,
    totalProgress,
    isActive,
    clearJob,
    clearAllCompleted,
    clearAllErrors,
  };
}
