'use client'

import { useState, useCallback, useRef } from 'react'
import { trpc } from '@/lib/trpc/client'
import { Button } from '@/components/ui/button'
import { Progress } from '@/components/ui/progress'
import { Badge } from '@/components/ui/badge'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'
import {
  Upload,
  X,
  FileIcon,
  CheckCircle2,
  AlertCircle,
  Loader2,
  Film,
  FileText,
  Presentation,
} from 'lucide-react'
import { cn, formatFileSize } from '@/lib/utils'

const MAX_FILE_SIZE = 500 * 1024 * 1024 // 500MB

type FileType = 'EXEC_SUMMARY' | 'PRESENTATION' | 'VIDEO' | 'OTHER'

interface UploadingFile {
  id: string
  file: File
  progress: number
  status: 'pending' | 'uploading' | 'complete' | 'error'
  error?: string
  fileType: FileType
  dbFileId?: string
}

interface FileUploadProps {
  projectId: string
  onUploadComplete?: (file: { id: string; fileName: string; fileType: string }) => void
  onUploadError?: (error: Error) => void
  allowedTypes?: string[]
  multiple?: boolean
  className?: string
  stageId?: string
  availableStages?: Array<{ id: string; name: string }>
}

// Map MIME types to suggested file types
function suggestFileType(mimeType: string): FileType {
  if (mimeType.startsWith('video/')) return 'VIDEO'
  if (mimeType === 'application/pdf') return 'EXEC_SUMMARY'
  if (
    mimeType.includes('presentation') ||
    mimeType.includes('powerpoint') ||
    mimeType.includes('slides')
  ) {
    return 'PRESENTATION'
  }
  return 'OTHER'
}

// Get icon for file type
function getFileTypeIcon(fileType: FileType) {
  switch (fileType) {
    case 'VIDEO':
      return <Film className="h-4 w-4" />
    case 'EXEC_SUMMARY':
      return <FileText className="h-4 w-4" />
    case 'PRESENTATION':
      return <Presentation className="h-4 w-4" />
    default:
      return <FileIcon className="h-4 w-4" />
  }
}

