import axios from 'axios';
import { useCallback, useEffect, useRef, useState } from 'react';
import { toast } from 'react-toastify';
import FileUploader from '../Uploaders/UploadService';
import type { AssetSection } from './AssetSectionCard';
import type { UploadQueueItem } from './UploadProgressList';
import { logger } from '../../lib/logger';
import {
  probeMediaDuration,
  isVideoMimeType,
  isAudioMimeType,
} from '../../lib/mediaDuration';

interface UseAssetUploaderOptions {
  selectedProjectId: string;
  onUploadComplete: () => void;
}

interface UseAssetUploaderReturn {
  uploadingSections: string[];
  uploadQueues: Record<string, UploadQueueItem[]>;
  runBatchUpload: (section: AssetSection, files: File[]) => Promise<void>;
}

export function useAssetUploader({
  selectedProjectId,
  onUploadComplete,
}: UseAssetUploaderOptions): UseAssetUploaderReturn {
  const abortControllerRef = useRef<AbortController | null>(null);
  const [uploadingSections, setUploadingSections] = useState<string[]>([]);
  const [uploadQueues, setUploadQueues] = useState<
    Record<string, UploadQueueItem[]>
  >({});

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }
    };
  }, []);

  const addSectionUpload = useCallback(
    (sectionKey: string, items: UploadQueueItem[]) => {
      setUploadQueues((prev) => ({
        ...prev,
        [sectionKey]: [...(prev[sectionKey] || []), ...items],
      }));
    },
    [],
  );

  const updateSectionUpload = useCallback(
    (sectionKey: string, itemId: string, patch: Partial<UploadQueueItem>) => {
      setUploadQueues((prev) => ({
        ...prev,
        [sectionKey]: (prev[sectionKey] || []).map((item) =>
          item.id === itemId ? { ...item, ...patch } : item,
        ),
      }));
    },
    [],
  );

  const markSectionUploading = useCallback(
    (sectionKey: string, isUploading: boolean) => {
      setUploadingSections((prev) => {
        if (isUploading) {
          if (prev.includes(sectionKey)) return prev;
          return [...prev, sectionKey];
        }
        return prev.filter((key) => key !== sectionKey);
      });
    },
    [],
  );

  const uploadAssetFile = useCallback(
    async (
      section: AssetSection,
      projectId: string,
      itemId: string,
      file: File,
      signal?: AbortSignal,
    ) => {
      updateSectionUpload(section.key, itemId, {
        status: 'uploading',
        progress: 0,
      });

      // Validate file type based on section's expected asset format
      const validationSchema = { assetType: section.assetFormat };

      const remoteFile = await FileUploader.uploadChunked(
        `assets/${projectId}`,
        file,
        validationSchema,
        {
          chunkSize: 5 * 1024 * 1024,
          maxRetries: 3,
          signal,
          onProgress: (progress: number) => {
            updateSectionUpload(section.key, itemId, {
              progress,
              status: 'uploading',
            });
          },
          onStatus: (status: string) => {
            if (status === 'finalizing') {
              updateSectionUpload(section.key, itemId, { status: 'saving' });
            }
          },
        },
      );

      // Probe media duration for video/audio files
      let durationSec: number | null = null;
      let widthPx: number | null = null;
      let heightPx: number | null = null;

      if (isVideoMimeType(file.type)) {
        const probeResult = await probeMediaDuration(file, 'video');
        if (probeResult) {
          durationSec = probeResult.duration;
          widthPx = probeResult.width ?? null;
          heightPx = probeResult.height ?? null;
        }
      } else if (isAudioMimeType(file.type)) {
        const probeResult = await probeMediaDuration(file, 'audio');
        if (probeResult) {
          durationSec = probeResult.duration;
        }
      }

      await axios.post('/assets', {
        data: {
          project: projectId,
          name: file.name,
          asset_type: section.assetFormat,
          type: section.assetCategory,
          cdn_url: remoteFile.publicUrl,
          storage_key: remoteFile.privateUrl,
          mime_type: file.type || null,
          size_mb: Number((file.size / (1024 * 1024)).toFixed(4)),
          is_public: false,
          duration_sec: durationSec,
          width_px: widthPx,
          height_px: heightPx,
        },
      });

      updateSectionUpload(section.key, itemId, {
        status: 'success',
        progress: 100,
      });
    },
    [updateSectionUpload],
  );

  const runBatchUpload = useCallback(
    async (section: AssetSection, files: File[]) => {
      if (!selectedProjectId) {
        toast('Select a project first', {
          type: 'warning',
          position: 'bottom-center',
        });
        return;
      }

      if (!files.length) return;

      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }
      abortControllerRef.current = new AbortController();
      const { signal } = abortControllerRef.current;

      const queueItems: UploadQueueItem[] = files.map((file) => ({
        id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
        fileName: file.name,
        progress: 0,
        status: 'queued',
      }));

      addSectionUpload(section.key, queueItems);
      markSectionUploading(section.key, true);

      const queue = files.map((file, index) => ({
        file,
        itemId: queueItems[index].id,
      }));
      const maxConcurrent = 2;
      let currentIndex = 0;
      let failedCount = 0;

      const worker = async () => {
        while (currentIndex < queue.length) {
          if (signal.aborted) break;

          const nextIndex = currentIndex;
          currentIndex += 1;
          const item = queue[nextIndex];

          try {
            await uploadAssetFile(
              section,
              selectedProjectId,
              item.itemId,
              item.file,
              signal,
            );
          } catch (error: unknown) {
            if (signal.aborted) break;

            const axiosError = error as {
              response?: { data?: { message?: string } };
              message?: string;
            };
            const message =
              axiosError?.response?.data?.message ||
              axiosError?.message ||
              'Upload failed';
            logger.error(
              `Failed to upload ${item.file.name}:`,
              error instanceof Error ? error : { error },
            );
            failedCount += 1;
            updateSectionUpload(section.key, item.itemId, {
              status: 'error',
              error: message,
            });
          }
        }
      };

      try {
        await Promise.all(
          Array.from({ length: Math.min(maxConcurrent, queue.length) }, () =>
            worker(),
          ),
        );
        if (!signal.aborted) {
          onUploadComplete();
          if (failedCount > 0) {
            toast(
              `Batch upload finished: ${queue.length - failedCount}/${queue.length} succeeded`,
              { type: 'warning', position: 'bottom-center' },
            );
          } else {
            toast(`Batch upload finished for ${section.label}`, {
              type: 'success',
              position: 'bottom-center',
            });
          }
        }
      } finally {
        markSectionUploading(section.key, false);
      }
    },
    [
      selectedProjectId,
      addSectionUpload,
      markSectionUploading,
      uploadAssetFile,
      updateSectionUpload,
      onUploadComplete,
    ],
  );

  return {
    uploadingSections,
    uploadQueues,
    runBatchUpload,
  };
}

export default useAssetUploader;
