import React from 'react';
import { useLocation, useNavigate, useParams } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Check, ChevronDown, Eye, EyeOff, Image as ImageIcon, RefreshCcw, Settings, Sparkles } 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 { Switch } from '@tamagui/switch';
import { Accordion } from '@tamagui/accordion';
import { ScrollView } from '@tamagui/scroll-view';
import { ToggleGroup } from '@tamagui/toggle-group';
import { MobileShell, HeaderActionButton } from './components/MobileShell';
import { MobileCard, CTAButton, SkeletonCard, ContentTabs } from './components/Primitives';
import { MobileField, MobileSelect } from './components/FormControls';
import { useEventContext } from '../context/EventContext';
import {
  approveAndLiveShowPhoto,
  approveLiveShowPhoto,
  clearLiveShowPhoto,
  ControlRoomSettings,
  ControlRoomUploaderRule,
  createEventAddonCheckout,
  EventAddonCatalogItem,
  EventLimitSummary,
  getAddonCatalog,
  featurePhoto,
  getEventPhotos,
  getEvents,
  getLiveShowQueue,
  LiveShowQueueStatus,
  rejectLiveShowPhoto,
  TenantEvent,
  TenantPhoto,
  unfeaturePhoto,
  updatePhotoStatus,
  updateEvent,
  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 { useAuth } from '../auth/context';
import { withAlpha } from './components/colors';
import { ContextHelpLink } from './components/ContextHelpLink';
import {
  enqueuePhotoAction,
  loadPhotoQueue,
  removePhotoAction,
  replacePhotoQueue,
  type PhotoModerationAction,
} from './lib/photoModerationQueue';
import { triggerHaptic } from './lib/haptics';
import { normalizeLiveStatus, resolveLiveShowApproveMode } 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);
}

type PhotoGridAction = {
  key: string;
  label: string;
  icon: React.ComponentType<{ size?: number; color?: string }>;
  onPress: () => void;
  disabled?: boolean;
  backgroundColor?: string;
  iconColor?: string;
};

function PhotoGrid({
  photos,
  actionsForPhoto,
  badgesForPhoto,
  isBusy,
}: {
  photos: TenantPhoto[];
  actionsForPhoto: (photo: TenantPhoto) => PhotoGridAction[];
  badgesForPhoto?: (photo: TenantPhoto) => string[];
  isBusy?: (photo: TenantPhoto) => boolean;
}) {
  const gridStyle: React.CSSProperties = {
    display: 'grid',
    gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))',
    gap: 12,
  };

  return (
    <div style={gridStyle}>
      {photos.map((photo) => (
        <PhotoGridTile
          key={photo.id}
          photo={photo}
          actions={actionsForPhoto(photo)}
          badges={badgesForPhoto ? badgesForPhoto(photo) : []}
          isBusy={isBusy ? isBusy(photo) : false}
        />
      ))}
    </div>
  );
}

function PhotoGridTile({
  photo,
  actions,
  badges,
  isBusy,
}: {
  photo: TenantPhoto;
  actions: PhotoGridAction[];
  badges: string[];
  isBusy: boolean;
}) {
  const { border, muted, surfaceMuted } = useAdminTheme();
  const overlayBg = withAlpha('#0f172a', 0.55);
  const actionBg = withAlpha('#0f172a', 0.55);

  return (
    <div
      style={{
        position: 'relative',
        borderRadius: 16,
        overflow: 'hidden',
        border: `1px solid ${border}`,
        aspectRatio: '1 / 1',
        backgroundColor: surfaceMuted,
        opacity: isBusy ? 0.7 : 1,
      }}
    >
      {photo.thumbnail_url ? (
        <img
          src={photo.thumbnail_url}
          alt={photo.original_name ?? 'Photo'}
          loading="lazy"
          decoding="async"
          style={{ width: '100%', height: '100%', objectFit: 'cover' }}
        />
      ) : (
        <XStack alignItems="center" justifyContent="center" height="100%" backgroundColor={surfaceMuted}>
          <ImageIcon size={22} color={muted} />
        </XStack>
      )}

      {badges.length ? (
        <XStack position="absolute" top={6} left={6} gap="$1.5">
          {badges.map((label) => (
            <PhotoStatusTag key={`${photo.id}-${label}`} label={label} />
          ))}
        </XStack>
      ) : null}

      <XStack
        position="absolute"
        left={6}
        right={6}
        bottom={6}
        padding="$1"
        borderRadius={12}
        backgroundColor={overlayBg}
        gap="$2"
        justifyContent="space-between"
      >
        {actions.map((action) => (
          <PhotoActionButton
            key={action.key}
            label={action.label}
            icon={action.icon}
            onPress={action.onPress}
            disabled={action.disabled}
            backgroundColor={action.backgroundColor ?? actionBg}
            iconColor={action.iconColor ?? '#fff'}
          />
        ))}
      </XStack>
    </div>
  );
}

