import React from 'react';
import { Link, useParams } from 'react-router-dom';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
import { Progress } from '@/components/ui/progress';
import { Skeleton } from '@/components/ui/skeleton';
import { Separator } from '@/components/ui/separator';
import { AnimatePresence, motion } from 'framer-motion';
import EmotionPicker from '../components/EmotionPicker';
import GalleryPreview from '../components/GalleryPreview';
import { useGuestIdentity } from '../context/GuestIdentityContext';
import { useEventData } from '../hooks/useEventData';
import { useGuestTaskProgress } from '../hooks/useGuestTaskProgress';
import { ArrowLeft, ArrowRight, Camera, ChevronDown, Sparkles, UploadCloud, X, RefreshCw, Timer } from 'lucide-react';
import { useTranslation, type TranslateFn } from '../i18n/useTranslation';
import { useEventBranding } from '../context/EventBrandingContext';
import type { EventBranding } from '../types/event-branding';
import { Swiper, SwiperSlide } from 'swiper/react';
import { EffectCards } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/effect-cards';
import { getEmotionIcon, getEmotionTheme, type EmotionIdentity } from '../lib/emotionTheme';
import { getDeviceId } from '../lib/device';
import { useDirectUpload } from '../hooks/useDirectUpload';
import { useNavigate } from 'react-router-dom';
import { isTaskModeEnabled } from '../lib/engagement';
import { FADE_SCALE, FADE_UP, STAGGER_FAST, getMotionContainerProps, getMotionItemProps, prefersReducedMotion } from '../lib/motion';

