import React from 'react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Button } from '@tamagui/button';
import { Camera, FlipHorizontal, Image, ListVideo, RefreshCcw, Sparkles, UploadCloud, X, Zap, ZapOff } from 'lucide-react';
import AppShell from '../components/AppShell';
import { useEventData } from '../context/EventDataContext';
import { useOptionalGuestIdentity } from '../context/GuestIdentityContext';
import { uploadPhoto, useUploadQueue } from '../services/uploadApi';
import { useGuestThemeVariant } from '../lib/guestTheme';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useTranslation } from '@/guest/i18n/useTranslation';
import { useGuestTaskProgress } from '@/guest/hooks/useGuestTaskProgress';
import { fetchPendingUploadsSummary, type PendingUpload } from '@/guest/services/pendingUploadsApi';
import { resolveUploadErrorDialog, type UploadErrorDialog } from '@/guest/lib/uploadErrorDialog';
import { fetchTasks, type TaskItem } from '../services/tasksApi';
import SurfaceCard from '../components/SurfaceCard';
import { pushGuestToast } from '../lib/toast';

function getTaskValue(task: TaskItem, key: string): string | undefined {
  const value = task?.[key as keyof TaskItem];
  if (typeof value === 'string' && value.trim() !== '') return value;
  if (value && typeof value === 'object') {
    const obj = value as Record<string, unknown>;
    const candidate = Object.values(obj).find((item) => typeof item === 'string' && item.trim() !== '');
    if (typeof candidate === 'string') return candidate;
  }
  return undefined;
}