function PhotoActionButton({
  label,
  icon: Icon,
  onPress,
  disabled = false,
  backgroundColor,
  iconColor,
}: {
  label: string;
  icon: React.ComponentType<{ size?: number; color?: string }>;
  onPress: () => void;
  disabled?: boolean;
  backgroundColor: string;
  iconColor: string;
}) {
  return (
    <Pressable
      onPress={disabled ? undefined : onPress}
      disabled={disabled}
      aria-label={label}
      title={label}
      style={{ flex: 1, opacity: disabled ? 0.55 : 1 }}
    >
      <YStack
        alignItems="center"
        justifyContent="center"
        paddingVertical={8}
        borderRadius={12}
        minHeight={40}
        style={{ backgroundColor }}
      >
        <Icon size={18} color={iconColor} />
      </YStack>
    </Pressable>
  );
}

function PhotoStatusTag({ label }: { label: string }) {
  return (
    <XStack paddingHorizontal={6} paddingVertical={2} borderRadius={999} backgroundColor={withAlpha('#0f172a', 0.7)}>
      <Text fontSize={9} fontWeight="700" color="#fff">
        {label}
      </Text>
    </XStack>
  );
}

function formatDeviceId(deviceId: string): string {
  if (!deviceId) {
    return '';
  }
  if (deviceId.length <= 10) {
    return deviceId;
  }
  return `${deviceId.slice(0, 4)}...${deviceId.slice(-4)}`;
}