export default function HomePage() {
  const { token } = useParams<{ token: string }>();
  const { name, hydrated } = useGuestIdentity();
  const { event } = useEventData();
  const { completedCount } = useGuestTaskProgress(token ?? '');
  const { t, locale } = useTranslation();
  const { branding } = useEventBranding();
  const headingFont = branding.typography?.heading ?? branding.fontFamily ?? undefined;
  const bodyFont = branding.typography?.body ?? branding.fontFamily ?? undefined;
  const radius = branding.buttons?.radius ?? 12;

  const heroStorageKey = token ? `guestHeroDismissed_${token}` : 'guestHeroDismissed';
  const [heroVisible, setHeroVisible] = React.useState(false);

  React.useEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }

    try {
      const stored = window.sessionStorage.getItem(heroStorageKey);
      // standardmäßig versteckt, nur sichtbar falls explizit gesetzt (kann später wieder aktiviert werden)
      setHeroVisible(stored === 'show');
    } catch {
      setHeroVisible(false);
    }
  }, [heroStorageKey]);

  const dismissHero = React.useCallback(() => {
    setHeroVisible(false);
    if (typeof window === 'undefined') {
      return;
    }

    try {
      window.sessionStorage.setItem(heroStorageKey, '1');
    } catch {
      // ignore storage exceptions (e.g. private mode)
    }
  }, [heroStorageKey]);

  const displayName = hydrated && name ? name : t('home.fallbackGuestName');
  const eventNameDisplay = event?.name ?? t('home.hero.defaultEventName');
  const accentColor = branding.primaryColor;
  const secondaryAccent = branding.secondaryColor;
  const uploadsRequireApproval =
    (event?.guest_upload_visibility as 'immediate' | 'review' | undefined) !== 'immediate';
  const tasksEnabled = isTaskModeEnabled(event);
  const motionEnabled = !prefersReducedMotion();
  const containerMotion = getMotionContainerProps(motionEnabled, STAGGER_FAST);
  const fadeUpMotion = getMotionItemProps(motionEnabled, FADE_UP);
  const fadeScaleMotion = getMotionItemProps(motionEnabled, FADE_SCALE);

  const [missionDeck, setMissionDeck] = React.useState<MissionPreview[]>([]);
  const [missionPool, setMissionPool] = React.useState<MissionPreview[]>([]);
  const [missionLoading, setMissionLoading] = React.useState(false);
  const [isLoadingMore, setIsLoadingMore] = React.useState(false);
  const [hasMore, setHasMore] = React.useState(true);
  const [page, setPage] = React.useState(1);
  const [swipeCount, setSwipeCount] = React.useState(0);
  const seenIdsRef = React.useRef<Set<number>>(new Set());
  const poolIndexRef = React.useRef(0);
  const sliderStateKey = token ? `missionSliderIndex:${token}` : null;
  const swiperRef = React.useRef<any>(null);

  const advanceDeck = React.useCallback(() => {
    if (swiperRef.current) {
      swiperRef.current.slideNext();
    }
  }, []);

  const normalizeTasks = React.useCallback(
    (tasks: Record<string, unknown>[]): MissionPreview[] =>
      tasks.map((task) => ({
        id: Number(task.id),
        title: typeof task.title === 'string' ? task.title : 'Mission',
        description: typeof task.description === 'string' ? task.description : '',
        duration: typeof task.duration === 'number' ? task.duration : 3,
        emotion: (task.emotion as EmotionIdentity) ?? null,
      })),
    []
  );

  const mergeIntoPool = React.useCallback(
    (incoming: MissionPreview[]) => {
      const slugTitle = (title: string) =>
        title
          .toLowerCase()
          .normalize('NFD')
          .replace(/[\u0300-\u036f]/g, '')
          .replace(/[^a-z0-9]+/g, ' ')
          .replace(/\s+/g, ' ')
          .trim();

      setMissionPool((prev) => {
        const byId = new Map<number, MissionPreview>();
        const byTitle = new Map<string, MissionPreview>();

        const addCandidate = (candidate: MissionPreview) => {
          if (byId.has(candidate.id)) return;
          const titleKey = slugTitle(candidate.title);
          if (byTitle.has(titleKey)) return;
          byId.set(candidate.id, candidate);
          byTitle.set(titleKey, candidate);
        };

        prev.forEach(addCandidate);
        incoming.forEach(addCandidate);

        return Array.from(byId.values());
      });
    },
    []
  );

  const fetchTasksPage = React.useCallback(
    async (pageToFetch: number, isInitial: boolean = false) => {
      if (!token) return;
      if (isInitial) {
        setMissionLoading(true);
      }
      setIsLoadingMore(true);
      try {
        const perPage = 20;
        const response = await fetch(
          `/api/v1/events/${encodeURIComponent(token)}/tasks?page=${pageToFetch}&per_page=${perPage}&locale=${encodeURIComponent(locale)}`,
          {
            headers: {
              Accept: 'application/json',
              'X-Locale': locale,
              'X-Device-Id': getDeviceId(),
            },
          }
        );
        if (!response.ok) throw new Error('Aufgaben konnten nicht geladen werden.');
        const payload = await response.json();

        let items: Record<string, unknown>[] = [];
        let hasMoreFlag = false;
        let nextPage = pageToFetch + 1;

        if (Array.isArray(payload)) {
          items = payload;
          hasMoreFlag = false;
        } else if (payload && Array.isArray(payload.tasks)) {
          items = payload.tasks;
          hasMoreFlag = false;
        } else if (payload && Array.isArray(payload.data)) {
          items = payload.data;
          if (payload.meta?.current_page && payload.meta?.last_page) {
            hasMoreFlag = payload.meta.current_page < payload.meta.last_page;
            nextPage = (payload.meta.current_page as number) + 1;
          } else if (payload.next_page_url !== undefined) {
            hasMoreFlag = Boolean(payload.next_page_url);
          } else {
            hasMoreFlag = items.length > 0;
          }
        } else {
          hasMoreFlag = false;
        }

        const normalized = normalizeTasks(items);
        const deduped = normalized.filter((task) => {
          if (seenIdsRef.current.has(task.id)) return false;
          seenIdsRef.current.add(task.id);
          return true;
        });

        if (deduped.length) {
          mergeIntoPool(deduped);
        }

        setHasMore(hasMoreFlag);
        if (hasMoreFlag) {
          setPage(nextPage);
        }
      } catch (err) {
        console.warn('Mission fetch failed', err);
        setHasMore(false);
      } finally {
        setIsLoadingMore(false);
        if (isInitial) {
          setMissionLoading(false);
        }
      }
    },
    [locale, normalizeTasks, token]
  );

  React.useEffect(() => {
    if (!token) return;
    seenIdsRef.current = new Set();
    setMissionDeck([]);
    setMissionPool([]);
    setPage(1);
    setHasMore(true);
    // restore persisted slider position for this event
    let restoredIndex = 0;
    if (sliderStateKey && typeof window !== 'undefined') {
      try {
        const stored = window.sessionStorage.getItem(sliderStateKey);
        if (stored) {
          const parsed = Number(stored);
          if (!Number.isNaN(parsed) && parsed >= 0) {
            restoredIndex = parsed;
          }
        }
      } catch {
        restoredIndex = 0;
      }
    }
    poolIndexRef.current = restoredIndex;
    if (!tasksEnabled) return;
    fetchTasksPage(1, true);
  }, [fetchTasksPage, locale, sliderStateKey, tasksEnabled, token]);

  React.useEffect(() => {
    if (missionPool.length === 0) return;
    if (poolIndexRef.current >= missionPool.length) {
      poolIndexRef.current = poolIndexRef.current % missionPool.length;
    }
    setMissionDeck(missionPool);
  }, [missionPool]);

  React.useEffect(() => {
    if (!swiperRef.current) return;
    if (!missionDeck.length) return;
    const target = poolIndexRef.current % missionDeck.length;
    const inst = swiperRef.current;
    if (typeof inst.slideToLoop === 'function') {
      inst.slideToLoop(target, 0);
    } else if (typeof inst.slideTo === 'function') {
      inst.slideTo(target, 0);
    }
  }, [missionDeck.length]);

  React.useEffect(() => {
    if (missionLoading) return;
    if (!hasMore || isLoadingMore) return;
    // Prefetch when we are within 6 items of the end of the current pool
    const remaining = missionPool.length - poolIndexRef.current;
    if (remaining <= 6) {
      fetchTasksPage(page);
    }
  }, [fetchTasksPage, hasMore, isLoadingMore, missionLoading, missionPool.length, page]);

  if (!token) {
    return null;
  }

  const introArray: string[] = [];
  for (let i = 0; i < 12; i += 1) {
    const candidate = t(`home.introRotating.${i}`, '');
    if (candidate) {
      introArray.push(candidate);
    }
  }
  const introMessageRef = React.useRef<string | null>(null);
  if (!introMessageRef.current) {
    introMessageRef.current =
      introArray.length > 0 ? introArray[Math.floor(Math.random() * introArray.length)] : '';
  }
  const introMessage = introMessageRef.current;

  if (!tasksEnabled) {
    return (
      <motion.div className="space-y-3 pb-24" style={bodyFont ? { fontFamily: bodyFont } : undefined} {...containerMotion}>
        <motion.section
          className="space-y-1 px-4"
          style={headingFont ? { fontFamily: headingFont } : undefined}
          {...fadeUpMotion}
        >
          <p className="text-sm font-semibold text-foreground">
            {t('home.welcomeLine').replace('{name}', displayName)}
          </p>
          {introMessage && <p className="text-xs text-muted-foreground">{introMessage}</p>}
        </motion.section>

        <motion.section className="space-y-2 px-4" {...fadeUpMotion}>
          <UploadActionCard
            token={token}
            accentColor={accentColor}
            secondaryAccent={secondaryAccent}
            radius={radius}
            bodyFont={bodyFont}
            requiresApproval={uploadsRequireApproval}
          />
        </motion.section>

        <motion.div {...fadeUpMotion}>
          <Separator />
        </motion.div>

        <motion.div {...fadeUpMotion}>
          <GalleryPreview token={token} />
        </motion.div>
      </motion.div>
    );
  }

  return (
    <motion.div className="space-y-0.5 pb-24" style={bodyFont ? { fontFamily: bodyFont } : undefined} {...containerMotion}>
      <motion.section
        className="space-y-1 px-4"
        style={headingFont ? { fontFamily: headingFont } : undefined}
        {...fadeUpMotion}
      >
        <p className="text-sm font-semibold text-foreground">
          {t('home.welcomeLine').replace('{name}', displayName)}
        </p>
        {introMessage && (
          <p className="text-xs text-muted-foreground">
            {introMessage}
          </p>
        )}
      </motion.section>

      {heroVisible && (
        <motion.div {...fadeScaleMotion}>
          <HeroCard
            name={displayName}
            eventName={eventNameDisplay}
            tasksCompleted={completedCount}
            t={t}
            branding={branding}
            onDismiss={dismissHero}
            ctaLabel={t('home.actions.items.tasks.label')}
            ctaHref={`/e/${encodeURIComponent(token)}/tasks`}
          />
        </motion.div>
      )}

      <motion.section className="space-y-0.5" style={headingFont ? { fontFamily: headingFont } : undefined} {...fadeUpMotion}>
        <div className="space-y-0.5">
          <MissionActionCard
            token={token}
            mission={missionDeck[0] ?? null}
            loading={missionLoading}
            onAdvance={advanceDeck}
            stack={missionDeck.slice(1)}
            initialIndex={poolIndexRef.current}
            swipeHintLabel={t('home.swipeHint')}
            onIndexChange={(idx, total) => {
              poolIndexRef.current = idx % Math.max(1, total);
              if (sliderStateKey && typeof window !== 'undefined') {
                try {
                  window.sessionStorage.setItem(sliderStateKey, String(poolIndexRef.current));
                } catch {
                  // ignore storage errors
                }
              }
            }}
            swiperRef={swiperRef}
          />
          <UploadActionCard
            token={token}
            accentColor={accentColor}
            secondaryAccent={secondaryAccent}
            radius={radius}
            bodyFont={bodyFont}
            requiresApproval={uploadsRequireApproval}
          />
          <EmotionActionCard />
        </div>
      </motion.section>

      <motion.div {...fadeUpMotion}>
        <Separator />
      </motion.div>

      <motion.div {...fadeUpMotion}>
        <GalleryPreview token={token} />
      </motion.div>
    </motion.div>
  );
}

