import React from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Image as ImageIcon, RefreshCcw, Settings } from 'lucide-react';
import { YStack, XStack } from '@tamagui/stacks';
import { SizableText as Text } from '@tamagui/text';
import { Pressable } from '@tamagui/react-native-web-lite';
import { MobileShell, HeaderActionButton } from './components/MobileShell';
import { MobileCard, CTAButton, PillBadge, SkeletonCard } from './components/Primitives';
import { MobileField, MobileSelect } from './components/FormControls';
import { useEventContext } from '../context/EventContext';
import {
  approveAndLiveShowPhoto,
  approveLiveShowPhoto,
  clearLiveShowPhoto,
  createEventAddonCheckout,
  EventAddonCatalogItem,
  EventLimitSummary,
  getAddonCatalog,
  featurePhoto,
  getEventPhotos,
  getEvents,
  getLiveShowQueue,
  LiveShowQueueStatus,
  rejectLiveShowPhoto,
  TenantEvent,
  TenantPhoto,
  unfeaturePhoto,
  updatePhotoStatus,
  updatePhotoVisibility,
} from '../api';
import { isAuthError } from '../auth/tokens';
import { getApiErrorMessage } from '../lib/apiError';
import { adminPath } from '../constants';
import toast from 'react-hot-toast';
import { useBackNavigation } from './hooks/useBackNavigation';
import { useAdminTheme } from './theme';
import { useOnlineStatus } from './hooks/useOnlineStatus';
import {
  enqueuePhotoAction,
  loadPhotoQueue,
  removePhotoAction,
  replacePhotoQueue,
  type PhotoModerationAction,
} from './lib/photoModerationQueue';
import { triggerHaptic } from './lib/haptics';
import { normalizeLiveStatus, resolveLiveShowApproveMode, resolveStatusTone } from './lib/controlRoom';
import { LegalConsentSheet } from './components/LegalConsentSheet';
import { selectAddonKeyForScope } from './addons';
import { LimitWarnings } from './components/LimitWarnings';

type ModerationFilter = 'all' | 'featured' | 'hidden' | 'pending';

const MODERATION_FILTERS: Array<{ value: ModerationFilter; labelKey: string; fallback: string }> = [
  { value: 'pending', labelKey: 'photos.filters.pending', fallback: 'Pending' },
  { value: 'all', labelKey: 'photos.filters.all', fallback: 'All' },
  { value: 'featured', labelKey: 'photos.filters.featured', fallback: 'Featured' },
  { value: 'hidden', labelKey: 'photos.filters.hidden', fallback: 'Hidden' },
];

const LIVE_STATUS_OPTIONS: Array<{ value: LiveShowQueueStatus; labelKey: string; fallback: string }> = [
  { value: 'pending', labelKey: 'liveShowQueue.statusPending', fallback: 'Pending' },
  { value: 'approved', labelKey: 'liveShowQueue.statusApproved', fallback: 'Approved' },
  { value: 'rejected', labelKey: 'liveShowQueue.statusRejected', fallback: 'Rejected' },
  { value: 'none', labelKey: 'liveShowQueue.statusNone', fallback: 'Not queued' },
];

type LimitTranslator = (key: string, options?: Record<string, unknown>) => string;

function translateLimits(t: (key: string, defaultValue?: string, options?: Record<string, unknown>) => string): LimitTranslator {
  const defaults: Record<string, string> = {
    photosBlocked: 'Upload limit reached. Buy more photos to continue.',
    photosWarning: '{{remaining}} of {{limit}} photos remaining.',
    guestsBlocked: 'Guest limit reached.',
    guestsWarning: '{{remaining}} of {{limit}} guests remaining.',
    galleryExpired: 'Gallery expired. Extend to keep it online.',
    galleryWarningHour: 'Gallery expires in {{hours}} hour.',
    galleryWarningHours: 'Gallery expires in {{hours}} hours.',
    galleryWarningDay: 'Gallery expires in {{days}} day.',
    galleryWarningDays: 'Gallery expires in {{days}} days.',
    buyMorePhotos: 'Buy more photos',
    extendGallery: 'Extend gallery',
    buyMoreGuests: 'Add more guests',
  };
  return (key, options) => t(`limits.${key}`, defaults[key] ?? key, options);
}