export default function MobileEventControlRoomPage() {
  const { slug: slugParam } = useParams<{ slug?: string }>();
  const navigate = useNavigate();
  const location = useLocation();
  const { t } = useTranslation('management');
  const { activeEvent, selectEvent, refetch } = useEventContext();
  const { user } = useAuth();
  const isMember = user?.role === 'member';
  const slug = slugParam ?? activeEvent?.slug ?? null;
  const online = useOnlineStatus();
  const { textStrong, text, muted, border, primary, danger, accent, surfaceMuted, surface } = 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 [moderationCounts, setModerationCounts] = React.useState<Record<ModerationFilter, number>>({
    all: 0,
    pending: 0,
    featured: 0,
    hidden: 0,
  });
  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 [liveCounts, setLiveCounts] = React.useState<Record<LiveShowQueueStatus, number>>({
    pending: 0,
    approved: 0,
    rejected: 0,
    none: 0,
    all: 0,
    expired: 0,
  });

  const [controlRoomSettings, setControlRoomSettings] = React.useState<ControlRoomSettings>({
    auto_approve_highlights: true,
    auto_add_approved_to_live: true,
    auto_remove_live_on_hide: true,
    trusted_uploaders: [],
    force_review_uploaders: [],
  });
  const [controlRoomSaving, setControlRoomSaving] = React.useState(false);
  const [trustedUploaderSelection, setTrustedUploaderSelection] = React.useState('');
  const [forceReviewSelection, setForceReviewSelection] = React.useState('');
  const isImmediateUploads = (activeEvent?.settings?.guest_upload_visibility ?? 'review') === 'immediate';
  const autoApproveHighlights = Boolean(controlRoomSettings.auto_approve_highlights);
  const autoAddApprovedToLive = Boolean(controlRoomSettings.auto_add_approved_to_live) || isImmediateUploads;
  const autoRemoveLiveOnHide = Boolean(controlRoomSettings.auto_remove_live_on_hide);
  const trustedUploaders = controlRoomSettings.trusted_uploaders ?? [];
  const forceReviewUploaders = controlRoomSettings.force_review_uploaders ?? [];

  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 saveControlRoomSettings = React.useCallback(
    async (nextSettings: ControlRoomSettings) => {
      if (!slug) {
        return;
      }

      const previous = controlRoomSettings;
      setControlRoomSettings(nextSettings);
      setControlRoomSaving(true);

      try {
        await updateEvent(slug, {
          settings: {
            control_room: nextSettings,
          },
        });
        refetch();
      } catch (err) {
        setControlRoomSettings(previous);
        toast.error(
          getApiErrorMessage(
            err,
            t('controlRoom.automation.saveFailed', 'Automation settings could not be saved.')
          )
        );
      } finally {
        setControlRoomSaving(false);
      }
    },
    [controlRoomSettings, refetch, slug, t],
  );

  const uploaderOptions = React.useMemo(() => {
    const options = new Map<string, { deviceId: string; label: string }>();
    const addPhoto = (photo: TenantPhoto) => {
      const deviceId = photo.created_by_device_id ?? '';
      if (!deviceId) {
        return;
      }
      if (options.has(deviceId)) {
        return;
      }
      options.set(deviceId, {
        deviceId,
        label: photo.uploader_name ?? t('common.anonymous', 'Anonymous'),
      });
    };

    moderationPhotos.forEach(addPhoto);
    livePhotos.forEach(addPhoto);

    return Array.from(options.values()).map((entry) => ({
      ...entry,
      display: `${entry.label} • ${formatDeviceId(entry.deviceId)}`,
    }));
  }, [livePhotos, moderationPhotos, t]);

  const mergeUploaderRule = (list: ControlRoomUploaderRule[], rule: ControlRoomUploaderRule) => {
    if (list.some((entry) => entry.device_id === rule.device_id)) {
      return list;
    }
    return [...list, rule];
  };

  const addUploaderRule = React.useCallback(
    (target: 'trusted' | 'force') => {
      const selectedId = target === 'trusted' ? trustedUploaderSelection : forceReviewSelection;
      if (!selectedId) {
        return;
      }

      const option = uploaderOptions.find((entry) => entry.deviceId === selectedId);
      const rule: ControlRoomUploaderRule = {
        device_id: selectedId,
        label: option?.label ?? t('common.anonymous', 'Anonymous'),
      };

      const currentTrusted = controlRoomSettings.trusted_uploaders ?? [];
      const currentForceReview = controlRoomSettings.force_review_uploaders ?? [];

      const nextTrusted =
        target === 'trusted'
          ? mergeUploaderRule(currentTrusted, rule)
          : currentTrusted.filter((entry) => entry.device_id !== selectedId);
      const nextForceReview =
        target === 'force'
          ? mergeUploaderRule(currentForceReview, rule)
          : currentForceReview.filter((entry) => entry.device_id !== selectedId);

      saveControlRoomSettings({
        ...controlRoomSettings,
        trusted_uploaders: nextTrusted,
        force_review_uploaders: nextForceReview,
      });
      setTrustedUploaderSelection('');
      setForceReviewSelection('');
    },
    [
      controlRoomSettings,
      forceReviewSelection,
      saveControlRoomSettings,
      t,
      trustedUploaderSelection,
      uploaderOptions,
    ],
  );

  const removeUploaderRule = React.useCallback(
    (target: 'trusted' | 'force', deviceId: string) => {
      const nextTrusted =
        target === 'trusted'
          ? (controlRoomSettings.trusted_uploaders ?? []).filter((entry) => entry.device_id !== deviceId)
          : controlRoomSettings.trusted_uploaders ?? [];
      const nextForceReview =
        target === 'force'
          ? (controlRoomSettings.force_review_uploaders ?? []).filter((entry) => entry.device_id !== deviceId)
          : controlRoomSettings.force_review_uploaders ?? [];

      saveControlRoomSettings({
        ...controlRoomSettings,
        trusted_uploaders: nextTrusted,
        force_review_uploaders: nextForceReview,
      });
    },
    [controlRoomSettings, saveControlRoomSettings],
  );

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

  React.useEffect(() => {
    const settings = (activeEvent?.settings?.control_room ?? {}) as ControlRoomSettings;
    const resolveFlag = (value: boolean | undefined) => (typeof value === 'boolean' ? value : true);
    setControlRoomSettings({
      auto_approve_highlights: resolveFlag(settings.auto_approve_highlights),
      auto_add_approved_to_live: resolveFlag(settings.auto_add_approved_to_live),
      auto_remove_live_on_hide: resolveFlag(settings.auto_remove_live_on_hide),
      trusted_uploaders: Array.isArray(settings.trusted_uploaders) ? settings.trusted_uploaders : [],
      force_review_uploaders: Array.isArray(settings.force_review_uploaders) ? settings.force_review_uploaders : [],
    });
    setTrustedUploaderSelection('');
    setForceReviewSelection('');
  }, [activeEvent?.settings?.control_room, activeEvent?.slug]);

  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 loadModerationCounts = React.useCallback(async () => {
    if (!slug) {
      return;
    }

    try {
      const [all, pending, featured, hidden] = await Promise.all([
        getEventPhotos(slug, { page: 1, perPage: 1, sort: 'desc' }),
        getEventPhotos(slug, { page: 1, perPage: 1, sort: 'desc', status: 'pending' }),
        getEventPhotos(slug, { page: 1, perPage: 1, sort: 'desc', featured: true }),
        getEventPhotos(slug, { page: 1, perPage: 1, sort: 'desc', status: 'hidden' }),
      ]);
      setModerationCounts({
        all: all.meta?.total ?? all.photos.length,
        pending: pending.meta?.total ?? pending.photos.length,
        featured: featured.meta?.total ?? featured.photos.length,
        hidden: hidden.meta?.total ?? hidden.photos.length,
      });
    } catch {
      // ignore
    }
  }, [slug]);

  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]);

  const loadLiveCounts = React.useCallback(async () => {
    if (!slug) {
      return;
    }

    try {
      const statuses: LiveShowQueueStatus[] = ['pending', 'approved', 'rejected', 'none'];
      const results = await Promise.all(
        statuses.map((status) => getLiveShowQueue(slug, { page: 1, perPage: 1, liveStatus: status }))
      );
      const nextCounts: Record<LiveShowQueueStatus, number> = {
        pending: results[0]?.meta?.total ?? results[0]?.photos.length ?? 0,
        approved: results[1]?.meta?.total ?? results[1]?.photos.length ?? 0,
        rejected: results[2]?.meta?.total ?? results[2]?.photos.length ?? 0,
        none: results[3]?.meta?.total ?? results[3]?.photos.length ?? 0,
        all: 0,
        expired: 0,
      };
      setLiveCounts(nextCounts);
    } catch {
      // ignore
    }
  }, [slug]);

  const refreshCounts = React.useCallback(() => {
    void loadModerationCounts();
    void loadLiveCounts();
  }, [loadLiveCounts, loadModerationCounts]);

  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(() => {
    refreshCounts();
  }, [refreshCounts]);

  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 updatePhotoInCollections = React.useCallback((updated: TenantPhoto) => {
    setModerationPhotos((prev) => prev.map((photo) => (photo.id === updated.id ? updated : photo)));
    setLivePhotos((prev) => prev.map((photo) => (photo.id === updated.id ? updated : photo)));
  }, []);

  const applyOptimisticUpdate = React.useCallback((photoId: number, action: PhotoModerationAction['action']) => {
    const applyUpdate = (photo: TenantPhoto) => {
      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' };
      }

      if (action === 'feature') {
        return { ...photo, is_featured: true };
      }

      if (action === 'unfeature') {
        return { ...photo, is_featured: false };
      }

      return photo;
    };

    setModerationPhotos((prev) => prev.map(applyUpdate));
    setLivePhotos((prev) => prev.map(applyUpdate));
  }, []);

  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 enqueueModerationActions = React.useCallback(
    (actions: PhotoModerationAction['action'][], photoId: number) => {
      if (!slug) {
        return;
      }
      let nextQueue: PhotoModerationAction[] = [];
      actions.forEach((action) => {
        nextQueue = enqueuePhotoAction({ eventSlug: slug, photoId, action });
        applyOptimisticUpdate(photoId, action);
      });
      setQueuedActions(nextQueue);
      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) {
          updatePhotoInCollections(updated);
        }
      } 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, updatePhotoInCollections, updateQueueState]);

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

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

      const shouldAutoApproveHighlight = action === 'feature' && autoApproveHighlights && photo.status === 'pending';

      if (!online) {
        if (shouldAutoApproveHighlight) {
          enqueueModerationActions(['approve', 'feature'], photo.id);
          return;
        }
        enqueueModerationAction(action, photo.id);
        return;
      }

      setModerationBusyId(photo.id);

      try {
        let updated: TenantPhoto;
        if (action === 'approve') {
          updated = autoAddApprovedToLive
            ? await approveAndLiveShowPhoto(slug, photo.id)
            : await updatePhotoStatus(slug, photo.id, 'approved');
        } else if (action === 'hide') {
          updated = await updatePhotoVisibility(slug, photo.id, true);
        } else if (action === 'show') {
          updated = await updatePhotoVisibility(slug, photo.id, false);
        } else if (action === 'feature') {
          if (shouldAutoApproveHighlight) {
            if (autoAddApprovedToLive) {
              await approveAndLiveShowPhoto(slug, photo.id);
            } else {
              await updatePhotoStatus(slug, photo.id, 'approved');
            }
          }
          updated = await featurePhoto(slug, photo.id);
        } else {
          updated = await unfeaturePhoto(slug, photo.id);
        }

        updatePhotoInCollections(updated);
        refreshCounts();
        triggerHaptic(action === 'approve' || action === 'feature' ? 'success' : 'medium');
      } catch (err) {
        if (!isAuthError(err)) {
          const fallbackMessage =
            action === 'approve'
              ? t('mobilePhotos.approveFailed', 'Approval failed.')
              : action === 'feature' || action === 'unfeature'
                ? t('mobilePhotos.featureFailed', 'Highlight could not be changed.')
                : t('mobilePhotos.visibilityFailed', 'Visibility could not be changed.');
          const message = getApiErrorMessage(err, fallbackMessage);
          setModerationError(message);
          toast.error(message);
        }
      } finally {
        setModerationBusyId(null);
      }
    },
    [
      autoAddApprovedToLive,
      autoApproveHighlights,
      enqueueModerationAction,
      enqueueModerationActions,
      online,
      slug,
      t,
      updatePhotoInCollections,
    ],
  );

  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)));
      refreshCounts();
      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)));
      refreshCounts();
      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)));
      refreshCounts();
      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)));
      refreshCounts();
      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 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 gap="$2">
      <HeaderActionButton
        onPress={() => {
          if (activeTab === 'moderation') {
            void loadModeration();
            return;
          }
          void loadLiveQueue();
        }}
        ariaLabel={t('common.refresh', 'Refresh')}
      >
        <RefreshCcw size={18} color={textStrong} />
      </HeaderActionButton>
      {slug && !isMember ? (
        <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}
    >
      <ContentTabs
        value={activeTab}
        onValueChange={(val) => setActiveTab(val as 'moderation' | 'live')}
        header={(
          <YStack gap="$2">
            <XStack justifyContent="flex-end">
              <ContextHelpLink slug="control-room-moderation" />
            </XStack>
            <MobileCard>
        <Accordion type="single" collapsible>
          <Accordion.Item value="upload-settings">
            <Accordion.Trigger
              {...({
                padding: '$2',
                borderWidth: 1,
                borderColor: border,
                borderRadius: 14,
                backgroundColor: surfaceMuted,
              } as any)}
            >
              <XStack justifyContent="space-between" alignItems="center" flex={1}>
                <Text fontSize="$sm" fontWeight="800" color={text}>
                  {t('controlRoom.automation.sectionTitle', 'Einstellungen für Uploads')}
                </Text>
                <ChevronDown size={16} color={muted} />
              </XStack>
            </Accordion.Trigger>
            <Accordion.Content {...({ paddingTop: '$2' } as any)}>
              <YStack gap="$3">
                <YStack gap="$2">
                  <Text fontSize="$sm" fontWeight="800" color={text}>
                    {t('controlRoom.automation.title', 'Automation')}
                  </Text>
                  <MobileField
                    label={t('controlRoom.automation.autoApproveHighlights.label', 'Auto-approve highlights')}
                    hint={t(
                      'controlRoom.automation.autoApproveHighlights.help',
                      'When you highlight a pending photo it will be approved automatically.',
                    )}
                  >
                    <Switch
                      size="$3"
                      checked={autoApproveHighlights}
                      disabled={controlRoomSaving}
                      onCheckedChange={(checked: boolean) =>
                        saveControlRoomSettings({
                          ...controlRoomSettings,
                          auto_approve_highlights: Boolean(checked),
                        })
                      }
                      aria-label={t('controlRoom.automation.autoApproveHighlights.label', 'Auto-approve highlights')}
                    >
                      <Switch.Thumb />
                    </Switch>
                  </MobileField>
                  <MobileField
                    label={t('controlRoom.automation.autoAddApproved.label', 'Auto-add approved to Live Show')}
                    hint={
                      isImmediateUploads
                        ? t(
                            'controlRoom.automation.autoAddApproved.locked',
                            'Enabled because uploads are visible immediately.',
                          )
                        : t(
                            'controlRoom.automation.autoAddApproved.help',
                            'Approved or highlighted photos go straight to the Live Show.',
                          )
                    }
                  >
                    <Switch
                      size="$3"
                      checked={autoAddApprovedToLive}
                      disabled={controlRoomSaving || isImmediateUploads}
                      onCheckedChange={(checked: boolean) => {
                        if (isImmediateUploads) {
                          return;
                        }
                        saveControlRoomSettings({
                          ...controlRoomSettings,
                          auto_add_approved_to_live: Boolean(checked),
                        });
                      }}
                      aria-label={t('controlRoom.automation.autoAddApproved.label', 'Auto-add approved to Live Show')}
                    >
                      <Switch.Thumb />
                    </Switch>
                  </MobileField>
                  <MobileField
                    label={t('controlRoom.automation.autoRemoveLive.label', 'Auto-remove from Live Show')}
                    hint={t(
                      'controlRoom.automation.autoRemoveLive.help',
                      'Hidden or rejected photos are removed from the Live Show automatically.',
                    )}
                  >
                    <Switch
                      size="$3"
                      checked={autoRemoveLiveOnHide}
                      disabled={controlRoomSaving}
                      onCheckedChange={(checked: boolean) =>
                        saveControlRoomSettings({
                          ...controlRoomSettings,
                          auto_remove_live_on_hide: Boolean(checked),
                        })
                      }
                      aria-label={t('controlRoom.automation.autoRemoveLive.label', 'Auto-remove from Live Show')}
                    >
                      <Switch.Thumb />
                    </Switch>
                  </MobileField>
                </YStack>

                <YStack gap="$2">
                  <Text fontSize="$sm" fontWeight="800" color={text}>
                    {t('controlRoom.automation.uploaders.title', 'Uploader overrides')}
                  </Text>
                  <Text fontSize="$xs" color={muted}>
                    {t(
                      'controlRoom.automation.uploaders.subtitle',
                      'Pick devices from recent uploads to always approve or always review their photos.',
                    )}
                  </Text>

                  <MobileField
                    label={t('controlRoom.automation.uploaders.trustedLabel', 'Always approve')}
                    hint={t(
                      'controlRoom.automation.uploaders.trustedHelp',
                      'Uploads from these devices skip the approval queue.',
                    )}
                  >
                    <YStack gap="$2">
                      <MobileSelect
                        value={trustedUploaderSelection}
                        onChange={(event) => setTrustedUploaderSelection(event.target.value)}
                      >
                        <option value="">{t('common.select', 'Select')}</option>
                        {uploaderOptions.map((option) => (
                          <option key={option.deviceId} value={option.deviceId}>
                            {option.display}
                          </option>
                        ))}
                      </MobileSelect>
                      <CTAButton
                        label={t('controlRoom.automation.uploaders.add', 'Add')}
                        onPress={() => addUploaderRule('trusted')}
                        tone="ghost"
                        disabled={controlRoomSaving || !trustedUploaderSelection}
                      />
                    </YStack>
                    {trustedUploaders.length ? (
                      <YStack gap="$2">
                        {trustedUploaders.map((rule) => (
                          <XStack
                            key={`trusted-${rule.device_id}`}
                            alignItems="center"
                            justifyContent="space-between"
                            padding="$2"
                            borderRadius={14}
                            borderWidth={1}
                            borderColor={border}
                            backgroundColor={surfaceMuted}
                          >
                            <YStack gap="$0.5">
                              <Text fontSize="$sm" fontWeight="700" color={text}>
                                {rule.label ?? t('common.anonymous', 'Anonymous')}
                              </Text>
                              <Text fontSize="$xs" color={muted}>
                                {formatDeviceId(rule.device_id)}
                              </Text>
                            </YStack>
                            <Pressable
                              onPress={() => removeUploaderRule('trusted', rule.device_id)}
                              disabled={controlRoomSaving}
                              aria-label={t('controlRoom.automation.uploaders.remove', 'Remove')}
                              style={{
                                paddingHorizontal: 12,
                                paddingVertical: 6,
                                borderRadius: 999,
                                backgroundColor: withAlpha(danger, 0.12),
                              }}
                            >
                              <Text fontSize="$xs" fontWeight="700" color={danger}>
                                {t('controlRoom.automation.uploaders.remove', 'Remove')}
                              </Text>
                            </Pressable>
                          </XStack>
                        ))}
                      </YStack>
                    ) : (
                      <Text fontSize="$xs" color={muted}>
                        {t('controlRoom.automation.uploaders.empty', 'No overrides yet.')}
                      </Text>
                    )}
                  </MobileField>

                  <MobileField
                    label={t('controlRoom.automation.uploaders.forceLabel', 'Always review')}
                    hint={t(
                      'controlRoom.automation.uploaders.forceHelp',
                      'Uploads from these devices always need approval.',
                    )}
                  >
                    <YStack gap="$2">
                      <MobileSelect
                        value={forceReviewSelection}
                        onChange={(event) => setForceReviewSelection(event.target.value)}
                      >
                        <option value="">{t('common.select', 'Select')}</option>
                        {uploaderOptions.map((option) => (
                          <option key={option.deviceId} value={option.deviceId}>
                            {option.display}
                          </option>
                        ))}
                      </MobileSelect>
                      <CTAButton
                        label={t('controlRoom.automation.uploaders.add', 'Add')}
                        onPress={() => addUploaderRule('force')}
                        tone="ghost"
                        disabled={controlRoomSaving || !forceReviewSelection}
                      />
                    </YStack>
                    {forceReviewUploaders.length ? (
                      <YStack gap="$2">
                        {forceReviewUploaders.map((rule) => (
                          <XStack
                            key={`force-${rule.device_id}`}
                            alignItems="center"
                            justifyContent="space-between"
                            padding="$2"
                            borderRadius={14}
                            borderWidth={1}
                            borderColor={border}
                            backgroundColor={surfaceMuted}
                          >
                            <YStack gap="$0.5">
                              <Text fontSize="$sm" fontWeight="700" color={text}>
                                {rule.label ?? t('common.anonymous', 'Anonymous')}
                              </Text>
                              <Text fontSize="$xs" color={muted}>
                                {formatDeviceId(rule.device_id)}
                              </Text>
                            </YStack>
                            <Pressable
                              onPress={() => removeUploaderRule('force', rule.device_id)}
                              disabled={controlRoomSaving}
                              aria-label={t('controlRoom.automation.uploaders.remove', 'Remove')}
                              style={{
                                paddingHorizontal: 12,
                                paddingVertical: 6,
                                borderRadius: 999,
                                backgroundColor: withAlpha(danger, 0.12),
                              }}
                            >
                              <Text fontSize="$xs" fontWeight="700" color={danger}>
                                {t('controlRoom.automation.uploaders.remove', 'Remove')}
                              </Text>
                            </Pressable>
                          </XStack>
                        ))}
                      </YStack>
                    ) : (
                      <Text fontSize="$xs" color={muted}>
                        {t('controlRoom.automation.uploaders.empty', 'No overrides yet.')}
                      </Text>
                    )}
                  </MobileField>
                </YStack>
              </YStack>
            </Accordion.Content>
          </Accordion.Item>
        </Accordion>
            </MobileCard>
          </YStack>
        )}
        tabs={[
          {
            value: 'moderation',
            label: t('controlRoom.tabs.moderation', 'Moderation'),
            content: (
        <YStack gap="$2">
          {queuedEventCount > 0 ? (
            <MobileCard>
              <XStack alignItems="center" justifyContent="space-between" gap="$2">
                <YStack gap="$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>
            <YStack gap="$2">
              <Text fontSize="$xs" fontWeight="800" color={text}>
                {t('mobilePhotos.filtersTitle', 'Filter')}
              </Text>
              <ScrollView horizontal showsHorizontalScrollIndicator={false}>
                <ToggleGroup
                  type="single"
                  value={moderationFilter}
                  onValueChange={(value: string) => value && setModerationFilter(value as ModerationFilter)}
                  disableDeactivation
                  orientation="horizontal"
                  flexDirection="row"
                  alignItems="center"
                  padding="$1"
                  gap="$1.5"
                  borderRadius="$pill"
                  borderWidth={1}
                  borderColor={border}
                  backgroundColor={surfaceMuted}
                >
                  {MODERATION_FILTERS.map((option) => {
                    const count = moderationCounts[option.value] ?? 0;
                    return (
                      <ToggleGroup.Item
                        key={option.value}
                        value={option.value}
                        borderRadius="$pill"
                        paddingVertical="$1.5"
                        paddingHorizontal="$3"
                      >
                        <XStack alignItems="center" gap="$1.5">
                          <Text fontSize="$xs" fontWeight="600">
                            {t(option.labelKey, option.fallback)}
                          </Text>
                          <XStack
                            paddingHorizontal="$1.5"
                            paddingVertical="$0.5"
                            borderRadius="$pill"
                            borderWidth={1}
                            borderColor={border}
                            backgroundColor="$backgroundStrong"
                          >
                            <Text fontSize={10} fontWeight="700">
                              {count}
                            </Text>
                          </XStack>
                        </XStack>
                      </ToggleGroup.Item>
                    );
                  })}
                </ToggleGroup>
              </ScrollView>
            </YStack>
          </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 gap="$2">
              {Array.from({ length: 3 }).map((_, idx) => (
                <SkeletonCard key={`moderation-skeleton-${idx}`} height={120} />
              ))}
            </YStack>
          ) : moderationPhotos.length === 0 ? (
            <MobileCard alignItems="center" justifyContent="center" gap="$2">
              <ImageIcon size={28} color={muted} />
              <Text fontSize="$sm" fontWeight="700" color={text}>
                {t('controlRoom.emptyModeration', 'No uploads match this filter.')}
              </Text>
            </MobileCard>
          ) : (
            <PhotoGrid
              photos={moderationPhotos}
              isBusy={(photo) => moderationBusyId === photo.id}
              badgesForPhoto={(photo) => {
                const badges: string[] = [];
                if (photo.status === 'hidden') {
                  badges.push(t('photos.filters.hidden', 'Hidden'));
                }
                if (photo.is_featured) {
                  badges.push(t('photos.filters.featured', 'Highlights'));
                }
                return badges;
              }}
              actionsForPhoto={(photo) => {
                const isBusy = moderationBusyId === photo.id;
                const galleryStatus = photo.status ?? 'pending';
                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');
                const featureAction: PhotoModerationAction['action'] = photo.is_featured ? 'unfeature' : 'feature';
                const featureLabel = photo.is_featured
                  ? t('photos.actions.unfeature', 'Remove highlight')
                  : t('photos.actions.feature', 'Set highlight');
                const approveBg = withAlpha(primary, 0.78);
                const hideBg = withAlpha(danger, 0.75);
                const highlightBg = photo.is_featured ? withAlpha(accent, 0.85) : withAlpha(accent, 0.55);
                return [
                  {
                    key: 'approve',
                    label: t('photos.actions.approve', 'Approve'),
                    icon: Check,
                    onPress: () => handleModerationAction('approve', photo),
                    disabled: !canApprove || isBusy,
                    backgroundColor: approveBg,
                  },
                  {
                    key: 'visibility',
                    label: visibilityLabel,
                    icon: canShow ? Eye : EyeOff,
                    onPress: () => handleModerationAction(visibilityAction, photo),
                    disabled: isBusy,
                    backgroundColor: hideBg,
                  },
                  {
                    key: 'feature',
                    label: featureLabel,
                    icon: Sparkles,
                    onPress: () => handleModerationAction(featureAction, photo),
                    disabled: isBusy,
                    backgroundColor: highlightBg,
                  },
                ];
              }}
            />
          )}

          {moderationHasMore ? (
            <MobileCard>
              <CTAButton
                label={t('common.loadMore', 'Load more')}
                onPress={() => setModerationPage((prev) => prev + 1)}
                disabled={moderationLoading}
              />
            </MobileCard>
          ) : null}
        </YStack>
            ),
          },
          {
            value: 'live',
            label: t('controlRoom.tabs.live', 'Live Show'),
            content: (
        <YStack gap="$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>
            <YStack gap="$2">
              <Text fontSize="$xs" fontWeight="800" color={text}>
                {t('liveShowQueue.filterLabel', 'Live status')}
              </Text>
              <ScrollView horizontal showsHorizontalScrollIndicator={false}>
                <ToggleGroup
                  type="single"
                  value={liveStatusFilter}
                  onValueChange={(value: string) => value && setLiveStatusFilter(value as LiveShowQueueStatus)}
                  disableDeactivation
                  orientation="horizontal"
                  flexDirection="row"
                  alignItems="center"
                  padding="$1"
                  gap="$1.5"
                  borderRadius="$pill"
                  borderWidth={1}
                  borderColor={border}
                  backgroundColor={surfaceMuted}
                >
                  {LIVE_STATUS_OPTIONS.map((option) => {
                    const count = liveCounts[option.value] ?? 0;
                    return (
                      <ToggleGroup.Item
                        key={option.value}
                        value={option.value}
                        borderRadius="$pill"
                        paddingVertical="$1.5"
                        paddingHorizontal="$3"
                      >
                        <XStack alignItems="center" gap="$1.5">
                          <Text fontSize="$xs" fontWeight="600">
                            {t(option.labelKey, option.fallback)}
                          </Text>
                          <XStack
                            paddingHorizontal="$1.5"
                            paddingVertical="$0.5"
                            borderRadius="$pill"
                            borderWidth={1}
                            borderColor={border}
                            backgroundColor="$backgroundStrong"
                          >
                            <Text fontSize={10} fontWeight="700">
                              {count}
                            </Text>
                          </XStack>
                        </XStack>
                      </ToggleGroup.Item>
                    );
                  })}
                </ToggleGroup>
              </ScrollView>
            </YStack>
          </MobileCard>

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

          {liveLoading && livePage === 1 ? (
            <YStack gap="$2">
              {Array.from({ length: 3 }).map((_, idx) => (
                <SkeletonCard key={`live-skeleton-${idx}`} height={120} />
              ))}
            </YStack>
          ) : livePhotos.length === 0 ? (
            <MobileCard alignItems="center" justifyContent="center" gap="$2">
              <Text fontSize="$sm" fontWeight="700" color={text}>
                {t('controlRoom.emptyLive', 'No photos waiting for Live Show.')}
              </Text>
            </MobileCard>
          ) : (
            <PhotoGrid
              photos={livePhotos}
              isBusy={(photo) => moderationBusyId === photo.id || liveBusyId === photo.id}
              badgesForPhoto={(photo) => {
                const badges: string[] = [];
                const liveStatus = normalizeLiveStatus(photo.live_status);
                if (liveStatus !== 'pending') {
                  badges.push(resolveLiveLabel(liveStatus));
                }
                if (photo.status === 'hidden') {
                  badges.push(t('photos.filters.hidden', 'Hidden'));
                }
                if (photo.is_featured) {
                  badges.push(t('photos.filters.featured', 'Highlights'));
                }
                return badges;
              }}
              actionsForPhoto={(photo) => {
                const isLiveBusy = liveBusyId === photo.id;
                const isModerationBusy = moderationBusyId === photo.id;
                const liveStatus = normalizeLiveStatus(photo.live_status);
                const galleryStatus = photo.status ?? 'pending';
                const approveMode = resolveLiveShowApproveMode(galleryStatus);
                const canApproveLive = approveMode !== 'not-eligible';
                const approveDisabled = !online || !canApproveLive || liveStatus === 'approved' || isLiveBusy;
                const visibilityLabel = t('photos.actions.hide', 'Hide');
                const featureAction: PhotoModerationAction['action'] = photo.is_featured ? 'unfeature' : 'feature';
                const featureLabel = photo.is_featured
                  ? t('photos.actions.unfeature', 'Remove highlight')
                  : t('photos.actions.feature', 'Set highlight');

                const approveBg = withAlpha(primary, 0.78);
                const hideBg = withAlpha(danger, 0.75);
                const highlightBg = photo.is_featured ? withAlpha(accent, 0.85) : withAlpha(accent, 0.55);
                return [
                  {
                    key: 'approve',
                    label: t('photos.actions.approve', 'Approve'),
                    icon: Check,
                    onPress: () => {
                      if (approveMode === 'approve-and-live') {
                        void handleApproveAndLive(photo);
                        return;
                      }
                      if (approveMode === 'approve-only') {
                        void handleApprove(photo);
                      }
                    },
                    disabled: approveDisabled,
                    backgroundColor: approveBg,
                  },
                  {
                    key: 'visibility',
                    label: visibilityLabel,
                    icon: EyeOff,
                    onPress: () => {
                      if (liveStatus === 'approved' || liveStatus === 'rejected') {
                        void handleClear(photo);
                        return;
                      }
                      void handleReject(photo);
                    },
                    disabled: !online || isLiveBusy || isModerationBusy,
                    backgroundColor: hideBg,
                  },
                  {
                    key: 'feature',
                    label: featureLabel,
                    icon: Sparkles,
                    onPress: () => handleModerationAction(featureAction, photo),
                    disabled: isModerationBusy,
                    backgroundColor: highlightBg,
                  },
                ];
              }}
            />
          )}

          {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>
  );
}