function HeroCard({
  name,
  eventName,
  tasksCompleted,
  t,
  branding,
  onDismiss,
  ctaLabel,
  ctaHref,
}: {
  name: string;
  eventName: string;
  tasksCompleted: number;
  t: TranslateFn;
  branding: EventBranding;
  onDismiss: () => void;
  ctaLabel?: string;
  ctaHref?: string;
}) {
  const heroTitle = t('home.hero.title').replace('{name}', name);
  const heroDescription = t('home.hero.description').replace('{eventName}', eventName);
  const progressMessage = tasksCompleted > 0
    ? t('home.hero.progress.some').replace('{count}', `${tasksCompleted}`)
    : t('home.hero.progress.none');

  const style = React.useMemo(() => ({
    background: `linear-gradient(135deg, ${branding.primaryColor}, ${branding.secondaryColor})`,
    color: '#ffffff',
    fontFamily: branding.fontFamily ?? undefined,
  }), [branding.fontFamily, branding.primaryColor, branding.secondaryColor]);

  return (
    <Card className="relative overflow-hidden border-0 text-white shadow-md" style={style}>
      <Button
        type="button"
        variant="ghost"
        size="icon"
        className="absolute right-3 top-3 h-8 w-8 rounded-full bg-white/20 text-white hover:bg-white/30"
        onClick={onDismiss}
      >
        <X className="h-4 w-4" aria-hidden />
        <span className="sr-only">{t('common.actions.close')}</span>
      </Button>
      <CardHeader className="space-y-4 pr-10">
        <CardDescription className="text-sm text-white/80">{t('home.hero.subtitle')}</CardDescription>
        <CardTitle className="text-2xl font-bold leading-snug">{heroTitle}</CardTitle>
        <p className="text-sm text-white/85">{heroDescription}</p>
        <div className="flex flex-wrap items-center gap-3">
          <p className="text-sm font-medium text-white/90">{progressMessage}</p>
          {ctaHref && ctaLabel && (
            <Button
              variant="secondary"
              size="sm"
              asChild
              className="rounded-full bg-white/15 px-4 py-1 text-xs font-semibold uppercase tracking-wide text-white hover:bg-white/25"
            >
              <Link to={ctaHref}>{ctaLabel}</Link>
            </Button>
          )}
        </div>
      </CardHeader>
    </Card>
  );
}

