import { router } from 'expo-router';
import { SymbolView } from 'expo-symbols';
import { useEffect, useState } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  Easing,
  Extrapolation,
  FadeIn,
  FadeOut,
  interpolate,
  interpolateColor,
  runOnJS,
  useAnimatedStyle,
  useSharedValue,
  withSpring,
  withTiming,
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

import { Fonts, Spacing } from '@/constants/theme';
import { tasks } from '@/data/task-repository';
import { PopButton } from '@/design/pop-button';
import { resolveSwipe, SWIPE_DISTANCE } from '@/design/shared';
import { useDecisionEngine } from '@/design/use-decision-engine';
import { type GlideDecision } from '@/domain/glide';
import { useColorScheme } from '@/hooks/use-color-scheme';
import { useTheme } from '@/hooks/use-theme';
import { gestureStartHaptic, selectionHaptic } from '@/lib/haptics';

const ROUNDED = Fonts?.rounded;

/**
 * Planning: the once-in-a-while sorting session. One task at a time, one
 * question — "Are you resistant?" Swipe right to ready it, left to let it
 * soften. Presented on demand as a focused, full-screen session.
 */
export function PlanningScreen() {
  const palette = useTheme();
  const scheme = useColorScheme();
  const insets = useSafeAreaInsets();
  const { status, task, upcoming, answer, decided, remaining } = useDecisionEngine();

  // Opening Plan starts a new Action session: zero out the "done this session"
  // tally so it reflects only what gets actioned from this pass's ready batch.
  useEffect(() => {
    tasks.startPlanSession();
  }, []);

  const shadowColor = scheme === 'dark' ? 'rgba(0,0,0,0.55)' : palette.border;

  const dragX = useSharedValue(0);
  const dragY = useSharedValue(0);
  const cue = useSharedValue(0);
  const enter = useSharedValue(0);

  // The just-decided card flies off as a separate overlay (rendered below the deck),
  // decoupled from the deck's drag so resetting the drag for the next card can never make
  // the old one snap back to centre for a frame — that was the transition glitch.
  const outX = useSharedValue(0);
  const outY = useSharedValue(0);
  const [outgoing, setOutgoing] = useState<{ title: string; dir: 1 | -1 } | null>(null);

  // Lift a fresh card up from the first-behind slot whenever the task changes —
  // a quick ease-out rise (no overshoot) so it reads as the next card being
  // promoted into place rather than popping in.
  useEffect(() => {
    enter.value = 0;
    enter.value = withTiming(1, { duration: 200, easing: Easing.out(Easing.cubic) });
  }, [task?.id, enter]);

  const close = () => router.back();

  // Hand the decided card off to the fly-off overlay and advance the deck in one step. The
  // overlay carries the card off-screen from wherever it was released, while the deck's drag
  // instantly resets for the incoming card — so the outgoing card never snaps back to centre,
  // and the next card simply promotes up behind it.
  const flyOff = (decision: GlideDecision, fromX: number, fromY: number) => {
    const current = task;
    if (!current) return;
    const dir: 1 | -1 = decision === 'clear' ? 1 : -1;
    setOutgoing({ title: current.title, dir });
    outX.value = fromX;
    outY.value = fromY;
    outX.value = withTiming(dir * 640, { duration: 240, easing: Easing.in(Easing.cubic) }, (finished) => {
      if (finished) runOnJS(setOutgoing)(null);
    });
    outY.value = withTiming(fromY + 90, { duration: 240 });
    answer(decision); // advance the deck
    dragX.value = 0; // reset the live card for the one promoting in behind the overlay
    dragY.value = 0;
  };

  // Big buttons feel identical to a swipe: fly the card off from centre.
  const flickOff = (decision: GlideDecision) => {
    if (status !== 'active') return;
    flyOff(decision, 0, 0);
  };

  const gesture = Gesture.Pan()
    .enabled(status === 'active')
    .activeOffsetX([-12, 12])
    .failOffsetY([-40, 40])
    .onStart(() => {
      cue.value = 0;
      runOnJS(gestureStartHaptic)();
    })
    .onUpdate((event) => {
      dragX.value = event.translationX;
      dragY.value = event.translationY * 0.5;
      const next = dragX.value < -SWIPE_DISTANCE ? -1 : dragX.value > SWIPE_DISTANCE ? 1 : 0;
      if (next !== cue.value) {
        cue.value = next;
        if (next !== 0) runOnJS(selectionHaptic)();
      }
    })
    .onEnd((event) => {
      const decision = resolveSwipe(event.translationX, event.velocityX);
      cue.value = 0;
      if (decision) {
        // Hand off from the exact release position so the fly-off continues the swipe.
        runOnJS(flyOff)(decision, dragX.value, dragY.value);
        return;
      }
      dragX.value = withSpring(0, { damping: 12, stiffness: 180 });
      dragY.value = withSpring(0, { damping: 12, stiffness: 180 });
    });

  const cardStyle = useAnimatedStyle(() => {
    // Promote from the first-behind slot's transform into centre.
    const promoteX = interpolate(enter.value, [0, 1], [-13, 0]);
    const promoteY = interpolate(enter.value, [0, 1], [16, 0]);
    const promoteRot = interpolate(enter.value, [0, 1], [-5, 0]);
    const promoteScale = interpolate(enter.value, [0, 1], [0.97, 1]);
    const dragRot = interpolate(dragX.value, [-300, 0, 300], [-18, 0, 18], Extrapolation.CLAMP);
    return {
      backgroundColor: interpolateColor(
        dragX.value,
        [-140, 0, 140],
        [palette.resistanceSoft, palette.surface, palette.clearSoft]
      ),
      transform: [
        { translateX: dragX.value + promoteX },
        { translateY: dragY.value + promoteY },
        { rotateZ: `${dragRot + promoteRot}deg` },
        { scale: promoteScale },
      ],
    };
  });

  const readyStampStyle = useAnimatedStyle(() => ({
    opacity: interpolate(dragX.value, [50, 130], [0, 1], Extrapolation.CLAMP),
    transform: [
      { rotate: '-14deg' },
      { scale: interpolate(dragX.value, [50, 130], [0.6, 1], Extrapolation.CLAMP) },
    ],
  }));
  const resistStampStyle = useAnimatedStyle(() => ({
    opacity: interpolate(dragX.value, [-130, -50], [1, 0], Extrapolation.CLAMP),
    transform: [
      { rotate: '14deg' },
      { scale: interpolate(dragX.value, [-130, -50], [1, 0.6], Extrapolation.CLAMP) },
    ],
  }));

  // The fly-off overlay: carries the decided card off-screen from its release point, fading
  // out near the edge. Independent of `dragX`, so it never interferes with the next card.
  const outStyle = useAnimatedStyle(() => {
    const rot = interpolate(outX.value, [-300, 0, 300], [-18, 0, 18], Extrapolation.CLAMP);
    const fade = interpolate(Math.abs(outX.value), [320, 600], [1, 0], Extrapolation.CLAMP);
    return {
      opacity: fade,
      transform: [
        { translateX: outX.value },
        { translateY: outY.value },
        { rotateZ: `${rot}deg` },
      ],
    };
  });

  const cleared = status === 'empty';

  return (
    <View style={[styles.root, { backgroundColor: palette.background }]}>
      <View
        style={[
          styles.content,
          {
            paddingTop: insets.top + Spacing.three,
            paddingBottom: insets.bottom + Spacing.four,
          },
        ]}>
        {/* Header: progress + close */}
        <View style={styles.header}>
          <View
            style={[
              styles.streak,
              { backgroundColor: palette.accent, borderColor: palette.border, boxShadow: `3px 3px 0 ${shadowColor}` },
            ]}>
            <Text style={styles.streakText}>⚡ {status === 'loading' ? '—' : decided} sorted</Text>
          </View>
          <Pressable
            accessibilityRole="button"
            accessibilityLabel="Done planning"
            onPressIn={selectionHaptic}
            onPress={close}
            style={[styles.close, { backgroundColor: palette.surface, borderColor: palette.border }]}>
            <SymbolView name="xmark" size={16} tintColor={palette.text} weight="bold" fallback={null} />
          </Pressable>
        </View>

        <Text style={[styles.question, { color: palette.text }]}>
          {cleared ? 'Caught up.' : 'Are you resistant?'}
        </Text>
        {!cleared ? (
          <Text style={[styles.subhead, { color: palette.textSecondary }]}>
            {status === 'loading' ? '—' : remaining} to go
          </Text>
        ) : null}

        {/* Deck */}
        <View style={styles.stage}>
          <View style={styles.deck}>
            {status === 'active'
              ? Array.from({ length: Math.min(upcoming.length, 2) }).map((_, index) => (
                  // Keyed by slot, not task — the depth cards hold their position
                  // through every advance and only fade when the deck shrinks.
                  <Animated.View
                    key={`slot-${index}`}
                    entering={FadeIn.duration(160)}
                    exiting={FadeOut.duration(160)}
                    style={[
                      styles.card,
                      styles.behindCard,
                      {
                        backgroundColor: palette.surface,
                        borderColor: palette.border,
                        boxShadow: `5px 6px 0 ${shadowColor}`,
                        transform: [
                          { translateY: index === 0 ? 16 : 30 },
                          { translateX: index === 0 ? -13 : 14 },
                          { rotate: index === 0 ? '-5deg' : '6deg' },
                          { scale: index === 0 ? 0.97 : 0.94 },
                        ],
                      },
                    ]}
                  />
                ))
              : null}

            <GestureDetector gesture={gesture}>
              <Animated.View
                key={task?.id ?? status}
                entering={FadeIn.duration(180)}
                style={[
                  styles.card,
                  styles.topCard,
                  { backgroundColor: palette.surface, borderColor: palette.border, boxShadow: `6px 7px 0 ${shadowColor}` },
                  cardStyle,
                ]}>
                <Animated.View
                  pointerEvents="none"
                  style={[styles.stamp, styles.readyStamp, { backgroundColor: palette.clear, borderColor: palette.border }, readyStampStyle]}>
                  <Text style={styles.stampText}>READY!</Text>
                </Animated.View>
                <Animated.View
                  pointerEvents="none"
                  style={[styles.stamp, styles.resistStamp, { backgroundColor: palette.resistance, borderColor: palette.border }, resistStampStyle]}>
                  <Text style={styles.stampText}>LATER!</Text>
                </Animated.View>

                {task ? (
                  <Text style={[styles.cardTitle, { color: palette.text }]}>{task.title}</Text>
                ) : (
                  <View style={styles.emptyCard}>
                    <Text style={styles.emptyEmoji}>{status === 'loading' ? '🌀' : '🎉'}</Text>
                    <Text style={[styles.emptyTitle, { color: palette.text }]}>
                      {status === 'loading' ? 'Shuffling…' : 'Deck cleared!'}
                    </Text>
                  </View>
                )}
              </Animated.View>
            </GestureDetector>

            {outgoing ? (
              <Animated.View
                pointerEvents="none"
                style={[
                  styles.card,
                  styles.topCard,
                  styles.outgoingCard,
                  {
                    backgroundColor: outgoing.dir === 1 ? palette.clearSoft : palette.resistanceSoft,
                    borderColor: palette.border,
                    boxShadow: `6px 7px 0 ${shadowColor}`,
                  },
                  outStyle,
                ]}>
                <View
                  style={[
                    styles.stamp,
                    outgoing.dir === 1 ? styles.readyStamp : styles.resistStamp,
                    {
                      backgroundColor: outgoing.dir === 1 ? palette.clear : palette.resistance,
                      borderColor: palette.border,
                      transform: [{ rotate: outgoing.dir === 1 ? '-14deg' : '14deg' }],
                    },
                  ]}>
                  <Text style={styles.stampText}>{outgoing.dir === 1 ? 'READY!' : 'LATER!'}</Text>
                </View>
                <Text style={[styles.cardTitle, { color: palette.text }]}>{outgoing.title}</Text>
              </Animated.View>
            ) : null}
          </View>
        </View>

        {/* Big buttons */}
        {cleared ? (
          <PopButton
            label="Done"
            symbol="checkmark"
            background={palette.clear}
            textColor="#062313"
            shadowColor={shadowColor}
            borderColor={palette.border}
            onPress={close}
          />
        ) : (
          <View style={styles.actions}>
            <PopButton
              label="Resist"
              symbol="hand.raised.fill"
              background={palette.resistance}
              textColor="#2A0A0E"
              shadowColor={shadowColor}
              borderColor={palette.border}
              disabled={status !== 'active'}
              onPress={() => flickOff('resistant')}
              style={styles.flex}
            />
            <PopButton
              label="Ready"
              symbol="checkmark"
              background={palette.clear}
              textColor="#062313"
              shadowColor={shadowColor}
              borderColor={palette.border}
              disabled={status !== 'active'}
              onPress={() => flickOff('clear')}
              style={styles.flex}
            />
          </View>
        )}
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  root: { flex: 1 },
  content: {
    flex: 1,
    paddingHorizontal: Spacing.four,
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  streak: {
    borderWidth: 2.5,
    borderRadius: 999,
    paddingHorizontal: 14,
    paddingVertical: 7,
  },
  streakText: {
    fontFamily: ROUNDED,
    fontSize: 15,
    fontWeight: '800',
    color: '#FFFFFF',
    letterSpacing: 0.2,
  },
  close: {
    width: 38,
    height: 38,
    borderRadius: 999,
    borderWidth: 2.5,
    alignItems: 'center',
    justifyContent: 'center',
  },
  question: {
    fontFamily: ROUNDED,
    fontSize: 30,
    fontWeight: '800',
    letterSpacing: -0.5,
    paddingTop: Spacing.four,
  },
  subhead: {
    fontFamily: ROUNDED,
    fontSize: 16,
    fontWeight: '700',
    paddingTop: Spacing.half,
  },
  stage: {
    flex: 1,
    justifyContent: 'center',
  },
  deck: {
    width: '100%',
    alignItems: 'center',
  },
  card: {
    width: '100%',
    minHeight: 300,
    borderRadius: 26,
    borderWidth: 3,
    padding: Spacing.four,
  },
  behindCard: {
    position: 'absolute',
    top: 0,
    height: 300,
  },
  topCard: {
    justifyContent: 'center',
  },
  outgoingCard: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
  },
  stamp: {
    position: 'absolute',
    top: 22,
    zIndex: 6,
    borderWidth: 3,
    borderRadius: 12,
    paddingHorizontal: 14,
    paddingVertical: 6,
  },
  readyStamp: { left: 18 },
  resistStamp: { right: 18 },
  stampText: {
    fontFamily: ROUNDED,
    fontSize: 22,
    fontWeight: '800',
    color: '#FFFFFF',
    letterSpacing: 1,
  },
  cardTitle: {
    fontFamily: ROUNDED,
    fontSize: 32,
    lineHeight: 38,
    fontWeight: '800',
    letterSpacing: -0.5,
  },
  emptyCard: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    gap: Spacing.two,
  },
  emptyEmoji: {
    fontSize: 56,
  },
  emptyTitle: {
    fontFamily: ROUNDED,
    fontSize: 26,
    fontWeight: '800',
  },
  actions: {
    flexDirection: 'row',
    gap: Spacing.three,
  },
  flex: {
    flex: 1,
  },
});
