import { useEffect, useMemo, useState } from 'react';
import {
  Dimensions,
  KeyboardAvoidingView,
  Modal,
  Platform,
  Pressable,
  Text,
  TextInput,
  View,
} from 'react-native';
import {
  initialWindowMetrics,
  SafeAreaProvider,
  SafeAreaView,
} from 'react-native-safe-area-context';
import { Send, X } from 'lucide-react-native';
import * as Haptics from 'expo-haptics';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  Easing,
  Extrapolation,
  interpolate,
  runOnJS,
  useAnimatedStyle,
  useSharedValue,
  withSpring,
  withTiming,
} from 'react-native-reanimated';
import { REACTION_EMOJIS, type Reactions, type Human } from '@/api/types';
import { resolveHumanDisplay } from '@/lib/humans';
import { sanitizeReactionText } from '@/lib/firestore-particles';
import { cn } from '@/lib/utils';
import { useSuspendPlayback } from '@/hooks/use-suspend-playback';

const TEXT_REACTION_MAX = 40;
const SCREEN_HEIGHT = Dimensions.get('window').height;
const ANIMATION_MS = 240;

const EMOJI_SET = new Set<string>(REACTION_EMOJIS);

interface ReactionSheetProps {
  open: boolean;
  onClose: () => void;
  reactions: Reactions;
  currentHumanId: string;
  humans: Human[] | undefined;
  /**
   * Toggle a reaction (emoji or text). Adds if the current human hasn't
   * reacted, removes if they have. Mirrors desktop's `onToggle` exactly.
   */
  onToggle: (key: string) => void;
}

/**
 * Slide-up reaction sheet — the mobile replacement for desktop's right-edge
 * reaction stack. Tap an emoji to toggle, or send a custom text reaction
 * (40-char cap). Existing reactions appear as toggleable pills at the top.
 *
 * Playback is suspended via `useSuspendPlayback` while the sheet is open so
 * the active particle doesn't auto-advance under the user. Drag the sheet
 * down past 30% of its travel to dismiss; everything else springs back.
 */