export default function UploadScreen() {
  const { token, event } = useEventData();
  const identity = useOptionalGuestIdentity();
  const { items, add } = useUploadQueue();
  const navigate = useNavigate();
  const [searchParams] = useSearchParams();
  const mockPreviewEnabled = import.meta.env.DEV && ['1', 'true', 'yes'].includes((searchParams.get('mockPreview') ?? '').toLowerCase());
  const { t, locale } = useTranslation();
  const { markCompleted } = useGuestTaskProgress(token ?? undefined);
  const inputRef = React.useRef<HTMLInputElement | null>(null);
  const videoRef = React.useRef<HTMLVideoElement | null>(null);
  const streamRef = React.useRef<MediaStream | null>(null);
  const mockPreviewTimerRef = React.useRef<number | null>(null);
  const [uploading, setUploading] = React.useState<{ name: string; progress: number } | null>(null);
  const [error, setError] = React.useState<string | null>(null);
  const [uploadDialog, setUploadDialog] = React.useState<UploadErrorDialog | null>(null);
  const [cameraState, setCameraState] = React.useState<'idle' | 'starting' | 'ready' | 'denied' | 'blocked' | 'unsupported' | 'error' | 'preview'>('idle');
  const [facingMode, setFacingMode] = React.useState<'user' | 'environment'>('environment');
  const [mirror, setMirror] = React.useState(true);
  const [flashPreferred, setFlashPreferred] = React.useState(false);
  const [previewFile, setPreviewFile] = React.useState<File | null>(null);
  const [previewUrl, setPreviewUrl] = React.useState<string | null>(null);
  const { isDark } = useGuestThemeVariant();
  const cardBorder = isDark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(15, 23, 42, 0.12)';
  const cardShadow = isDark ? '0 18px 40px rgba(2, 6, 23, 0.4)' : '0 16px 30px rgba(15, 23, 42, 0.12)';
  const iconColor = isDark ? '#F8FAFF' : '#0F172A';
  const mutedButton = isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(15, 23, 42, 0.06)';
  const mutedButtonBorder = isDark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(15, 23, 42, 0.12)';
  const fabShadow = isDark
    ? '0 20px 40px rgba(255, 79, 216, 0.38), 0 0 0 8px rgba(255, 79, 216, 0.16)'
    : '0 18px 32px rgba(15, 23, 42, 0.2), 0 0 0 8px rgba(255, 255, 255, 0.7)';
  const accessoryShadow = isDark ? '0 10px 20px rgba(2, 6, 23, 0.45)' : '0 8px 16px rgba(15, 23, 42, 0.14)';
  const autoApprove = event?.guest_upload_visibility === 'immediate';
  const isExpanded = cameraState === 'ready' || cameraState === 'starting' || cameraState === 'preview';

  const queueCount = items.filter((item) => item.status !== 'done').length;
  const sendingCount = items.filter((item) => item.status === 'uploading').length;
  const taskIdParam = searchParams.get('taskId');
  const parsedTaskId = taskIdParam ? Number(taskIdParam) : NaN;
  const taskId = Number.isFinite(parsedTaskId) ? parsedTaskId : undefined;
  const [task, setTask] = React.useState<TaskItem | null>(null);
  const [taskLoading, setTaskLoading] = React.useState(false);
  const [taskError, setTaskError] = React.useState<string | null>(null);
  const [pendingItems, setPendingItems] = React.useState<PendingUpload[]>([]);
  const [pendingCount, setPendingCount] = React.useState(0);
  const [pendingLoading, setPendingLoading] = React.useState(false);

  const previewQueueItems = React.useMemo(() => items.filter((item) => item.status !== 'done').slice(0, 3), [items]);
  const previewQueueUrls = React.useMemo(() => {
    if (typeof URL === 'undefined' || typeof URL.createObjectURL !== 'function') {
      return [];
    }

    return previewQueueItems.map((item) => ({
      key: item.id ?? item.fileName,
      url: URL.createObjectURL(item.blob),
    }));
  }, [previewQueueItems]);

  React.useEffect(() => {
    return () => {
      previewQueueUrls.forEach((item) => URL.revokeObjectURL(item.url));
    };
  }, [previewQueueUrls]);

  const loadPending = React.useCallback(async () => {
    if (!token) {
      setPendingItems([]);
      setPendingCount(0);
      return;
    }
    setPendingLoading(true);
    try {
      const result = await fetchPendingUploadsSummary(token, 3);
      setPendingItems(result.items);
      setPendingCount(result.totalCount);
    } catch (err) {
      console.error('Pending uploads load failed', err);
      setPendingItems([]);
      setPendingCount(0);
    } finally {
      setPendingLoading(false);
    }
  }, [token]);

  React.useEffect(() => {
    void loadPending();
  }, [loadPending]);

  React.useEffect(() => {
    let active = true;

    if (!token || !taskId) {
      setTask(null);
      setTaskLoading(false);
      setTaskError(null);
      return;
    }

    setTaskLoading(true);
    setTaskError(null);

    fetchTasks(token, { locale })
      .then((tasks) => {
        if (!active) return;
        const match = tasks.find((item) => {
          const id = item?.id ?? item?.task_id ?? item?.slug;
          return String(id) === String(taskId);
        }) ?? null;
        setTask(match);
        setTaskLoading(false);
      })
      .catch((err) => {
        if (!active) return;
        setTaskError(err instanceof Error ? err.message : t('tasks.error', 'Tasks could not be loaded.'));
        setTask(null);
        setTaskLoading(false);
      });

    return () => {
      active = false;
    };
  }, [locale, t, taskId, token]);

  const enqueueFile = React.useCallback(
    async (file: File) => {
      if (!token) return;
      await add({ eventToken: token, fileName: file.name, blob: file, task_id: taskId ?? null });
    },
    [add, taskId, token]
  );

  const triggerConfetti = React.useCallback(async () => {
    if (typeof window === 'undefined') return;
    const prefersReducedMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)')?.matches;
    if (prefersReducedMotion) return;

    try {
      const { default: confetti } = await import('canvas-confetti');
      confetti({
        particleCount: 70,
        spread: 65,
        origin: { x: 0.5, y: 0.35 },
        ticks: 160,
        scalar: 0.9,
      });
    } catch (error) {
      console.warn('Confetti could not start', error);
    }
  }, []);

  const uploadFiles = React.useCallback(
    async (files: File[]) => {
      if (!token || files.length === 0) return;
      if (files.length === 0) {
        setError(t('uploadV2.errors.invalidFile', 'Please choose a photo file.'));
        return;
      }

      setError(null);
      setUploadDialog(null);

      for (const file of files) {
        if (!navigator.onLine) {
          await enqueueFile(file);
          pushGuestToast({ text: t('uploadV2.toast.queued', 'Offline — added to upload queue.'), type: 'info' });
          continue;
        }

        try {
          setUploading({ name: file.name, progress: 0 });
          await uploadPhoto(token, file, taskId, undefined, {
            guestName: identity?.name ?? undefined,
            onProgress: (percent) => {
              setUploading((prev) => (prev ? { ...prev, progress: percent } : prev));
            },
            maxRetries: 1,
          });
          if (taskId) {
            markCompleted(taskId);
          }
          if (autoApprove) {
            void triggerConfetti();
          }
          pushGuestToast({ text: t('uploadV2.toast.uploaded', 'Upload complete.'), type: 'success' });
          void loadPending();
        } catch (err) {
          const uploadErr = err as { code?: string; meta?: Record<string, unknown> };
          console.error('Upload failed, enqueueing', err);
          setUploadDialog(resolveUploadErrorDialog(uploadErr?.code, uploadErr?.meta, t));
          await enqueueFile(file);
        } finally {
          setUploading(null);
        }
      }
    },
    [autoApprove, enqueueFile, identity?.name, loadPending, markCompleted, t, taskId, token, triggerConfetti]
  );

  const handleFiles = React.useCallback(
    async (fileList: FileList | null) => {
      if (!fileList) return;
      const files = Array.from(fileList).filter((file) => file.type.startsWith('image/'));
      await uploadFiles(files);
    },
    [uploadFiles]
  );

  const handlePick = React.useCallback(() => {
    inputRef.current?.click();
  }, []);

  const stopCamera = React.useCallback(() => {
    if (mockPreviewTimerRef.current) {
      window.clearTimeout(mockPreviewTimerRef.current);
      mockPreviewTimerRef.current = null;
    }
    if (streamRef.current) {
      streamRef.current.getTracks().forEach((track) => track.stop());
      streamRef.current = null;
    }
    if (videoRef.current) {
      videoRef.current.srcObject = null;
    }
    setCameraState('idle');
  }, []);

  const startCamera = React.useCallback(
    async (modeOverride?: 'user' | 'environment') => {
      if (mockPreviewEnabled) {
        const mode = modeOverride ?? facingMode;
        if (mockPreviewTimerRef.current) {
          window.clearTimeout(mockPreviewTimerRef.current);
        }
        setFacingMode(mode);
        setCameraState('starting');
        mockPreviewTimerRef.current = window.setTimeout(() => {
          setCameraState('ready');
        }, 420);
        return;
      }

      if (!navigator.mediaDevices?.getUserMedia) {
        setCameraState('unsupported');
        return;
      }

      const mode = modeOverride ?? facingMode;
      setCameraState('starting');
      try {
        const stream = await navigator.mediaDevices.getUserMedia({
          video: { facingMode: mode },
          audio: false,
        });
        streamRef.current = stream;
        if (videoRef.current) {
          videoRef.current.srcObject = stream;
          await videoRef.current.play();
        }
        setFacingMode(mode);
        setCameraState('ready');
      } catch (err) {
        const error = err as { name?: string };
        if (error?.name === 'NotAllowedError') {
          setCameraState('denied');
        } else if (error?.name === 'SecurityError') {
          setCameraState('blocked');
        } else if (error?.name === 'NotFoundError') {
          setCameraState('error');
        } else {
          setCameraState('error');
        }
      }
    },
    [facingMode, mockPreviewEnabled]
  );

  const handleSwitchCamera = React.useCallback(async () => {
    const nextMode = facingMode === 'user' ? 'environment' : 'user';
    stopCamera();
    await startCamera(nextMode);
  }, [facingMode, startCamera, stopCamera]);

  const handleToggleFlash = React.useCallback(() => {
    setFlashPreferred((prev) => !prev);
  }, []);

  const handleCapture = React.useCallback(async () => {
    const video = videoRef.current;
    if (!video) return;
    if (cameraState !== 'ready') {
      await startCamera();
      return;
    }

    const width = video.videoWidth;
    const height = video.videoHeight;
    if (!width || !height) {
      setError(t('upload.cameraError.explanation', 'Camera could not be started.'));
      return;
    }

    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    const ctx = canvas.getContext('2d');
    if (!ctx) return;

    if (mirror && facingMode === 'user') {
      ctx.translate(width, 0);
      ctx.scale(-1, 1);
    }
    ctx.drawImage(video, 0, 0, width, height);

    const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, 'image/jpeg', 0.92));
    if (!blob) {
      setError(t('uploadV2.errors.invalidFile', 'Please choose a photo file.'));
      return;
    }
    const file = new File([blob], `camera-${Date.now()}.jpg`, { type: blob.type });
    const url = URL.createObjectURL(file);
    setPreviewFile(file);
    setPreviewUrl(url);
    stopCamera();
    setCameraState('preview');
  }, [cameraState, facingMode, mirror, startCamera, stopCamera, t]);

  React.useEffect(() => {
    if (!mockPreviewEnabled) return;
    void startCamera();
    return () => {
      if (mockPreviewTimerRef.current) {
        window.clearTimeout(mockPreviewTimerRef.current);
        mockPreviewTimerRef.current = null;
      }
    };
  }, [mockPreviewEnabled, startCamera]);

  const handleRetake = React.useCallback(async () => {
    if (previewUrl) {
      URL.revokeObjectURL(previewUrl);
    }
    setPreviewFile(null);
    setPreviewUrl(null);
    await startCamera();
  }, [previewUrl, startCamera]);

  const handleUseImage = React.useCallback(async () => {
    if (!previewFile) return;
    await uploadFiles([previewFile]);
    if (previewUrl) {
      URL.revokeObjectURL(previewUrl);
    }
    setPreviewFile(null);
    setPreviewUrl(null);
    setCameraState('idle');
  }, [previewFile, previewUrl, uploadFiles]);

  const handleAbortCamera = React.useCallback(() => {
    if (previewUrl) {
      URL.revokeObjectURL(previewUrl);
    }
    setPreviewFile(null);
    setPreviewUrl(null);
    stopCamera();
  }, [previewUrl, stopCamera]);

  React.useEffect(() => {
    return () => stopCamera();
  }, [stopCamera]);

  if (!token) {
    return (
      <AppShell>
        <YStack padding="$4" borderRadius="$card" backgroundColor="$surface">
          <Text fontSize="$4" fontWeight="$7">
            {t('uploadV2.errors.eventMissing', 'Event not found')}
          </Text>
        </YStack>
      </AppShell>
    );
  }

  return (
    <AppShell>
      <YStack gap="$4">
        {taskId ? (
          <SurfaceCard>
            <XStack alignItems="center" gap="$2">
              <Sparkles size={18} color={iconColor} />
              <Text fontSize="$3" fontWeight="$7">
                {t('tasks.page.title', 'Your next task')}
              </Text>
            </XStack>
            {taskLoading ? (
              <Text fontSize="$2" color="$color" opacity={0.7} marginTop="$2">
                {t('tasks.loading', 'Loading tasks...')}
              </Text>
            ) : task ? (
              <>
                <Text fontSize="$4" fontWeight="$7" marginTop="$3">
                  {getTaskValue(task, 'title') ?? getTaskValue(task, 'name') ?? t('tasks.page.title', 'Task')}
                </Text>
                {getTaskValue(task, 'description') ?? getTaskValue(task, 'prompt') ? (
                  <Text fontSize="$2" color="$color" opacity={0.7} marginTop="$2">
                    {getTaskValue(task, 'description') ?? getTaskValue(task, 'prompt')}
                  </Text>
                ) : null}
              </>
            ) : taskError ? (
              <Text fontSize="$2" color="$color" opacity={0.7} marginTop="$2">
                {taskError}
              </Text>
            ) : null}
          </SurfaceCard>
        ) : null}
        <YStack
          borderRadius="$card"
          backgroundColor="$muted"
          borderWidth={1}
          borderColor={cardBorder}
          overflow="hidden"
          style={{
            backgroundImage: isDark
              ? 'linear-gradient(135deg, rgba(15, 23, 42, 0.7), rgba(8, 12, 24, 0.9)), radial-gradient(circle at 20% 20%, rgba(255, 79, 216, 0.2), transparent 50%)'
              : 'linear-gradient(135deg, rgba(255, 255, 255, 0.92), rgba(248, 250, 255, 0.82)), radial-gradient(circle at 20% 20%, color-mix(in oklab, var(--guest-primary, #FF5A5F) 16%, white), transparent 60%)',
            boxShadow: isExpanded
              ? isDark
                ? '0 28px 60px rgba(2, 6, 23, 0.55)'
                : '0 22px 44px rgba(15, 23, 42, 0.16)'
              : isDark
                ? '0 22px 40px rgba(2, 6, 23, 0.5)'
                : '0 18px 32px rgba(15, 23, 42, 0.12)',
            borderRadius: isExpanded ? 28 : undefined,
            transition: 'box-shadow 360ms ease, border-radius 360ms ease',
          }}
        >
          <YStack
            position="relative"
            alignItems="center"
            justifyContent="center"
            style={{
              height: isExpanded ? 'min(84vh, 720px)' : 320,
              transition: 'height 360ms cubic-bezier(0.22, 0.61, 0.36, 1)',
            }}
          >
            {previewUrl ? (
              <img
                src={previewUrl}
                alt={t('upload.preview.imageAlt', 'Captured photo')}
                style={{
                  width: '100%',
                  height: '100%',
                  objectFit: 'cover',
                }}
              />
            ) : (
              <video
                ref={videoRef}
                playsInline
                muted
                style={{
                  width: '100%',
                  height: '100%',
                  objectFit: 'cover',
                  opacity: cameraState === 'ready' ? 1 : 0,
                  transform: mirror && facingMode === 'user' ? 'scaleX(-1)' : 'none',
                  transition: 'opacity 200ms ease',
                }}
              />
            )}
            {cameraState === 'ready' && !previewUrl ? (
              <XStack
                position="absolute"
                bottom="$4"
                left="$4"
                right="$4"
                alignItems="center"
                justifyContent="space-between"
              >
                <Button
                  size="$3"
                  circular
                  backgroundColor={flashPreferred ? '$primary' : mutedButton}
                  borderWidth={1}
                  borderColor={flashPreferred ? 'rgba(255,255,255,0.25)' : mutedButtonBorder}
                  onPress={handleToggleFlash}
                  disabled={facingMode !== 'environment'}
                  style={{ boxShadow: accessoryShadow }}
                  aria-label={t('upload.controls.toggleFlash', 'Toggle flash')}
                >
                  {flashPreferred ? (
                    <Zap size={18} color="#FFFFFF" />
                  ) : (
                    <ZapOff size={18} color={iconColor} />
                  )}
                </Button>
                <Button
                  size="$5"
                  circular
                  backgroundColor="$primary"
                  onPress={handleCapture}
                  width={90}
                  height={90}
                  borderRadius={999}
                  shadowColor={isDark ? 'rgba(255, 79, 216, 0.5)' : 'rgba(15, 23, 42, 0.2)'}
                  shadowOpacity={0.5}
                  shadowRadius={22}
                  shadowOffset={{ width: 0, height: 10 }}
                  style={{ boxShadow: fabShadow }}
                  aria-label={t('upload.captureButton', 'Capture')}
                >
                  <Camera size={34} color="#FFFFFF" />
                </Button>
                <Button
                  size="$3"
                  circular
                  backgroundColor={mutedButton}
                  borderWidth={1}
                  borderColor={mutedButtonBorder}
                  onPress={handleSwitchCamera}
                  style={{ boxShadow: accessoryShadow }}
                  aria-label={t('upload.controls.switchCamera', 'Switch camera')}
                >
                  <RefreshCcw size={18} color={iconColor} />
                </Button>
              </XStack>
            ) : null}
            {(cameraState === 'ready' || cameraState === 'starting' || cameraState === 'preview') ? (
              <Button
                size="$3"
                circular
                position="absolute"
                top="$3"
                right="$3"
                backgroundColor={isDark ? 'rgba(15, 23, 42, 0.6)' : 'rgba(255, 255, 255, 0.8)'}
                borderWidth={1}
                borderColor={cardBorder}
                onPress={handleAbortCamera}
                aria-label={t('common.actions.close', 'Close')}
              >
                <X size={16} color={iconColor} />
              </Button>
            ) : null}
            {cameraState !== 'ready' && cameraState !== 'preview' ? (
              <YStack alignItems="center" gap="$2" padding="$4">
                <Camera size={32} color={isDark ? '#F8FAFF' : '#0F172A'} />
                <Text fontSize="$4" fontWeight="$7" textAlign="center">
                  {cameraState === 'unsupported'
                    ? t('upload.cameraUnsupported.title', 'Camera not available')
                    : cameraState === 'blocked'
                      ? t('upload.cameraBlocked.title', 'Camera blocked')
                      : cameraState === 'denied'
                        ? t('upload.cameraDenied.title', 'Camera access denied')
                        : t('upload.cameraTitle', 'Camera')}
                </Text>
                <Text fontSize="$2" color="$color" opacity={0.7} textAlign="center">
                  {cameraState === 'unsupported'
                    ? t('upload.cameraUnsupported.message', 'Your device does not support live camera preview in this browser.')
                    : cameraState === 'blocked'
                      ? t('upload.cameraBlocked.message', 'Camera access is blocked by the site security policy.')
                      : cameraState === 'denied'
                        ? t('upload.cameraDenied.prompt', 'We need access to your camera. Allow the request or pick a photo from your gallery.')
                        : t('uploadV2.preview.subtitle', 'Stay in the flow – keep the camera ready.')}
                </Text>
                <Button
                  size="$3"
                  borderRadius="$pill"
                  backgroundColor="$primary"
                  onPress={() => startCamera()}
                  disabled={cameraState === 'unsupported' || cameraState === 'blocked'}
                >
                  {t('upload.buttons.startCamera', 'Start camera')}
                </Button>
              </YStack>
            ) : null}
          </YStack>
          <XStack
            gap="$2"
            padding={isExpanded ? '$2' : '$3'}
            alignItems="center"
            justifyContent="space-between"
            borderTopWidth={1}
            borderColor={cardBorder}
            backgroundColor={isDark ? 'rgba(10, 14, 28, 0.7)' : 'rgba(255, 255, 255, 0.75)'}
          >
            {cameraState === 'preview' ? (
              <XStack gap="$2" flex={1} justifyContent="space-between">
                <Button
                  size="$3"
                  borderRadius="$pill"
                  backgroundColor={mutedButton}
                  borderWidth={1}
                  borderColor={mutedButtonBorder}
                  onPress={handleRetake}
                  flex={1}
                  justifyContent="center"
                >
                  <Text fontSize="$2" fontWeight="$6">
                    {t('upload.review.retake', 'Retake')}
                  </Text>
                </Button>
                <Button
                  size="$3"
                  borderRadius="$pill"
                  backgroundColor="$primary"
                  onPress={handleUseImage}
                  flex={1}
                  justifyContent="center"
                >
                  <Text fontSize="$2" fontWeight="$6" color="#FFFFFF">
                    {t('upload.review.keep', 'Use image')}
                  </Text>
                </Button>
              </XStack>
            ) : (
              <>
                <Button
                  size="$3"
                  borderRadius="$pill"
                  backgroundColor={mutedButton}
                  borderWidth={1}
                  borderColor={mutedButtonBorder}
                  onPress={handlePick}
                  paddingHorizontal="$4"
                  gap="$2"
                  alignSelf="center"
                  flexShrink={0}
                  justifyContent="center"
                >
                  <Image size={16} color={iconColor} />
                  <Text fontSize="$2" fontWeight="$6">
                    {t('uploadV2.galleryCta', 'Upload from gallery')}
                  </Text>
                </Button>
                {facingMode === 'user' ? (
                  <Button
                    size="$3"
                    circular
                    backgroundColor={mirror ? '$primary' : mutedButton}
                    borderWidth={1}
                    borderColor={mutedButtonBorder}
                    onPress={() => setMirror((prev) => !prev)}
                  >
                    <FlipHorizontal size={16} color={mirror ? '#FFFFFF' : iconColor} />
                  </Button>
                ) : null}
              </>
            )}
          </XStack>
        </YStack>

        <YStack
          padding="$4"
          borderRadius="$card"
          backgroundColor="$surface"
          borderWidth={1}
          borderColor={cardBorder}
          gap="$2"
          style={{
            boxShadow: cardShadow,
          }}
        >
          <Text fontSize="$4" fontWeight="$7">
            {t('uploadQueue.title', 'Uploads')}
          </Text>
          <Text fontSize="$2" color="$color" opacity={0.7}>
            {t('uploadV2.queue.summary', { waiting: queueCount, sending: sendingCount }, '{waiting} waiting, {sending} sending')}
          </Text>
          {uploading ? (
            <Text fontSize="$2" color="$color" opacity={0.7}>
              {t(
                'uploadV2.queue.uploading',
                { name: uploading.name, progress: uploading.progress },
                'Uploading {name} · {progress}%'
              )}
            </Text>
          ) : null}
          {uploadDialog ? (
            <YStack gap="$1">
              <Text fontSize="$3" fontWeight="$7" color={uploadDialog.tone === 'danger' ? '#FCA5A5' : '$color'}>
                {uploadDialog.title}
              </Text>
              <Text fontSize="$2" color="$color" opacity={0.7}>
                {uploadDialog.description}
              </Text>
              {uploadDialog.hint ? (
                <Text fontSize="$2" color="$color" opacity={0.6}>
                  {uploadDialog.hint}
                </Text>
              ) : null}
            </YStack>
          ) : null}
          {error ? (
            <Text fontSize="$2" color="#FCA5A5">
              {error}
            </Text>
          ) : null}
          <XStack gap="$2">
            {previewQueueUrls.length > 0 ? (
              previewQueueUrls.map((item) => (
                <YStack
                  key={item.key}
                  flex={1}
                  height={72}
                  borderRadius="$tile"
                  backgroundColor="$muted"
                  borderWidth={1}
                  borderColor={cardBorder}
                  overflow="hidden"
                >
                  <img src={item.url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
                </YStack>
              ))
            ) : (
              <YStack flex={1} height={72} borderRadius="$tile" backgroundColor="$muted" borderWidth={1} borderColor={cardBorder} />
            )}
          </XStack>
          <YStack gap="$1">
            <Text fontSize="$3" fontWeight="$7">
              {t('pendingUploads.title', 'Pending uploads')}
            </Text>
            <Text fontSize="$2" color="$color" opacity={0.7}>
              {pendingLoading
                ? t('pendingUploads.loading', 'Loading uploads...')
                : t('pendingUploads.subtitle', 'Your photos are waiting for approval.')}
            </Text>
            {pendingItems.length > 0 ? (
              <XStack gap="$2">
                {pendingItems.map((photo) => (
                  <YStack
                    key={photo.id}
                    flex={1}
                    height={72}
                    borderRadius="$tile"
                    backgroundColor="$muted"
                    borderWidth={1}
                    borderColor={cardBorder}
                    overflow="hidden"
                    alignItems="center"
                    justifyContent="center"
                  >
                    {photo.thumbnail_url ? (
                      <img src={photo.thumbnail_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
                    ) : (
                      <UploadCloud size={20} color={iconColor} />
                    )}
                  </YStack>
                ))}
              </XStack>
            ) : (
              <Text fontSize="$2" color="$color" opacity={0.6}>
                {t('pendingUploads.emptyTitle', 'No pending uploads')}
              </Text>
            )}
          </YStack>
          <Button
            size="$3"
            borderRadius="$pill"
            backgroundColor={mutedButton}
            borderWidth={1}
            borderColor={mutedButtonBorder}
            onPress={() => navigate('../queue')}
            alignSelf="flex-start"
            width="auto"
            paddingHorizontal="$3"
            gap="$2"
            justifyContent="flex-start"
          >
            <ListVideo size={16} color={iconColor} />
            <Text fontSize="$2" fontWeight="$6">
              {t('uploadV2.queue.button', 'Queue')}
            </Text>
          </Button>
        </YStack>
      </YStack>
      <input
        ref={inputRef}
        type="file"
        accept="image/*"
        multiple
        style={{ display: 'none' }}
        onChange={(event) => {
          handleFiles(event.target.files);
          if (event.target) {
            event.target.value = '';
          }
        }}
      />
    </AppShell>
  );
}