export default function MobileEventControlRoomPage() {
  const { slug: slugParam } = useParams<{ slug?: string }>();
  const navigate = useNavigate();
  const location = useLocation();
  const { t } = useTranslation('management');
  const { activeEvent, selectEvent } = useEventContext();
  const slug = slugParam ?? activeEvent?.slug ?? null;
  const online = useOnlineStatus();
  const { textStrong, text, muted, border, accentSoft, accent, danger } = useAdminTheme();
  const [activeTab, setActiveTab] = React.useState<'moderation' | 'live'>('moderation');

  const [moderationPhotos, setModerationPhotos] = React.useState<TenantPhoto[]>([]);
  const [moderationFilter, setModerationFilter] = React.useState<ModerationFilter>('pending');
  const [moderationPage, setModerationPage] = React.useState(1);
  const [moderationHasMore, setModerationHasMore] = React.useState(false);
  const [moderationLoading, setModerationLoading] = React.useState(true);
  const [moderationError, setModerationError] = React.useState<string | null>(null);
  const [moderationBusyId, setModerationBusyId] = React.useState<number | null>(null);
  const [limits, setLimits] = React.useState<EventLimitSummary | null>(null);
  const [catalogAddons, setCatalogAddons] = React.useState<EventAddonCatalogItem[]>([]);
  const [busyScope, setBusyScope] = React.useState<string | null>(null);
  const [consentOpen, setConsentOpen] = React.useState(false);
  const [consentTarget, setConsentTarget] = React.useState<{ scope: 'photos' | 'gallery' | 'guests'; addonKey: string } | null>(null);
  const [consentBusy, setConsentBusy] = React.useState(false);

  const [livePhotos, setLivePhotos] = React.useState<TenantPhoto[]>([]);
  const [liveStatusFilter, setLiveStatusFilter] = React.useState<LiveShowQueueStatus>('pending');
  const [livePage, setLivePage] = React.useState(1);
  const [liveHasMore, setLiveHasMore] = React.useState(false);
  const [liveLoading, setLiveLoading] = React.useState(true);
  const [liveError, setLiveError] = React.useState<string | null>(null);
  const [liveBusyId, setLiveBusyId] = React.useState<number | null>(null);

  const [queuedActions, setQueuedActions] = React.useState<PhotoModerationAction[]>(() => loadPhotoQueue());
  const [syncingQueue, setSyncingQueue] = React.useState(false);
  const syncingQueueRef = React.useRef(false);
  const moderationResetRef = React.useRef(false);
  const liveResetRef = React.useRef(false);
  const [fallbackAttempted, setFallbackAttempted] = React.useState(false);
  const back = useBackNavigation(slug ? adminPath(`/mobile/events/${slug}`) : adminPath('/mobile/events'));
  const infoBg = accentSoft;
  const infoBorder = accent;

  React.useEffect(() => {
    if (slugParam && activeEvent?.slug !== slugParam) {
      selectEvent(slugParam);
    }
  }, [slugParam, activeEvent?.slug, selectEvent]);

  const ensureSlug = React.useCallback(async () => {
    if (slug) {
      return slug;
    }
    if (fallbackAttempted) {
      return null;
    }
    setFallbackAttempted(true);
    try {
      const events = await getEvents({ force: true });
      const first = events[0] as TenantEvent | undefined;
      if (first?.slug) {
        selectEvent(first.slug);
        navigate(adminPath(`/mobile/events/${first.slug}/control-room`), { replace: true });
        return first.slug;
      }
    } catch {
      // ignore
    }
    return null;
  }, [slug, fallbackAttempted, navigate, selectEvent]);

  React.useEffect(() => {
    setModerationPage(1);
  }, [moderationFilter, slug]);

  React.useEffect(() => {
    setLivePage(1);
  }, [liveStatusFilter, slug]);

  React.useEffect(() => {
    if (activeTab === 'moderation') {
      moderationResetRef.current = true;
      setModerationPhotos([]);
      setModerationPage(1);
    } else {
      liveResetRef.current = true;
      setLivePhotos([]);
      setLivePage(1);
    }
  }, [activeTab]);

  const loadModeration = React.useCallback(async () => {
    const resolvedSlug = await ensureSlug();
    if (!resolvedSlug) {
      setModerationLoading(false);
      setModerationError(t('events.errors.missingSlug', 'No event selected.'));
      return;
    }

    setModerationLoading(true);
    setModerationError(null);
    try {
      const status =
        moderationFilter === 'hidden'
          ? 'hidden'
          : moderationFilter === 'pending'
            ? 'pending'
            : undefined;
      const result = await getEventPhotos(resolvedSlug, {
        page: moderationPage,
        perPage: 20,
        sort: 'desc',
        featured: moderationFilter === 'featured',
        status,
      });
      setModerationPhotos((prev) => (moderationPage === 1 ? result.photos : [...prev, ...result.photos]));
      setLimits(result.limits ?? null);
      const lastPage = result.meta?.last_page ?? 1;
      setModerationHasMore(moderationPage < lastPage);
      const addons = await getAddonCatalog().catch(() => [] as EventAddonCatalogItem[]);
      setCatalogAddons(addons ?? []);
    } catch (err) {
      if (!isAuthError(err)) {
        const message = getApiErrorMessage(err, t('mobilePhotos.loadFailed', 'Photos could not be loaded.'));
        setModerationError(message);
      }
    } finally {
      setModerationLoading(false);
    }
  }, [ensureSlug, moderationFilter, moderationPage, t]);

  const loadLiveQueue = React.useCallback(async () => {
    const resolvedSlug = await ensureSlug();
    if (!resolvedSlug) {
      setLiveLoading(false);
      setLiveError(t('events.errors.missingSlug', 'No event selected.'));
      return;
    }

    setLiveLoading(true);
    setLiveError(null);
    try {
      const result = await getLiveShowQueue(resolvedSlug, {
        page: livePage,
        perPage: 20,
        liveStatus: liveStatusFilter,
      });
      setLivePhotos((prev) => (livePage === 1 ? result.photos : [...prev, ...result.photos]));
      const lastPage = result.meta?.last_page ?? 1;
      setLiveHasMore(livePage < lastPage);
    } catch (err) {
      if (!isAuthError(err)) {
        const message = getApiErrorMessage(err, t('liveShowQueue.loadFailed', 'Live Show queue could not be loaded.'));
        setLiveError(message);
        toast.error(message);
      }
    } finally {
      setLiveLoading(false);
    }
  }, [ensureSlug, livePage, liveStatusFilter, t]);

  React.useEffect(() => {
    if (activeTab === 'moderation') {
      if (moderationResetRef.current && moderationPage !== 1) {
        return;
      }
      moderationResetRef.current = false;
      void loadModeration();
    }
  }, [activeTab, loadModeration, moderationPage]);

  React.useEffect(() => {
    if (activeTab === 'live') {
      if (liveResetRef.current && livePage !== 1) {
        return;
      }
      liveResetRef.current = false;
      void loadLiveQueue();
    }
  }, [activeTab, loadLiveQueue, livePage]);

  React.useEffect(() => {
    if (!location.search || !slug) {
      return;
    }
    const params = new URLSearchParams(location.search);
    if (params.get('addon_success')) {
      toast.success(t('mobileBilling.addonApplied', 'Add-on applied. Limits update shortly.'));
      setModerationPage(1);
      void loadModeration();
      params.delete('addon_success');
      navigate({ pathname: location.pathname, search: params.toString() ? `?${params.toString()}` : '' }, { replace: true });
    }
  }, [location.search, slug, loadModeration, navigate, t, location.pathname]);

  const updateQueueState = React.useCallback((queue: PhotoModerationAction[]) => {
    replacePhotoQueue(queue);
    setQueuedActions(queue);
  }, []);

  const applyOptimisticUpdate = React.useCallback((photoId: number, action: PhotoModerationAction['action']) => {
    setModerationPhotos((prev) =>
      prev.map((photo) => {
        if (photo.id !== photoId) {
          return photo;
        }

        if (action === 'approve') {
          return { ...photo, status: 'approved' };
        }

        if (action === 'hide') {
          return { ...photo, status: 'hidden' };
        }

        if (action === 'show') {
          return { ...photo, status: 'approved' };
        }

        return photo;
      }),
    );
  }, []);

  const enqueueModerationAction = React.useCallback(
    (action: PhotoModerationAction['action'], photoId: number) => {
      if (!slug) {
        return;
      }
      const nextQueue = enqueuePhotoAction({ eventSlug: slug, photoId, action });
      setQueuedActions(nextQueue);
      applyOptimisticUpdate(photoId, action);
      toast.success(t('mobilePhotos.queued', 'Action saved. Syncs when you are back online.'));
      triggerHaptic('selection');
    },
    [applyOptimisticUpdate, slug, t],
  );

  const syncQueuedActions = React.useCallback(async () => {
    if (!online || syncingQueueRef.current) {
      return;
    }

    const queue = loadPhotoQueue();
    if (queue.length === 0) {
      return;
    }

    syncingQueueRef.current = true;
    setSyncingQueue(true);

    let remaining = queue;

      for (const entry of queue) {
        try {
          let updated: TenantPhoto | null = null;
          if (entry.action === 'approve') {
            updated = await updatePhotoStatus(entry.eventSlug, entry.photoId, 'approved');
          } else if (entry.action === 'hide') {
            updated = await updatePhotoVisibility(entry.eventSlug, entry.photoId, true);
          } else if (entry.action === 'show') {
            updated = await updatePhotoVisibility(entry.eventSlug, entry.photoId, false);
          } else if (entry.action === 'feature') {
            updated = await featurePhoto(entry.eventSlug, entry.photoId);
          } else if (entry.action === 'unfeature') {
            updated = await unfeaturePhoto(entry.eventSlug, entry.photoId);
          }

        remaining = removePhotoAction(remaining, entry.id);

        if (updated && entry.eventSlug === slug) {
          setModerationPhotos((prev) => prev.map((photo) => (photo.id === updated!.id ? updated! : photo)));
        }
      } catch (err) {
        toast.error(t('mobilePhotos.syncFailed', 'Sync failed. Please try again later.'));
        if (isAuthError(err)) {
          break;
        }
      }
    }

    updateQueueState(remaining);
    setSyncingQueue(false);
    syncingQueueRef.current = false;
  }, [online, slug, t, updateQueueState]);

  React.useEffect(() => {
    if (online) {
      void syncQueuedActions();
    }
  }, [online, syncQueuedActions]);

  const handleModerationAction = React.useCallback(
    async (action: PhotoModerationAction['action'], photo: TenantPhoto) => {
      if (!slug) {
        return;
      }

      if (!online) {
        enqueueModerationAction(action, photo.id);
        return;
      }

      setModerationBusyId(photo.id);

      try {
        let updated: TenantPhoto;
        if (action === 'approve') {
          updated = await updatePhotoStatus(slug, photo.id, 'approved');
        } else if (action === 'hide') {
          updated = await updatePhotoVisibility(slug, photo.id, true);
        } else {
          updated = await updatePhotoVisibility(slug, photo.id, false);
        }

        setModerationPhotos((prev) => prev.map((item) => (item.id === photo.id ? updated : item)));
        triggerHaptic(action === 'approve' ? 'success' : 'medium');
      } catch (err) {
        if (!isAuthError(err)) {
          const message = getApiErrorMessage(err, t('mobilePhotos.visibilityFailed', 'Visibility could not be changed.'));
          setModerationError(message);
          toast.error(message);
        }
      } finally {
        setModerationBusyId(null);
      }
    },
    [enqueueModerationAction, online, slug, t],
  );

  function resolveScopeAndAddonKey(scopeOrKey: 'photos' | 'gallery' | 'guests' | string) {
    const scope =
      scopeOrKey === 'photos' || scopeOrKey === 'gallery' || scopeOrKey === 'guests'
        ? scopeOrKey
        : scopeOrKey.includes('gallery')
          ? 'gallery'
          : scopeOrKey.includes('guest')
            ? 'guests'
            : 'photos';

    const addonKey =
      scopeOrKey === 'photos' || scopeOrKey === 'gallery' || scopeOrKey === 'guests'
        ? selectAddonKeyForScope(catalogAddons, scope)
        : scopeOrKey;

    return { scope, addonKey };
  }

  function startAddonCheckout(scopeOrKey: 'photos' | 'gallery' | 'guests' | string) {
    if (!slug) return;
    const { scope, addonKey } = resolveScopeAndAddonKey(scopeOrKey);
    setConsentTarget({ scope: scope as 'photos' | 'gallery' | 'guests', addonKey });
    setConsentOpen(true);
  }

  async function confirmAddonCheckout(consents: { acceptedTerms: boolean; acceptedWaiver: boolean }) {
    if (!slug || !consentTarget) return;

    const currentUrl = typeof window !== 'undefined'
      ? `${window.location.origin}${adminPath(`/mobile/events/${slug}/control-room`)}`
      : '';
    const successUrl = `${currentUrl}?addon_success=1`;
    setBusyScope(consentTarget.scope);
    setConsentBusy(true);
    try {
      const checkout = await createEventAddonCheckout(slug, {
        addon_key: consentTarget.addonKey,
        quantity: 1,
        success_url: successUrl,
        cancel_url: currentUrl,
        accepted_terms: consents.acceptedTerms,
        accepted_waiver: consents.acceptedWaiver,
      } as any);
      if (checkout.checkout_url) {
        window.location.href = checkout.checkout_url;
      } else {
        toast.error(t('events.errors.checkoutMissing', 'Checkout could not be started.'));
      }
    } catch (err) {
      toast.error(getApiErrorMessage(err, t('events.errors.checkoutFailed', 'Add-on checkout failed.')));
    } finally {
      setConsentBusy(false);
      setConsentOpen(false);
      setConsentTarget(null);
      setBusyScope(null);
    }
  }

  async function handleApprove(photo: TenantPhoto) {
    if (!slug || liveBusyId) return;
    setLiveBusyId(photo.id);
    try {
      const updated = await approveLiveShowPhoto(slug, photo.id);
      setLivePhotos((prev) => prev.map((item) => (item.id === photo.id ? updated : item)));
      toast.success(t('liveShowQueue.approveSuccess', 'Photo approved for Live Show'));
    } catch (err) {
      if (!isAuthError(err)) {
        toast.error(t('liveShowQueue.actionFailed', 'Live Show update failed.'));
      }
    } finally {
      setLiveBusyId(null);
    }
  }

  async function handleApproveAndLive(photo: TenantPhoto) {
    if (!slug || liveBusyId) return;
    setLiveBusyId(photo.id);
    try {
      const updated = await approveAndLiveShowPhoto(slug, photo.id);
      setLivePhotos((prev) => prev.map((item) => (item.id === photo.id ? updated : item)));
      toast.success(t('liveShowQueue.approveAndLiveSuccess', 'Photo approved and added to Live Show'));
    } catch (err) {
      if (!isAuthError(err)) {
        toast.error(t('liveShowQueue.actionFailed', 'Live Show update failed.'));
      }
    } finally {
      setLiveBusyId(null);
    }
  }

  async function handleReject(photo: TenantPhoto) {
    if (!slug || liveBusyId) return;
    setLiveBusyId(photo.id);
    try {
      const updated = await rejectLiveShowPhoto(slug, photo.id);
      setLivePhotos((prev) => prev.map((item) => (item.id === photo.id ? updated : item)));
      toast.success(t('liveShowQueue.rejectSuccess', 'Photo removed from Live Show'));
    } catch (err) {
      if (!isAuthError(err)) {
        toast.error(t('liveShowQueue.actionFailed', 'Live Show update failed.'));
      }
    } finally {
      setLiveBusyId(null);
    }
  }

  async function handleClear(photo: TenantPhoto) {
    if (!slug || liveBusyId) return;
    setLiveBusyId(photo.id);
    try {
      const updated = await clearLiveShowPhoto(slug, photo.id);
      setLivePhotos((prev) => prev.map((item) => (item.id === photo.id ? updated : item)));
      toast.success(t('liveShowQueue.clearSuccess', 'Live Show approval removed'));
    } catch (err) {
      if (!isAuthError(err)) {
        toast.error(t('liveShowQueue.actionFailed', 'Live Show update failed.'));
      }
    } finally {
      setLiveBusyId(null);
    }
  }

  function resolveGalleryLabel(status?: string | null): string {
    const key = status ?? 'pending';
    const fallbackMap: Record<string, string> = {
      approved: 'Gallery approved',
      pending: 'Gallery pending',
      rejected: 'Gallery rejected',
      hidden: 'Hidden',
    };
    return t(`liveShowQueue.galleryStatus.${key}`, fallbackMap[key] ?? key);
  }

  function resolveLiveLabel(status?: string | null): string {
    const key = normalizeLiveStatus(status);
    return t(`liveShowQueue.status.${key}`, key);
  }

  const queuedEventCount = React.useMemo(() => {
    if (!slug) {
      return queuedActions.length;
    }
    return queuedActions.filter((action) => action.eventSlug === slug).length;
  }, [queuedActions, slug]);

  const headerActions = (
    <XStack space="$2">
      <HeaderActionButton
        onPress={() => {
          if (activeTab === 'moderation') {
            void loadModeration();
            return;
          }
          void loadLiveQueue();
        }}
        ariaLabel={t('common.refresh', 'Refresh')}
      >
        <RefreshCcw size={18} color={textStrong} />
      </HeaderActionButton>
      {slug ? (
        <HeaderActionButton
          onPress={() => navigate(adminPath(`/mobile/events/${slug}/live-show/settings`))}
          ariaLabel={t('events.quick.liveShowSettings', 'Live Show settings')}
        >
          <Settings size={18} color={textStrong} />
        </HeaderActionButton>
      ) : null}
    </XStack>
  );

  return (
    <MobileShell
      activeTab="uploads"
      title={t('controlRoom.title', 'Moderation & Live Show')}
      subtitle={t('controlRoom.subtitle', 'Review uploads and manage the live slideshow.')}
      onBack={back}
      headerActions={headerActions}
    >
      <XStack space="$2">
        {([
          { key: 'moderation', label: t('controlRoom.tabs.moderation', 'Moderation') },
          { key: 'live', label: t('controlRoom.tabs.live', 'Live Show') },
        ] as const).map((tab) => (
          <Pressable key={tab.key} onPress={() => setActiveTab(tab.key)} style={{ flex: 1 }}>
            <MobileCard
              backgroundColor={activeTab === tab.key ? infoBg : 'transparent'}
              borderColor={activeTab === tab.key ? infoBorder : border}
              padding="$2.5"
            >
              <Text fontSize="$sm" fontWeight="700" textAlign="center" color={textStrong}>
                {tab.label}
              </Text>
            </MobileCard>
          </Pressable>
        ))}
      </XStack>

      {activeTab === 'moderation' ? (
        <YStack space="$2">
          {queuedEventCount > 0 ? (
            <MobileCard>
              <XStack alignItems="center" justifyContent="space-between" space="$2">
                <YStack space="$1" flex={1}>
                  <Text fontSize="$sm" fontWeight="700" color={text}>
                    {t('mobilePhotos.queueTitle', 'Changes waiting to sync')}
                  </Text>
                  <Text fontSize="$xs" color={muted}>
                    {online
                      ? t('mobilePhotos.queueOnline', '{{count}} actions ready to sync.', { count: queuedEventCount })
                      : t('mobilePhotos.queueOffline', '{{count}} actions saved offline.', { count: queuedEventCount })}
                  </Text>
                </YStack>
                <CTAButton
                  label={online ? t('mobilePhotos.queueSync', 'Sync') : t('mobilePhotos.queueWaiting', 'Offline')}
                  onPress={() => syncQueuedActions()}
                  tone="ghost"
                  fullWidth={false}
                  disabled={!online}
                  loading={syncingQueue}
                />
              </XStack>
            </MobileCard>
          ) : null}

          <MobileCard>
            <MobileField label={t('mobilePhotos.filtersTitle', 'Filter')}>
              <MobileSelect
                value={moderationFilter}
                onChange={(event) => setModerationFilter(event.target.value as ModerationFilter)}
              >
                {MODERATION_FILTERS.map((option) => (
                  <option key={option.value} value={option.value}>
                    {t(option.labelKey, option.fallback)}
                  </option>
                ))}
              </MobileSelect>
            </MobileField>
          </MobileCard>

          {!moderationLoading ? (
            <LimitWarnings
              limits={limits}
              addons={catalogAddons}
              onCheckout={startAddonCheckout}
              busyScope={busyScope}
              translate={translateLimits(t as any)}
              textColor={text}
              borderColor={border}
            />
          ) : null}

          {moderationError ? (
            <MobileCard>
              <Text fontWeight="700" color={danger}>
                {moderationError}
              </Text>
            </MobileCard>
          ) : null}

          {moderationLoading && moderationPage === 1 ? (
            <YStack space="$2">
              {Array.from({ length: 3 }).map((_, idx) => (
                <SkeletonCard key={`moderation-skeleton-${idx}`} height={120} />
              ))}
            </YStack>
          ) : moderationPhotos.length === 0 ? (
            <MobileCard alignItems="center" justifyContent="center" space="$2">
              <ImageIcon size={28} color={muted} />
              <Text fontSize="$sm" fontWeight="700" color={text}>
                {t('controlRoom.emptyModeration', 'No uploads match this filter.')}
              </Text>
            </MobileCard>
          ) : (
            <YStack space="$2">
              {moderationPhotos.map((photo) => {
                const isBusy = moderationBusyId === photo.id;
                const galleryStatus = photo.status ?? 'pending';
                const liveStatus = normalizeLiveStatus(photo.live_status);
                const canApprove = galleryStatus === 'pending';
                const canShow = galleryStatus === 'hidden';
                const visibilityAction: PhotoModerationAction['action'] = canShow ? 'show' : 'hide';
                const visibilityLabel = canShow
                  ? t('photos.actions.show', 'Show')
                  : t('photos.actions.hide', 'Hide');
                return (
                  <MobileCard key={photo.id}>
                    <XStack space="$3" alignItems="center">
                      {photo.thumbnail_url ? (
                        <img
                          src={photo.thumbnail_url}
                          alt={photo.original_name ?? 'Photo'}
                          style={{
                            width: 72,
                            height: 72,
                            borderRadius: 14,
                            objectFit: 'cover',
                            border: `1px solid ${border}`,
                          }}
                        />
                      ) : null}
                      <YStack flex={1} space="$2">
                        <Text fontSize="$sm" fontWeight="700" color={text}>
                          {photo.original_name ?? t('common.photo', 'Photo')}
                        </Text>
                        <XStack alignItems="center" space="$2">
                          <PillBadge tone={resolveStatusTone(galleryStatus)}>
                            {resolveGalleryLabel(galleryStatus)}
                          </PillBadge>
                          <PillBadge tone={resolveStatusTone(liveStatus)}>
                            {resolveLiveLabel(liveStatus)}
                          </PillBadge>
                        </XStack>
                      </YStack>
                    </XStack>
                    <XStack space="$2" marginTop="$2">
                      <CTAButton
                        label={t('photos.actions.approve', 'Approve')}
                        onPress={() => handleModerationAction('approve', photo)}
                        disabled={!canApprove}
                        loading={isBusy}
                        tone="primary"
                      />
                      <CTAButton
                        label={visibilityLabel}
                        onPress={() => handleModerationAction(visibilityAction, photo)}
                        disabled={false}
                        loading={isBusy}
                        tone="ghost"
                      />
                    </XStack>
                  </MobileCard>
                );
              })}
            </YStack>
          )}

          {moderationHasMore ? (
            <MobileCard>
              <CTAButton
                label={t('common.loadMore', 'Load more')}
                onPress={() => setModerationPage((prev) => prev + 1)}
                disabled={moderationLoading}
              />
            </MobileCard>
          ) : null}
        </YStack>
      ) : (
        <YStack space="$2">
          <MobileCard borderColor={border} backgroundColor="transparent">
            <Text fontSize="$sm" color={muted}>
              {t(
                'liveShowQueue.galleryApprovedOnly',
                'Gallery and Live Show approvals are separate. Pending photos can be approved here.'
              )}
            </Text>
            {!online ? (
              <Text fontSize="$sm" color={danger}>
                {t('liveShowQueue.offlineNotice', 'You are offline. Live Show actions are disabled.')}
              </Text>
            ) : null}
          </MobileCard>

          <MobileCard>
            <MobileField label={t('liveShowQueue.filterLabel', 'Live status')}>
              <MobileSelect
                value={liveStatusFilter}
                onChange={(event) => setLiveStatusFilter(event.target.value as LiveShowQueueStatus)}
              >
                {LIVE_STATUS_OPTIONS.map((option) => (
                  <option key={option.value} value={option.value}>
                    {t(option.labelKey, option.fallback)}
                  </option>
                ))}
              </MobileSelect>
            </MobileField>
          </MobileCard>

          {liveError ? (
            <MobileCard>
              <Text fontWeight="700" color={danger}>
                {liveError}
              </Text>
            </MobileCard>
          ) : null}

          {liveLoading && livePage === 1 ? (
            <YStack space="$2">
              {Array.from({ length: 3 }).map((_, idx) => (
                <SkeletonCard key={`live-skeleton-${idx}`} height={120} />
              ))}
            </YStack>
          ) : livePhotos.length === 0 ? (
            <MobileCard alignItems="center" justifyContent="center" space="$2">
              <Text fontSize="$sm" fontWeight="700" color={text}>
                {t('controlRoom.emptyLive', 'No photos waiting for Live Show.')}
              </Text>
            </MobileCard>
          ) : (
            <YStack space="$2">
              {livePhotos.map((photo) => {
                const isBusy = liveBusyId === photo.id;
                const liveStatus = normalizeLiveStatus(photo.live_status);
                const galleryStatus = photo.status ?? 'pending';
                const approveMode = resolveLiveShowApproveMode(galleryStatus);
                const canApproveLive = approveMode !== 'not-eligible';
                const showApproveAction = liveStatus !== 'approved';
                const approveLabel =
                  approveMode === 'approve-and-live'
                    ? t('liveShowQueue.approveAndLive', 'Approve + Live')
                    : approveMode === 'approve-only'
                      ? t('liveShowQueue.approve', 'Approve for Live Show')
                      : t('liveShowQueue.notEligible', 'Not eligible');

                return (
                  <MobileCard key={photo.id}>
                    <XStack space="$3" alignItems="center">
                      {photo.thumbnail_url ? (
                        <img
                          src={photo.thumbnail_url}
                          alt={photo.original_name ?? 'Photo'}
                          style={{
                            width: 72,
                            height: 72,
                            borderRadius: 14,
                            objectFit: 'cover',
                            border: `1px solid ${border}`,
                          }}
                        />
                      ) : null}
                      <YStack flex={1} space="$2">
                        <Text fontSize="$sm" fontWeight="700" color={text}>
                          {photo.original_name ?? t('common.photo', 'Photo')}
                        </Text>
                        <XStack alignItems="center" space="$2">
                          <PillBadge tone={resolveStatusTone(galleryStatus)}>
                            {resolveGalleryLabel(galleryStatus)}
                          </PillBadge>
                          <PillBadge tone={resolveStatusTone(liveStatus)}>
                            {resolveLiveLabel(liveStatus)}
                          </PillBadge>
                        </XStack>
                      </YStack>
                    </XStack>
                    <XStack space="$2" marginTop="$2">
                      {showApproveAction ? (
                        <CTAButton
                          label={approveLabel}
                          onPress={() => {
                            if (approveMode === 'approve-and-live') {
                              void handleApproveAndLive(photo);
                              return;
                            }
                            if (approveMode === 'approve-only') {
                              void handleApprove(photo);
                            }
                          }}
                          disabled={!online || !canApproveLive}
                          loading={isBusy}
                          tone="primary"
                        />
                      ) : (
                        <CTAButton
                          label={t('liveShowQueue.clear', 'Remove from Live Show')}
                          onPress={() => handleClear(photo)}
                          disabled={!online}
                          loading={isBusy}
                          tone="ghost"
                        />
                      )}
                      {liveStatus !== 'rejected' ? (
                        <CTAButton
                          label={t('liveShowQueue.reject', 'Reject')}
                          onPress={() => handleReject(photo)}
                          disabled={!online}
                          loading={isBusy}
                          tone="danger"
                        />
                      ) : (
                        <CTAButton
                          label={t('liveShowQueue.clear', 'Remove from Live Show')}
                          onPress={() => handleClear(photo)}
                          disabled={!online}
                          loading={isBusy}
                          tone="ghost"
                        />
                      )}
                    </XStack>
                  </MobileCard>
                );
              })}
            </YStack>
          )}

          {liveHasMore ? (
            <MobileCard>
              <CTAButton
                label={t('common.loadMore', 'Load more')}
                onPress={() => setLivePage((prev) => prev + 1)}
                disabled={liveLoading}
              />
            </MobileCard>
          ) : null}
        </YStack>
      )}

      <LegalConsentSheet
        open={consentOpen}
        onClose={() => {
          if (consentBusy) return;
          setConsentOpen(false);
          setConsentTarget(null);
        }}
        onConfirm={confirmAddonCheckout}
        busy={consentBusy}
        t={t}
      />
    </MobileShell>
  );
}