export function ReactionSheet({
  open,
  onClose,
  reactions,
  currentHumanId,
  humans,
  onToggle,
}: ReactionSheetProps) {
  // Suspend playback whenever the sheet is mounted-and-open. The Modal
  // controls visibility so we tie the suspender to `open` directly.
  useSuspendPlayback(open, 'reactions-sheet');

  // We mount the modal slightly delayed from `open` so the slide-up animation
  // has its starting position rendered. Using local `mounted` state lets us
  // play the close animation before unmounting.
  const [mounted, setMounted] = useState(false);
  const translateY = useSharedValue(SCREEN_HEIGHT);

  useEffect(() => {
    if (open) {
      // Schedule the slide-in after the modal mounts (handled at render time).
      requestAnimationFrame(() => {
        translateY.value = withSpring(0, {
          damping: 24,
          stiffness: 260,
          mass: 0.7,
        });
      });
    } else if (mounted) {
      translateY.value = withTiming(
        SCREEN_HEIGHT,
        { duration: ANIMATION_MS, easing: Easing.in(Easing.cubic) },
        (finished) => {
          if (finished) runOnJS(setMounted)(false);
        },
      );
    }
    // intentional: only react to `open`. Closing animation reads from `mounted`.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [open]);

  const dismiss = () => {
    onClose();
  };

  const sheetPan = Gesture.Pan()
    .activeOffsetY(10)
    .failOffsetX([-25, 25])
    .onUpdate((e) => {
      'worklet';
      // Reanimated shared values are mutated by design; react-hooks/immutability
      // doesn't model worklets, so the mutations below are flagged spuriously.
      // eslint-disable-next-line react-hooks/immutability
      translateY.value = Math.max(0, e.translationY);
    })
    .onEnd((e) => {
      'worklet';
      if (e.translationY > 120 || e.velocityY > 800) {
        runOnJS(dismiss)();
      } else {
        // eslint-disable-next-line react-hooks/immutability
        translateY.value = withSpring(0, {
          damping: 24,
          stiffness: 260,
          mass: 0.7,
        });
      }
    });

  const sheetStyle = useAnimatedStyle(() => ({
    transform: [{ translateY: translateY.value }],
  }));

  const backdropStyle = useAnimatedStyle(() => {
    const opacity = interpolate(
      translateY.value,
      [0, SCREEN_HEIGHT * 0.7],
      [0.55, 0],
      Extrapolation.CLAMP,
    );
    return { opacity };
  });

  // --- Existing reaction pills ---
  const activeEmojis = REACTION_EMOJIS.filter(
    (e) => reactions?.[e] && (reactions[e]?.length ?? 0) > 0,
  );
  const activeTextKeys = useMemo(
    () =>
      Object.keys(reactions ?? {}).filter(
        (k) => !EMOJI_SET.has(k) && (reactions?.[k]?.length ?? 0) > 0,
      ),
    [reactions],
  );

  // --- Text reaction input ---
  const [text, setText] = useState('');

  // Mount on open (staying mounted through the exit animation) and clear the
  // input. Render-time adjustment avoids a setState-in-effect cascade.
  const [prevOpen, setPrevOpen] = useState(open);
  if (open !== prevOpen) {
    setPrevOpen(open);
    if (open) {
      setMounted(true);
      setText('');
    }
  }

  const submitText = () => {
    const trimmed = text.trim();
    if (!trimmed) return;
    void Haptics.selectionAsync();
    onToggle(trimmed.slice(0, TEXT_REACTION_MAX));
    setText('');
    onClose();
  };

  const handleEmoji = (emoji: string) => {
    void Haptics.selectionAsync();
    onToggle(emoji);
    onClose();
  };

  if (!mounted) return null;

  return (
    <Modal
      visible={mounted}
      transparent
      animationType="none"
      onRequestClose={dismiss}
    >
      <SafeAreaProvider initialMetrics={initialWindowMetrics}>
        <View style={{ flex: 1 }}>
          <Animated.View
            pointerEvents={open ? 'auto' : 'none'}
            style={[
              { position: 'absolute', inset: 0, backgroundColor: 'black' },
              backdropStyle,
            ]}
          >
            <Pressable style={{ flex: 1 }} onPress={dismiss} />
          </Animated.View>

          <KeyboardAvoidingView
            behavior={Platform.OS === 'ios' ? 'padding' : undefined}
            style={{ flex: 1, justifyContent: 'flex-end' }}
            pointerEvents="box-none"
          >
            <GestureDetector gesture={sheetPan}>
              <Animated.View
                style={[
                  {
                    backgroundColor: '#1c1c1c',
                    borderTopLeftRadius: 22,
                    borderTopRightRadius: 22,
                    overflow: 'hidden',
                  },
                  sheetStyle,
                ]}
              >
                <SafeAreaView edges={['bottom']}>
                  <View className="px-5 pt-3 pb-2 items-center">
                    {/* Drag handle — affords downward dismissal at a glance. */}
                    <View className="bg-white/25 h-1 w-12 rounded-full mb-3" />

                    <View className="flex-row items-center justify-between w-full">
                      <Text className="text-white text-base font-semibold">
                        React
                      </Text>
                      <Pressable
                        onPress={dismiss}
                        hitSlop={12}
                        accessibilityLabel="Close reactions"
                      >
                        <X color="rgba(255,255,255,0.6)" size={20} />
                      </Pressable>
                    </View>
                  </View>

                  {/* Existing reactions row — tap a pill to toggle yours. */}
                  {activeEmojis.length > 0 || activeTextKeys.length > 0 ? (
                    <View className="px-5 pb-3 flex-row flex-wrap gap-2">
                      {activeEmojis.map((emoji) => {
                        const reactors = reactions![emoji];
                        const isMine = reactors.includes(currentHumanId);
                        return (
                          <Pressable
                            key={emoji}
                            onPress={() => handleEmoji(emoji)}
                            className={cn(
                              'flex-row items-center gap-1.5 rounded-full px-3 py-1.5',
                              isMine
                                ? 'bg-white/25 border border-white/40'
                                : 'bg-white/10',
                            )}
                          >
                            <Text className="text-base">{emoji}</Text>
                            <Text className="text-white/85 text-xs font-semibold">
                              {reactors.length}
                            </Text>
                          </Pressable>
                        );
                      })}

                      {activeTextKeys.map((key) => {
                        const reactors = reactions![key];
                        const isMine = reactors.includes(currentHumanId);
                        const firstReactor = resolveHumanDisplay(
                          reactors[0],
                          humans,
                        );
                        return (
                          <Pressable
                            key={key}
                            onPress={() => handleEmoji(key)}
                            className={cn(
                              'flex-row items-center gap-1.5 rounded-full px-2.5 py-1.5 max-w-[220px]',
                              isMine
                                ? 'bg-white/25 border border-white/40'
                                : 'bg-white/10',
                            )}
                          >
                            <View className="bg-white/20 h-5 w-5 items-center justify-center rounded-full">
                              <Text className="text-white text-[9px] font-semibold">
                                {firstReactor.initials}
                              </Text>
                            </View>
                            <Text
                              className="text-white/90 text-xs"
                              numberOfLines={1}
                            >
                              {key}
                            </Text>
                            {reactors.length > 1 ? (
                              <Text className="text-white/60 text-xs font-medium">
                                {reactors.length}
                              </Text>
                            ) : null}
                          </Pressable>
                        );
                      })}
                    </View>
                  ) : null}

                  {/* Quick-pick emoji palette — six big tappable buttons. */}
                  <View className="px-3 pt-2 pb-4 flex-row justify-around">
                    {REACTION_EMOJIS.slice(0, 6).map((emoji) => {
                      const isMine =
                        reactions?.[emoji]?.includes(currentHumanId) ?? false;
                      return (
                        <Pressable
                          key={emoji}
                          onPress={() => handleEmoji(emoji)}
                          accessibilityLabel={`React with ${emoji}`}
                          className={cn(
                            'h-14 w-14 items-center justify-center rounded-full',
                            isMine ? 'bg-white/25' : 'bg-white/10',
                          )}
                        >
                          <Text style={{ fontSize: 28 }}>{emoji}</Text>
                        </Pressable>
                      );
                    })}
                  </View>

                  {/* Text reaction input — 40-char cap matches desktop. */}
                  <View className="px-4 pb-4 flex-row items-center gap-2">
                    <View className="flex-1 bg-white/10 rounded-full px-4 py-2.5">
                      <TextInput
                        value={text}
                        onChangeText={(v) =>
                          setText(
                            sanitizeReactionText(v).slice(0, TEXT_REACTION_MAX),
                          )
                        }
                        placeholder="Send a quick reply..."
                        placeholderTextColor="rgba(255,255,255,0.4)"
                        maxLength={TEXT_REACTION_MAX}
                        autoCapitalize="none"
                        autoCorrect={false}
                        onSubmitEditing={submitText}
                        returnKeyType="send"
                        className="text-white text-base"
                      />
                    </View>
                    <Pressable
                      onPress={submitText}
                      disabled={text.trim().length === 0}
                      accessibilityLabel="Send text reaction"
                      className={cn(
                        'h-11 w-11 items-center justify-center rounded-full',
                        text.trim().length === 0 ? 'bg-white/10' : 'bg-white',
                      )}
                    >
                      <Send
                        color={
                          text.trim().length === 0
                            ? 'rgba(255,255,255,0.3)'
                            : 'black'
                        }
                        size={18}
                        strokeWidth={2}
                      />
                    </Pressable>
                  </View>
                </SafeAreaView>
              </Animated.View>
            </GestureDetector>
          </KeyboardAvoidingView>
        </View>
      </SafeAreaProvider>
    </Modal>
  );
}