export function FileUpload({
  projectId,
  onUploadComplete,
  onUploadError,
  allowedTypes,
  multiple = true,
  className,
  stageId,
  availableStages,
}: FileUploadProps) {
  const [uploadingFiles, setUploadingFiles] = useState<UploadingFile[]>([])
  const [isDragging, setIsDragging] = useState(false)
  const [selectedStageId, setSelectedStageId] = useState<string | null>(stageId ?? null)
  const fileInputRef = useRef<HTMLInputElement>(null)

  const getUploadUrl = trpc.file.getUploadUrl.useMutation()
  const confirmUpload = trpc.file.confirmUpload.useMutation()
  const utils = trpc.useUtils()

  // Validate file
  const validateFile = useCallback(
    (file: File): string | null => {
      if (file.size > MAX_FILE_SIZE) {
        return `File size exceeds ${formatFileSize(MAX_FILE_SIZE)} limit`
      }
      if (allowedTypes && !allowedTypes.includes(file.type)) {
        return `File type ${file.type} is not allowed`
      }
      return null
    },
    [allowedTypes]
  )

  // Upload a single file
  const uploadFile = useCallback(
    async (uploadingFile: UploadingFile) => {
      const { file, id, fileType } = uploadingFile

      try {
        // Update status to uploading
        setUploadingFiles((prev) =>
          prev.map((f) => (f.id === id ? { ...f, status: 'uploading' as const } : f))
        )

        // Get pre-signed upload URL
        const { uploadUrl, file: dbFile } = await getUploadUrl.mutateAsync({
          projectId,
          fileName: file.name,
          fileType,
          mimeType: file.type || 'application/octet-stream',
          size: file.size,
          stageId: selectedStageId ?? undefined,
        })

        // Store the DB file ID
        setUploadingFiles((prev) =>
          prev.map((f) => (f.id === id ? { ...f, dbFileId: dbFile.id } : f))
        )

        // Upload to MinIO using XHR for progress tracking
        await new Promise<void>((resolve, reject) => {
          const xhr = new XMLHttpRequest()

          xhr.upload.addEventListener('progress', (event) => {
            if (event.lengthComputable) {
              const progress = Math.round((event.loaded / event.total) * 100)
              setUploadingFiles((prev) =>
                prev.map((f) => (f.id === id ? { ...f, progress } : f))
              )
            }
          })

          xhr.addEventListener('load', () => {
            if (xhr.status >= 200 && xhr.status < 300) {
              resolve()
            } else {
              reject(new Error(`Upload failed with status ${xhr.status}`))
            }
          })

          xhr.addEventListener('error', () => {
            reject(new Error('Network error during upload'))
          })

          xhr.addEventListener('abort', () => {
            reject(new Error('Upload aborted'))
          })

          xhr.open('PUT', uploadUrl)
          xhr.setRequestHeader('Content-Type', file.type || 'application/octet-stream')
          xhr.send(file)
        })

        // Confirm upload
        await confirmUpload.mutateAsync({ fileId: dbFile.id })

        // Update status to complete
        setUploadingFiles((prev) =>
          prev.map((f) =>
            f.id === id ? { ...f, status: 'complete' as const, progress: 100 } : f
          )
        )

        // Invalidate file list queries
        utils.file.listByProject.invalidate({ projectId })

        // Notify parent
        onUploadComplete?.({
          id: dbFile.id,
          fileName: file.name,
          fileType,
        })
      } catch (error) {
        const errorMessage =
          error instanceof Error ? error.message : 'Upload failed'

        setUploadingFiles((prev) =>
          prev.map((f) =>
            f.id === id ? { ...f, status: 'error' as const, error: errorMessage } : f
          )
        )

        onUploadError?.(error instanceof Error ? error : new Error(errorMessage))
      }
    },
    [projectId, getUploadUrl, confirmUpload, utils, onUploadComplete, onUploadError]
  )

  // Handle file selection
  const handleFiles = useCallback(
    (files: FileList | File[]) => {
      const fileArray = Array.from(files)
      const filesToUpload = multiple ? fileArray : [fileArray[0]].filter(Boolean)

      const newUploadingFiles: UploadingFile[] = filesToUpload.map((file) => {
        const validationError = validateFile(file)
        return {
          id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
          file,
          progress: 0,
          status: validationError ? ('error' as const) : ('pending' as const),
          error: validationError || undefined,
          fileType: suggestFileType(file.type),
        }
      })

      setUploadingFiles((prev) => [...prev, ...newUploadingFiles])

      // Start uploading valid files
      newUploadingFiles
        .filter((f) => f.status === 'pending')
        .forEach((f) => uploadFile(f))
    },
    [multiple, validateFile, uploadFile]
  )

  // Drag and drop handlers
  const handleDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault()
    e.stopPropagation()
    setIsDragging(true)
  }, [])

  const handleDragLeave = useCallback((e: React.DragEvent) => {
    e.preventDefault()
    e.stopPropagation()
    setIsDragging(false)
  }, [])

  const handleDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault()
      e.stopPropagation()
      setIsDragging(false)

      if (e.dataTransfer.files?.length) {
        handleFiles(e.dataTransfer.files)
      }
    },
    [handleFiles]
  )

  // File input change handler
  const handleFileInputChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      if (e.target.files?.length) {
        handleFiles(e.target.files)
      }
      // Reset input so same file can be selected again
      e.target.value = ''
    },
    [handleFiles]
  )

  // Update file type for a pending file
  const updateFileType = useCallback((fileId: string, fileType: FileType) => {
    setUploadingFiles((prev) =>
      prev.map((f) => (f.id === fileId ? { ...f, fileType } : f))
    )
  }, [])

  // Remove a file from the queue
  const removeFile = useCallback((fileId: string) => {
    setUploadingFiles((prev) => prev.filter((f) => f.id !== fileId))
  }, [])

  // Retry a failed upload
  const retryUpload = useCallback(
    (fileId: string) => {
      setUploadingFiles((prev) =>
        prev.map((f) =>
          f.id === fileId
            ? { ...f, status: 'pending' as const, progress: 0, error: undefined }
            : f
        )
      )

      const file = uploadingFiles.find((f) => f.id === fileId)
      if (file) {
        uploadFile({ ...file, status: 'pending', progress: 0, error: undefined })
      }
    },
    [uploadingFiles, uploadFile]
  )

  const hasActiveUploads = uploadingFiles.some(
    (f) => f.status === 'pending' || f.status === 'uploading'
  )

  return (
    <div className={cn('space-y-4', className)}>
      {/* Stage selector */}
      {availableStages && availableStages.length > 0 && (
        <div className="space-y-2">
          <label className="text-sm font-medium">
            Upload for Stage
          </label>
          <Select
            value={selectedStageId ?? 'null'}
            onValueChange={(value) => setSelectedStageId(value === 'null' ? null : value)}
          >
            <SelectTrigger className="w-full">
              <SelectValue placeholder="Select a stage" />
            </SelectTrigger>
            <SelectContent>
              <SelectItem value="null">General (no specific stage)</SelectItem>
              {availableStages.map((stage) => (
                <SelectItem key={stage.id} value={stage.id}>
                  {stage.name}
                </SelectItem>
              ))}
            </SelectContent>
          </Select>
        </div>
      )}

      {/* Drop zone */}
      <div
        className={cn(
          'relative border-2 border-dashed rounded-lg p-6 text-center transition-colors cursor-pointer',
          isDragging
            ? 'border-primary bg-primary/5'
            : 'border-muted-foreground/25 hover:border-primary/50',
          hasActiveUploads && 'opacity-50 pointer-events-none'
        )}
        onDragOver={handleDragOver}
        onDragLeave={handleDragLeave}
        onDrop={handleDrop}
        onClick={() => fileInputRef.current?.click()}
      >
        <input
          ref={fileInputRef}
          type="file"
          multiple={multiple}
          accept={allowedTypes?.join(',')}
          onChange={handleFileInputChange}
          className="hidden"
        />

        <Upload className="mx-auto h-10 w-10 text-muted-foreground" />
        <p className="mt-2 font-medium">
          {isDragging ? 'Drop files here' : 'Drag and drop files here'}
        </p>
        <p className="text-sm text-muted-foreground">
          or click to browse (max {formatFileSize(MAX_FILE_SIZE)})
        </p>
      </div>

      {/* Upload queue */}
      {uploadingFiles.length > 0 && (
        <div className="space-y-2">
          {uploadingFiles.map((uploadingFile) => (
            <div
              key={uploadingFile.id}
              className={cn(
                'flex items-center gap-3 rounded-lg border p-3',
                uploadingFile.status === 'error' && 'border-destructive/50 bg-destructive/5',
                uploadingFile.status === 'complete' && 'border-green-500/50 bg-green-500/5'
              )}
            >
              {/* File type icon */}
              <div className="shrink-0 text-muted-foreground">
                {getFileTypeIcon(uploadingFile.fileType)}
              </div>

              {/* File info */}
              <div className="min-w-0 flex-1">
                <div className="flex items-center gap-2">
                  <p className="truncate text-sm font-medium">
                    {uploadingFile.file.name}
                  </p>
                  <Badge variant="outline" className="shrink-0 text-xs">
                    {formatFileSize(uploadingFile.file.size)}
                  </Badge>
                </div>

                {/* Progress bar or error message */}
                {uploadingFile.status === 'uploading' && (
                  <div className="mt-1 flex items-center gap-2">
                    <Progress value={uploadingFile.progress} className="h-1.5 flex-1" />
                    <span className="text-xs text-muted-foreground">
                      {uploadingFile.progress}%
                    </span>
                  </div>
                )}

                {uploadingFile.status === 'error' && (
                  <p className="mt-1 text-xs text-destructive">{uploadingFile.error}</p>
                )}

                {uploadingFile.status === 'pending' && (
                  <div className="mt-1 flex items-center gap-2">
                    <Select
                      value={uploadingFile.fileType}
                      onValueChange={(v) => updateFileType(uploadingFile.id, v as FileType)}
                    >
                      <SelectTrigger className="h-7 w-32 text-xs">
                        <SelectValue />
                      </SelectTrigger>
                      <SelectContent>
                        <SelectItem value="EXEC_SUMMARY">Executive Summary</SelectItem>
                        <SelectItem value="PRESENTATION">Presentation</SelectItem>
                        <SelectItem value="VIDEO">Video</SelectItem>
                        <SelectItem value="OTHER">Other</SelectItem>
                      </SelectContent>
                    </Select>
                  </div>
                )}
              </div>

              {/* Status / Actions */}
              <div className="shrink-0">
                {uploadingFile.status === 'pending' && (
                  <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" />
                )}

                {uploadingFile.status === 'uploading' && (
                  <Loader2 className="h-4 w-4 animate-spin text-primary" />
                )}

                {uploadingFile.status === 'complete' && (
                  <CheckCircle2 className="h-4 w-4 text-green-600" />
                )}

                {uploadingFile.status === 'error' && (
                  <div className="flex items-center gap-1">
                    <Button
                      variant="ghost"
                      size="sm"
                      className="h-7 px-2 text-xs"
                      onClick={(e) => {
                        e.stopPropagation()
                        retryUpload(uploadingFile.id)
                      }}
                    >
                      Retry
                    </Button>
                    <Button
                      variant="ghost"
                      size="icon"
                      className="h-7 w-7"
                      aria-label="Remove file"
                      onClick={(e) => {
                        e.stopPropagation()
                        removeFile(uploadingFile.id)
                      }}
                    >
                      <X className="h-4 w-4" />
                    </Button>
                  </div>
                )}
              </div>
            </div>
          ))}

          {/* Clear completed */}
          {uploadingFiles.some((f) => f.status === 'complete') && (
            <Button
              variant="ghost"
              size="sm"
              className="text-xs"
              onClick={() =>
                setUploadingFiles((prev) => prev.filter((f) => f.status !== 'complete'))
              }
            >
              Clear completed
            </Button>
          )}
        </div>
      )}
    </div>
  )
}
