'use client';

import React, { useState, useRef, useCallback } from 'react';
import type { UploadSession } from '../types/api';
import { API_PATHS } from '../types/api';

interface UploadFile {
  id: string;
  file: File;
  progress: number;
  status: 'pending' | 'uploading' | 'paused' | 'completed' | 'error';
  uploadSession?: UploadSession;
  error?: string;
}

interface UploadManagerProps {
  onUploadComplete?: (fileId: string, file: File) => void;
  onUploadError?: (error: string, file: File) => void;
  maxFileSize?: number; // in bytes
  allowedTypes?: string[];
  maxConcurrentUploads?: number;
  chunkSize?: number; // in bytes
}

export default function UploadManager({
  onUploadComplete,
  onUploadError,
  maxFileSize = 100 * 1024 * 1024, // 100MB default
  allowedTypes = ['audio/mpeg', 'audio/wav', 'audio/flac', 'audio/ogg'],
  maxConcurrentUploads = 3,
  chunkSize = 1024 * 1024, // 1MB chunks
}: UploadManagerProps) {
  const [files, setFiles] = useState<UploadFile[]>([]);
  const [isDragging, setIsDragging] = useState(false);
  const [isPaused, setIsPaused] = useState(false);
  const fileInputRef = useRef<HTMLInputElement>(null);
  const activeUploads = useRef<Map<string, AbortController>>(new Map());

  // Validate file
  const validateFile = (file: File): string | null => {
    if (file.size > maxFileSize) {
      return `File size exceeds ${Math.round(maxFileSize / (1024 * 1024))}MB limit`;
    }

    if (allowedTypes.length > 0 && !allowedTypes.includes(file.type)) {
      return `File type ${file.type} is not supported`;
    }

    return null;
  };

  // Handle file selection
  const handleFileSelect = (selectedFiles: FileList | null) => {
    if (!selectedFiles) return;

    const newFiles: UploadFile[] = [];

    Array.from(selectedFiles).forEach(file => {
      const error = validateFile(file);
      const uploadFile: UploadFile = {
        id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
        file,
        progress: 0,
        status: error ? 'error' : 'pending',
        error: error || undefined,
      };
      newFiles.push(uploadFile);
    });

    setFiles(prev => [...prev, ...newFiles]);
  };

  // Initialize upload session
  const initializeUpload = async (uploadFile: UploadFile): Promise<UploadSession | null> => {
    try {
      const response = await fetch(API_PATHS.UPLOAD_INIT, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          fileName: uploadFile.file.name,
          fileSize: uploadFile.file.size,
          mimeType: uploadFile.file.type,
          chunkSize,
        }),
      });

      if (!response.ok) {
        throw new Error('Failed to initialize upload');
      }

      const session: UploadSession = await response.json();
      return session;
    } catch (error) {
      console.error('Upload initialization error:', error);
      return null;
    }
  };

  // Upload a single chunk
  const uploadChunk = async (
    uploadFile: UploadFile,
    chunk: Blob,
    chunkIndex: number,
    uploadSession: UploadSession
  ): Promise<void> => {
    const controller = new AbortController();
    activeUploads.current.set(uploadFile.id, controller);

    try {
      const formData = new FormData();
      formData.append('chunk', chunk);
      formData.append('chunkIndex', chunkIndex.toString());
      formData.append('uploadId', uploadSession.id);

      const chunkPath = API_PATHS.UPLOAD_CHUNK
        .replace(':uploadId', uploadSession.id)
        .replace(':chunkIndex', chunkIndex.toString());

      const response = await fetch(chunkPath, {
        method: 'POST',
        body: formData,
        signal: controller.signal,
      });

      if (!response.ok) {
        throw new Error(`Failed to upload chunk ${chunkIndex}`);
      }
    } finally {
      activeUploads.current.delete(uploadFile.id);
    }
  };

  // Complete upload
  const completeUpload = async (uploadSession: UploadSession): Promise<string | null> => {
    try {
      const response = await fetch(
        API_PATHS.UPLOAD_COMPLETE.replace(':uploadId', uploadSession.id),
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            uploadId: uploadSession.id,
          }),
        }
      );

      if (!response.ok) {
        throw new Error('Failed to complete upload');
      }

      const result = await response.json();
      return result.fileId;
    } catch (error) {
      console.error('Upload completion error:', error);
      return null;
    }
  };

  // Upload file with chunks
  const processFileUpload = async (uploadFile: UploadFile) => {
    if (!uploadFile.uploadSession) return;

    const { file, uploadSession } = uploadFile;
    const totalChunks = Math.ceil(file.size / chunkSize);
    let uploadedChunks = 0;

    setFiles(prev =>
      prev.map(f =>
        f.id === uploadFile.id
          ? { ...f, status: 'uploading', progress: 0 }
          : f
      )
    );

    try {
      // Upload chunks
      for (let i = 0; i < totalChunks; i++) {
        // Check if paused
        if (isPaused) {
          await new Promise(resolve => {
            const checkInterval = setInterval(() => {
              if (!isPaused) {
                clearInterval(checkInterval);
                resolve(undefined);
              }
            }, 100);
          });
        }

        const start = i * chunkSize;
        const end = Math.min(start + chunkSize, file.size);
        const chunk = file.slice(start, end);

        await uploadChunk(uploadFile, chunk, i, uploadSession);
        uploadedChunks++;

        // Update progress
        const progress = (uploadedChunks / totalChunks) * 100;
        setFiles(prev =>
          prev.map(f =>
            f.id === uploadFile.id
              ? { ...f, progress }
              : f
          )
        );
      }

      // Complete upload
      const fileId = await completeUpload(uploadSession);

      if (fileId) {
        setFiles(prev =>
          prev.map(f =>
            f.id === uploadFile.id
              ? { ...f, status: 'completed', progress: 100 }
              : f
          )
        );
        onUploadComplete?.(fileId, file);
      } else {
        throw new Error('Failed to complete upload');
      }
    } catch (error) {
      const errorMessage = error instanceof Error ? error.message : 'Upload failed';

      setFiles(prev =>
        prev.map(f =>
          f.id === uploadFile.id
            ? { ...f, status: 'error', error: errorMessage }
            : f
        )
      );
      onUploadError?.(errorMessage, file);
    }
  };

  // Start upload process
  const initializeAndStartUpload = async (uploadFile: UploadFile) => {
    const session = await initializeUpload(uploadFile);

    if (session) {
      const updatedFile = { ...uploadFile, uploadSession: session };
      setFiles(prev =>
        prev.map(f => f.id === uploadFile.id ? updatedFile : f)
      );
      await processFileUpload(updatedFile);
    } else {
      setFiles(prev =>
        prev.map(f =>
          f.id === uploadFile.id
            ? { ...f, status: 'error', error: 'Failed to initialize upload' }
            : f
        )
      );
    }
  };

  // Process upload queue
  const processQueue = useCallback(() => {
    const pendingFiles = files.filter(f => f.status === 'pending');
    const activeUploadsCount = files.filter(f => f.status === 'uploading').length;

    if (isPaused || activeUploadsCount >= maxConcurrentUploads) return;

    const slotsAvailable = maxConcurrentUploads - activeUploadsCount;
    const filesToUpload = pendingFiles.slice(0, slotsAvailable);

    filesToUpload.forEach(file => {
      initializeAndStartUpload(file);
    });
  }, [files, isPaused, maxConcurrentUploads]);

  // Auto-process queue when files change
  React.useEffect(() => {
    processQueue();
  }, [files, processQueue]);

  // Drag and drop handlers
  const handleDragEnter = (e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setIsDragging(true);
  };

  const handleDragLeave = (e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setIsDragging(false);
  };

  const handleDragOver = (e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
  };

  const handleDrop = (e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    setIsDragging(false);
    handleFileSelect(e.dataTransfer.files);
  };

  // Pause/resume all uploads
  const togglePause = () => {
    setIsPaused(!isPaused);

    if (!isPaused) {
      // Cancel all active uploads
      activeUploads.current.forEach(controller => {
        controller.abort();
      });
      activeUploads.current.clear();
    }
  };

  // Remove file from list
  const removeFile = (id: string) => {
    // Cancel if uploading
    const controller = activeUploads.current.get(id);
    if (controller) {
      controller.abort();
      activeUploads.current.delete(id);
    }

    setFiles(prev => prev.filter(f => f.id !== id));
  };

  // Retry failed upload
  const retryUpload = (uploadFile: UploadFile) => {
    setFiles(prev =>
      prev.map(f =>
        f.id === uploadFile.id
          ? { ...f, status: 'pending', progress: 0, error: undefined }
          : f
      )
    );
  };

  // Clear completed uploads
  const clearCompleted = () => {
    setFiles(prev => prev.filter(f => f.status !== 'completed'));
  };

  return (
    <div className="bg-white rounded-lg shadow-lg p-6">
      <h2 className="text-xl font-semibold mb-4">Upload Manager</h2>

      {/* Upload area */}
      <div
        className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors ${
          isDragging
            ? 'border-blue-500 bg-blue-50'
            : 'border-gray-300 hover:border-gray-400'
        }`}
        onDragEnter={handleDragEnter}
        onDragOver={handleDragOver}
        onDragLeave={handleDragLeave}
        onDrop={handleDrop}
      >
        <svg
          className="mx-auto h-12 w-12 text-gray-400 mb-4"
          fill="none"
          stroke="currentColor"
          viewBox="0 0 24 24"
        >
          <path
            strokeLinecap="round"
            strokeLinejoin="round"
            strokeWidth={2}
            d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"
          />
        </svg>

        <p className="text-gray-600 mb-2">
          Drag and drop audio files here, or{' '}
          <button
            onClick={() => fileInputRef.current?.click()}
            className="text-blue-600 hover:text-blue-700 font-medium"
          >
            browse
          </button>
        </p>

        <p className="text-sm text-gray-500">
          Supported formats: MP3, WAV, FLAC, OGG (max {Math.round(maxFileSize / (1024 * 1024))}MB)
        </p>

        <input
          ref={fileInputRef}
          type="file"
          multiple
          accept={allowedTypes.join(',')}
          className="hidden"
          onChange={(e) => handleFileSelect(e.target.files)}
        />
      </div>

      {/* File list */}
      {files.length > 0 && (
        <div className="mt-6">
          <div className="flex justify-between items-center mb-4">
            <h3 className="font-medium">
              Uploads ({files.length} file{files.length !== 1 ? 's' : ''})
            </h3>

            <div className="flex gap-2">
              {files.some(f => f.status === 'uploading') && (
                <button
                  onClick={togglePause}
                  className="px-3 py-1 text-sm bg-gray-200 hover:bg-gray-300 rounded-md transition-colors"
                >
                  {isPaused ? 'Resume' : 'Pause'}
                </button>
              )}

              {files.some(f => f.status === 'completed') && (
                <button
                  onClick={clearCompleted}
                  className="px-3 py-1 text-sm bg-gray-200 hover:bg-gray-300 rounded-md transition-colors"
                >
                  Clear Completed
                </button>
              )}
            </div>
          </div>

          <div className="space-y-3">
            {files.map(uploadFile => (
              <div
                key={uploadFile.id}
                className="border rounded-lg p-3 bg-gray-50"
              >
                <div className="flex justify-between items-start mb-2">
                  <div className="flex-1 min-w-0">
                    <p className="font-medium truncate">{uploadFile.file.name}</p>
                    <p className="text-sm text-gray-500">
                      {(uploadFile.file.size / (1024 * 1024)).toFixed(2)} MB
                    </p>
                  </div>

                  <div className="flex items-center gap-2 ml-4">
                    {uploadFile.status === 'error' && (
                      <button
                        onClick={() => retryUpload(uploadFile)}
                        className="p-1 text-blue-600 hover:text-blue-700"
                        title="Retry"
                      >
                        <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
                        </svg>
                      </button>
                    )}

                    {uploadFile.status !== 'uploading' && (
                      <button
                        onClick={() => removeFile(uploadFile.id)}
                        className="p-1 text-red-600 hover:text-red-700"
                        title="Remove"
                      >
                        <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                          <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
                        </svg>
                      </button>
                    )}
                  </div>
                </div>

                {/* Status and progress */}
                <div className="flex items-center gap-2">
                  <div className="flex-1 bg-gray-200 rounded-full h-2">
                    <div
                      className={`h-2 rounded-full transition-all ${
                        uploadFile.status === 'completed'
                          ? 'bg-green-500'
                          : uploadFile.status === 'error'
                          ? 'bg-red-500'
                          : 'bg-blue-500'
                      }`}
                      style={{ width: `${uploadFile.progress}%` }}
                    />
                  </div>

                  <span className="text-sm text-gray-600 min-w-0">
                    {uploadFile.status === 'pending' && 'Pending'}
                    {uploadFile.status === 'uploading' && `${Math.round(uploadFile.progress)}%`}
                    {uploadFile.status === 'paused' && 'Paused'}
                    {uploadFile.status === 'completed' && 'Completed'}
                    {uploadFile.status === 'error' && 'Error'}
                  </span>
                </div>

                {/* Error message */}
                {uploadFile.error && (
                  <p className="text-sm text-red-600 mt-1">{uploadFile.error}</p>
                )}
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}