type MissionPreview = {
  id: number;
  title: string;
  description?: string;
  duration?: number;
  emotion?: EmotionIdentity | null;
};

export function MissionActionCard({
  token,
  mission,
  loading,
  onAdvance,
  stack,
  initialIndex,
  onIndexChange,
  swiperRef,
  swipeHintLabel,
}: {
  token: string;
  mission: MissionPreview | null;
  loading: boolean;
  onAdvance: () => void;
  stack: MissionPreview[];
  initialIndex: number;
  onIndexChange: (index: number, total: number) => void;
  swiperRef: React.MutableRefObject<any>;
  swipeHintLabel?: string;
}) {
  const { branding } = useEventBranding();
  const radius = branding.buttons?.radius ?? 12;
  const primary = branding.buttons?.primary ?? branding.primaryColor;
  const secondary = branding.buttons?.secondary ?? branding.secondaryColor;
  const headingFont = branding.typography?.heading ?? branding.fontFamily ?? undefined;
  const bodyFont = branding.typography?.body ?? branding.fontFamily ?? undefined;
  const cards = mission ? [mission, ...stack] : stack;
  const shellRadius = `${radius + 10}px`;
  const normalizeText = (value: string | undefined | null) =>
    (value ?? '').trim().toLowerCase().replace(/\s+/g, ' ');
  const [expandedTaskId, setExpandedTaskId] = React.useState<number | null>(null);
  const lastSlideIndexRef = React.useRef<number>(initialIndex);
  const titleRefs = React.useRef(new Map<number, HTMLParagraphElement | null>());
  const [expandableTitles, setExpandableTitles] = React.useState<Record<number, boolean>>({});
  const [showSwipeHint, setShowSwipeHint] = React.useState(false);
  const hintTimeoutRef = React.useRef<number | null>(null);
  const hintStorageKey = token ? `guestMissionSwipeHintSeen_${token}` : 'guestMissionSwipeHintSeen';
  const motionEnabled = !prefersReducedMotion();
  const shouldShowHint = motionEnabled && cards.length > 1 && Boolean(swipeHintLabel);

  const dismissSwipeHint = React.useCallback((persist = true) => {
    if (!showSwipeHint) {
      return;
    }
    setShowSwipeHint(false);
    if (hintTimeoutRef.current) {
      window.clearTimeout(hintTimeoutRef.current);
      hintTimeoutRef.current = null;
    }
    if (persist && typeof window !== 'undefined') {
      try {
        window.sessionStorage.setItem(hintStorageKey, '1');
      } catch {
        // ignore storage exceptions
      }
    }
  }, [hintStorageKey, showSwipeHint]);

  React.useEffect(() => {
    if (!shouldShowHint) {
      setShowSwipeHint(false);
      return;
    }
    if (typeof window === 'undefined') {
      return;
    }
    try {
      if (window.sessionStorage.getItem(hintStorageKey)) {
        setShowSwipeHint(false);
        return;
      }
    } catch {
      // ignore storage exceptions
    }

    setShowSwipeHint(true);
    hintTimeoutRef.current = window.setTimeout(() => {
      setShowSwipeHint(false);
      try {
        window.sessionStorage.setItem(hintStorageKey, '1');
      } catch {
        // ignore storage exceptions
      }
    }, 3200);

    return () => {
      if (hintTimeoutRef.current) {
        window.clearTimeout(hintTimeoutRef.current);
        hintTimeoutRef.current = null;
      }
    };
  }, [hintStorageKey, shouldShowHint]);

  const measureTitleOverflow = React.useCallback(() => {
    setExpandableTitles((prev) => {
      let hasChange = false;
      const next = { ...prev };

      titleRefs.current.forEach((element, id) => {
        if (!element || element.dataset.collapsed !== 'true') {
          return;
        }
        const isOverflowing = element.scrollHeight > element.clientHeight + 1;
        if (next[id] !== isOverflowing) {
          next[id] = isOverflowing;
          hasChange = true;
        }
      });

      return hasChange ? next : prev;
    });
  }, []);

  React.useLayoutEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }
    const raf = window.requestAnimationFrame(measureTitleOverflow);
    return () => window.cancelAnimationFrame(raf);
  }, [measureTitleOverflow, cards, headingFont]);

  React.useEffect(() => {
    if (typeof window === 'undefined') {
      return;
    }
    const handleResize = () => {
      window.requestAnimationFrame(measureTitleOverflow);
    };
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, [measureTitleOverflow]);

  const renderCardContent = (card: MissionPreview | null) => {
    const theme = getEmotionTheme(card?.emotion ?? null);
    const emotionIcon = getEmotionIcon(card?.emotion ?? null);
    const durationMinutes = card?.duration ?? 3;
    const titleFont = headingFont ? { fontFamily: headingFont } : undefined;
    const gradientBackground = card ? theme.gradientBackground : `linear-gradient(135deg, ${primary}, ${secondary})`;
    const isExpanded = card ? expandedTaskId === card.id : false;
    const isExpandable = Boolean(card && expandableTitles[card.id]);
    const titleClamp = isExpanded ? '' : 'line-clamp-2 sm:line-clamp-3';
    const titleClasses = `text-xl font-semibold leading-snug text-slate-900 sm:text-2xl break-words py-1 min-h-[3.75rem] sm:min-h-[4.5rem] ${titleClamp}`;
    const titleId = card ? `task-title-${card.id}` : undefined;
    const toggleExpanded = () => {
      if (!card) return;
      setExpandedTaskId((prev) => (prev === card.id ? null : card.id));
    };

    return (
      <div
        className="relative isolate overflow-hidden"
        style={{
          borderRadius: shellRadius,
          background: gradientBackground,
          boxShadow: `0 18px 50px ${primary}35, 0 6px 18px ${primary}22`,
          fontFamily: bodyFont,
        }}
      >
        <div
          className="absolute inset-0 opacity-25"
          style={{
            backgroundImage:
              'linear-gradient(90deg, rgba(255,255,255,0.18) 1px, transparent 1px), linear-gradient(0deg, rgba(255,255,255,0.18) 1px, transparent 1px)',
            backgroundSize: '26px 26px',
          }}
        />
        <div className="absolute -left-12 -top-10 h-32 w-32 rounded-full bg-white/40 blur-3xl" />
        <div className="absolute -right-10 bottom-0 h-28 w-28 rounded-full bg-white/25 blur-3xl" />

        <div className="relative z-10 m-3 rounded-2xl border border-white/35 bg-white/80 px-4 py-4 shadow-lg backdrop-blur-xl">
          <div className="flex items-center justify-between gap-3">
            <div className="flex items-center gap-3">
              <div className="relative">
                <div className="absolute inset-0 rounded-full bg-white/60 blur-xl" />
                <Avatar className="relative h-12 w-12 border border-white/50 bg-white/80 shadow-md">
                  <AvatarFallback className="text-xl">{emotionIcon}</AvatarFallback>
                </Avatar>
              </div>
              <div className="space-y-1">
                <Badge
                  className="border-0 text-[11px] font-semibold uppercase tracking-wide text-white shadow"
                  style={{ backgroundImage: gradientBackground }}
                >
                  {card?.emotion?.name ?? 'Fotoaufgabe'}
                </Badge>
                <div className="flex items-center gap-2 text-xs font-medium text-slate-600">
                  <Sparkles className="h-4 w-4 text-amber-500" aria-hidden />
                  <span>Foto-Challenge</span>
                </div>
              </div>
            </div>

            <div className="hidden items-center gap-1 rounded-full border border-white/70 bg-white/80 px-3 py-1 text-xs font-semibold text-slate-700 shadow-sm sm:flex">
              <Timer className="mr-1 h-3.5 w-3.5 text-slate-500" aria-hidden />
              <span>ca. {durationMinutes} min</span>
            </div>
          </div>

          <div className="mt-4 text-center">
            {card ? (
              isExpandable ? (
                <button
                  type="button"
                  onClick={toggleExpanded}
                  className="mx-auto flex w-full flex-col items-center gap-1 text-center"
                  aria-expanded={isExpanded}
                  aria-controls={titleId}
                >
                  <p
                    id={titleId}
                    ref={(node) => {
                      if (card) {
                        titleRefs.current.set(card.id, node);
                      }
                    }}
                    data-collapsed={!isExpanded}
                    className={titleClasses}
                    style={{ ...titleFont, textShadow: '0 6px 18px rgba(15,23,42,0.28)' }}
                  >
                    {card.title}
                  </p>
                  <ChevronDown
                    className={`h-4 w-4 text-slate-600 transition-transform ${isExpanded ? 'rotate-180' : ''}`}
                    aria-hidden
                  />
                  <span className="sr-only">Titel ein- oder ausklappen</span>
                </button>
              ) : (
                <p
                  id={titleId}
                  ref={(node) => {
                    if (card) {
                      titleRefs.current.set(card.id, node);
                    }
                  }}
                  data-collapsed={!isExpanded}
                  className={titleClasses}
                  style={{ ...titleFont, textShadow: '0 6px 18px rgba(15,23,42,0.28)' }}
                >
                  {card.title}
                </p>
              )
            ) : loading ? (
              <div className="space-y-2">
                <Skeleton className="mx-auto h-6 w-3/4" />
                <Skeleton className="mx-auto h-4 w-full" />
                <Skeleton className="mx-auto h-4 w-5/6" />
              </div>
            ) : (
              <p className="text-sm text-slate-600">Ziehe deine erste Mission oder wähle eine Stimmung.</p>
            )}
          </div>

          {card?.description && normalizeText(card.title) !== normalizeText(card.description) ? (
            <div className="mt-4 space-y-2">
              <div className="flex items-center justify-between">
                <div className="space-y-1 text-left">
                  <p className="text-sm font-semibold text-slate-800" style={titleFont}>
                    {card.title}
                  </p>
                  <p className="text-sm leading-relaxed text-slate-600">{card.description}</p>
                </div>
              </div>
            </div>
          ) : null}

          <div className="mt-4 grid grid-cols-[2fr_1fr] gap-2">
            <Button
              asChild
              className="w-full text-white shadow-lg"
              style={{
                borderRadius: `${radius}px`,
                background: `linear-gradient(120deg, ${primary}, ${secondary})`,
                boxShadow: `0 12px 28px ${primary}25`,
              }}
            >
              <Link
                to={
                  card
                    ? `/e/${encodeURIComponent(token)}/upload?task=${card.id}`
                    : `/e/${encodeURIComponent(token)}/tasks`
                }
                className="inline-flex items-center justify-center gap-2"
              >
                <Camera className="h-4 w-4" aria-hidden />
                <span>Aufgabe starten</span>
              </Link>
            </Button>
            <Button
              type="button"
              variant="secondary"
              className="w-full border border-slate-200 bg-white/80 text-slate-800 shadow-sm backdrop-blur"
              onClick={() => {
                dismissSwipeHint();
                onAdvance();
              }}
              disabled={loading}
              style={{
                borderRadius: `${radius}px`,
              }}
            >
              <RefreshCw className={`mr-2 h-4 w-4 ${loading ? 'animate-spin' : ''}`} aria-hidden />
              <span className="hidden sm:inline">Andere</span>
            </Button>
          </div>
        </div>
      </div>
    );
  };

  const slides = cards.length ? cards : [mission ?? null];
  const initialSlide = Math.min(initialIndex, Math.max(0, slides.length - 1));

  return (
    <Card className="border-0 bg-transparent py-2 shadow-none" style={{ fontFamily: bodyFont }}>
      <CardContent className="px-0 py-0">
        <div className="relative min-h-[240px] px-2 sm:min-h-[260px]" data-testid="mission-card-stack">
          <AnimatePresence initial={false}>
            {showSwipeHint ? (
              <motion.div
                className="pointer-events-none absolute inset-x-0 bottom-2 z-10 flex justify-center"
                initial={{ opacity: 0, y: 6 }}
                animate={{ opacity: 1, y: 0 }}
                exit={{ opacity: 0, y: 6 }}
                transition={{ duration: 0.2, ease: [0.22, 0.61, 0.36, 1] }}
              >
                <div className="flex items-center gap-2 rounded-full border border-white/40 bg-white/85 px-3 py-1 text-[10px] font-semibold uppercase tracking-[0.18em] text-slate-700 shadow-sm backdrop-blur">
                  <motion.span
                    animate={{ x: [-6, 0, -6] }}
                    transition={{ duration: 1.6, repeat: Infinity, ease: 'easeInOut' }}
                  >
                    <ArrowLeft className="h-3.5 w-3.5" aria-hidden />
                  </motion.span>
                  <span>{swipeHintLabel}</span>
                  <motion.span
                    animate={{ x: [6, 0, 6] }}
                    transition={{ duration: 1.6, repeat: Infinity, ease: 'easeInOut' }}
                  >
                    <ArrowRight className="h-3.5 w-3.5" aria-hidden />
                  </motion.span>
                </div>
              </motion.div>
            ) : null}
          </AnimatePresence>
          <Swiper
            effect="cards"
            modules={[EffectCards]}
            cardsEffect={{
              perSlideRotate: 1,
              perSlideOffset: 1,
              slideShadows: false,
            }}
            slidesPerView={1}
            grabCursor
            allowTouchMove
            loop={slides.length > 1}
            initialSlide={initialSlide}
            onSwiper={(instance) => {
              swiperRef.current = instance;
              if (initialSlide > 0) {
                instance.slideToLoop ? instance.slideToLoop(initialSlide, 0) : instance.slideTo(initialSlide, 0);
              }
            }}
            onSlideChange={(instance) => {
              const realIndex = typeof instance.realIndex === 'number' ? instance.realIndex : instance.activeIndex ?? 0;
              if (realIndex !== lastSlideIndexRef.current) {
                setExpandedTaskId(null);
                dismissSwipeHint();
              }
              lastSlideIndexRef.current = realIndex;
              onIndexChange(realIndex, slides.length);
            }}
            className="!pb-1"
            style={{ paddingLeft: '0.25rem', paddingRight: '0.25rem' }}
          >
            {slides.map((card, index) => {
              const key = `card-${card?.id ?? 'x'}-${index}`;
              return (
                <SwiperSlide key={key} className="!h-auto">
                  <div className="mx-auto w-full max-w-[calc(100vw-2rem)]">
                    {renderCardContent(card)}
                  </div>
                </SwiperSlide>
              );
            })}
          </Swiper>
        </div>
      </CardContent>
    </Card>
  );
}

function EmotionActionCard() {
  return (
    <Card className="border border-muted/40 shadow-sm">
      <CardHeader className="py-[4px]">
        <CardTitle>Wähle eine Stimmung und erhalte eine passende Aufgabe</CardTitle>
        <CardDescription className="text-sm text-muted-foreground">
          Tippe deinen Mood, wir picken die nächste Mission für dich.
        </CardDescription>
      </CardHeader>
      <CardContent className="py-[4px]">
        <EmotionPicker variant="embedded" showSkip={false} />
      </CardContent>
    </Card>
  );
}

export function UploadActionCard({
  token,
  accentColor,
  secondaryAccent,
  radius,
  bodyFont,
  requiresApproval,
}: {
  token: string;
  accentColor: string;
  secondaryAccent: string;
  radius: number;
  bodyFont?: string;
  requiresApproval: boolean;
}) {
  const inputRef = React.useRef<HTMLInputElement | null>(null);
  const [busy, setBusy] = React.useState(false);
  const [message, setMessage] = React.useState<string | null>(null);
  const navigate = useNavigate();

  const { upload, uploading, error, warning, progress, reset } = useDirectUpload({
    eventToken: token,
    taskId: undefined,
    emotionSlug: undefined,
    onCompleted: () => {
      setMessage(null);
      navigate(`/e/${encodeURIComponent(token)}/gallery`);
    },
  });

  const onPick = React.useCallback(
    async (e: React.ChangeEvent<HTMLInputElement>) => {
      const file = e.target.files?.[0];
      if (!file) return;
      setBusy(true);
      setMessage(null);
      try {
        await upload(file);
      } finally {
        setBusy(false);
      }
      if (inputRef.current) {
        inputRef.current.value = '';
      }
    },
    [upload]
  );

  React.useEffect(() => {
    if (error) {
      setMessage(error);
    } else if (warning) {
      setMessage(warning);
    } else {
      setMessage(null);
    }
  }, [error, warning]);

  return (
    <Card
      className="gap-2 overflow-hidden border border-muted/30 bg-[var(--guest-surface)] py-2 shadow-sm dark:border-slate-800/60 dark:bg-slate-950/70"
      data-testid="upload-action-card"
      style={{
        borderRadius: `${radius}px`,
        fontFamily: bodyFont,
      }}
    >
      <CardContent className="flex flex-col gap-1.5 px-4 py-2">
        <Button
          type="button"
          className="text-white justify-center"
          style={{
            borderRadius: `${radius}px`,
            background: `linear-gradient(120deg, ${accentColor}, ${secondaryAccent})`,
            boxShadow: `0 12px 28px ${accentColor}25`,
          }}
          disabled={busy || uploading}
          onClick={() => {
            reset();
            setMessage(null);
            inputRef.current?.click();
          }}
        >
          <span className="flex items-center gap-2">
            <UploadCloud className={`h-5 w-5 ${busy || uploading ? 'animate-pulse' : ''}`} aria-hidden />
            <span>{busy || uploading ? 'Lädt …' : 'Direkt hochladen'}</span>
          </span>
        </Button>
        <input
          ref={inputRef}
          type="file"
          accept="image/*"
          capture="environment"
          className="hidden"
          onChange={onPick}
        />
        <p className="text-xs text-muted-foreground">
          Kamera öffnen oder ein Foto aus deiner Galerie wählen. Offline möglich – wir laden später hoch.
        </p>
        {requiresApproval ? (
          <p className="text-xs font-medium text-amber-700 dark:text-amber-300">
            Deine Fotos werden kurz geprüft und erscheinen danach in der Galerie.
          </p>
        ) : null}
        {message && (
          <p className="text-xs font-medium text-amber-600 dark:text-amber-400">
            {message} {progress > 0 && progress < 100 ? `(${Math.round(progress)}%)` : ''}
          </p>
        )}
      </CardContent>
    </Card>
  );
}
