import { GlassView, isLiquidGlassAvailable, type GlassViewProps } from 'expo-glass-effect';
import { router } from 'expo-router';
import { SymbolView } from 'expo-symbols';
import { useEffect, useState, type ReactNode } from 'react';
import {
  Pressable,
  StyleSheet,
  Text,
  View,
  type LayoutChangeEvent,
  type StyleProp,
  type ViewStyle,
} from 'react-native';
import Animated, {
  Easing,
  Extrapolation,
  FadeIn,
  FadeOut,
  FadeOutUp,
  interpolate,
  LinearTransition,
  useAnimatedProps,
  useAnimatedScrollHandler,
  useAnimatedStyle,
  useDerivedValue,
  useSharedValue,
  withTiming,
  type DerivedValue,
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';

import { Fonts, Spacing } from '@/constants/theme';
import { isSyncEnabled } from '@/data/sync';
import { tasks } from '@/data/task-repository';
import { PopButton } from '@/design/pop-button';
import { useActionList, type ActionListStatus } from '@/design/use-action-list';
import { type GlideTask } from '@/domain/glide';
import { useColorScheme } from '@/hooks/use-color-scheme';
import { useTheme } from '@/hooks/use-theme';
import { selectionHaptic } from '@/lib/haptics';

const ROUNDED = Fonts?.rounded;

// Liquid Glass can't be faded via opacity directly — setting opacity to 0 on a
// GlassView (or any parent) stops the effect rendering. So we animate a wrapper's
// opacity and flip `glassEffectStyle` to 'none' below a threshold, per the
// expo-glass-effect docs' opacity workaround.
const AnimatedGlassView = Animated.createAnimatedComponent(GlassView);

// How far (px) content slides behind a bar before its glass is fully "on".
const GLASS_RAMP = 36;

/**
 * Actioning: the everyday surface. The single task to do now sits front and
 * centre; whatever's ready behind it lines up below. This is what's visible
 * almost all the time — planning and capture are one tap away.
 */
export function ActioningScreen() {
  const palette = useTheme();
  const scheme = useColorScheme();
  const insets = useSafeAreaInsets();
  const shadowColor = scheme === 'dark' ? 'rgba(0,0,0,0.55)' : palette.border;

  // Seed starter tasks on a fresh install so there's something to plan. When sync is on,
  // the engine seeds after the first pull instead (so an existing account's tasks aren't
  // mixed with starters); in pure-local mode we seed here on mount.
  useEffect(() => {
    if (!isSyncEnabled()) tasks.ensureStarterTasks();
  }, []);

  const { status, current, rest, unsorted, done, complete } = useActionList();
  const readyCount = (current ? 1 : 0) + rest.length;

  // The header and footer are Liquid Glass bars layered over a full-height scroll
  // view, so cards frost softly beneath them instead of meeting a hard cut-off.
  // Each bar's height is measured so the content insets keep the first and last
  // card clear of it at rest (and seeded with estimates to avoid a first-frame
  // jump).
  const glassAvailable = isLiquidGlassAvailable();
  const glassScheme = scheme === 'dark' ? 'dark' : 'light';
  // Tint each bar's glass toward the theme background so the frost reads as a
  // quiet, background-coloured pane rather than the material's default — which
  // brightens the dark theme and cools the warm cream of the light one. The `8C`
  // suffix (~55% alpha) matches the background tone while keeping the glass
  // translucent; deriving it from the palette keeps both modes in sync.
  const glassTint = `${palette.background}8C`;
  const [headerHeight, setHeaderHeight] = useState(insets.top + 64);
  const [footerHeight, setFooterHeight] = useState(insets.bottom + 76);

  // Drive each bar's glass from scroll position: fully transparent at the
  // boundaries (nothing behind it), fading in as cards slide underneath. The
  // header keys off how far we've scrolled from the top; the footer off how far we
  // are from the bottom (so it needs the content and viewport heights).
  const scrollY = useSharedValue(0);
  const contentH = useSharedValue(0);
  const viewportH = useSharedValue(0);

  const scrollHandler = useAnimatedScrollHandler((event) => {
    scrollY.value = event.contentOffset.y;
  });

  const headerGlass = useDerivedValue(() =>
    interpolate(scrollY.value, [0, GLASS_RAMP], [0, 1], Extrapolation.CLAMP)
  );
  const footerGlass = useDerivedValue(() => {
    const fromBottom = Math.max(0, contentH.value - viewportH.value - scrollY.value);
    return interpolate(fromBottom, [0, GLASS_RAMP], [0, 1], Extrapolation.CLAMP);
  });

  // Reset when the list isn't scrollable (e.g. the empty state) so the bars sit
  // fully transparent over it rather than holding a stale glass level.
  useEffect(() => {
    if (status !== 'ready' || !current) {
      scrollY.set(0);
      contentH.set(0);
      viewportH.set(0);
    }
  }, [status, current, scrollY, contentH, viewportH]);

  return (
    <View style={[styles.root, { backgroundColor: palette.background }]}>
      {status === 'ready' && current ? (
        <Animated.ScrollView
          style={styles.scroll}
          contentContainerStyle={[
            styles.scrollContent,
            { paddingTop: headerHeight + Spacing.three, paddingBottom: footerHeight + Spacing.three },
          ]}
          showsVerticalScrollIndicator={false}
          onScroll={scrollHandler}
          scrollEventThrottle={16}
          onContentSizeChange={(_width, height) => {
            contentH.set(height);
          }}
          onLayout={(event) => {
            viewportH.set(event.nativeEvent.layout.height);
          }}>
          <HeroCard task={current} palette={palette} shadowColor={shadowColor} onDone={() => complete(current.id)} />

          {rest.length > 0 ? (
            <View style={styles.section}>
              <Text style={[styles.sectionLabel, { color: palette.textSecondary }]}>NEXT UP</Text>
              <View style={styles.list}>
                {rest.map((task) => (
                  <TaskRow
                    key={task.id}
                    task={task}
                    palette={palette}
                    shadowColor={shadowColor}
                    onComplete={() => complete(task.id)}
                  />
                ))}
              </View>
            </View>
          ) : null}
        </Animated.ScrollView>
      ) : (
        <View style={[styles.centered, { paddingTop: headerHeight, paddingBottom: footerHeight }]}>
          <EmptyState status={status} unsorted={unsorted} palette={palette} shadowColor={shadowColor} />
        </View>
      )}

      {/* Header — a Liquid Glass bar; content scrolls and frosts beneath it. */}
      <GlassBar
        style={styles.headerBar}
        glassAvailable={glassAvailable}
        fallbackColor={palette.background}
        glassScheme={glassScheme}
        glassTint={glassTint}
        glassOpacity={headerGlass}
        onLayout={(event) => setHeaderHeight(event.nativeEvent.layout.height)}>
        <View style={[styles.header, { paddingTop: insets.top + Spacing.three }]}>
          <Text style={[styles.wordmark, { color: palette.text }]}>Glide</Text>
          <View style={styles.badges}>
            {readyCount > 0 ? (
              <CountPill label={`${readyCount} ready`} background={palette.clear} textColor="#062313" palette={palette} shadowColor={shadowColor} />
            ) : null}
            {done > 0 ? (
              <CountPill label={`${done} done`} background={palette.accent} textColor="#FFFFFF" palette={palette} shadowColor={shadowColor} />
            ) : null}
          </View>
        </View>
      </GlassBar>

      {/* Footer — matching Liquid Glass bar over the bottom actions. */}
      <GlassBar
        style={styles.footerBar}
        glassAvailable={glassAvailable}
        fallbackColor={palette.background}
        glassScheme={glassScheme}
        glassTint={glassTint}
        glassOpacity={footerGlass}
        onLayout={(event) => setFooterHeight(event.nativeEvent.layout.height)}>
        <View style={[styles.actions, { paddingBottom: insets.bottom + Spacing.three }]}>
          <PopButton
            label="Plan"
            symbol="rectangle.stack.fill"
            background={palette.surface}
            textColor={palette.text}
            shadowColor={shadowColor}
            borderColor={palette.border}
            size="md"
            onPress={() => router.push('/plan')}
            style={styles.flex}
          />
          <PopButton
            label="New task"
            symbol="plus"
            background={palette.accent}
            textColor="#FFFFFF"
            shadowColor={shadowColor}
            borderColor={palette.border}
            size="md"
            onPress={() => router.push('/new')}
            style={styles.flex}
          />
        </View>
      </GlassBar>
    </View>
  );
}

/**
 * A top/bottom bar backed by iOS 26 Liquid Glass — content scrolls and frosts
 * beneath it. Where Liquid Glass isn't available it falls back to a solid bar in
 * the app's background colour, so the layout and measured insets stay identical;
 * only the translucency is lost.
 */
function GlassBar({
  children,
  style,
  onLayout,
  glassAvailable,
  fallbackColor,
  glassScheme,
  glassTint,
  glassOpacity,
}: {
  children: ReactNode;
  style: StyleProp<ViewStyle>;
  onLayout: (event: LayoutChangeEvent) => void;
  glassAvailable: boolean;
  fallbackColor: string;
  glassScheme: 'light' | 'dark';
  glassTint: string;
  glassOpacity: DerivedValue<number>;
}) {
  // Fade a wrapper's opacity and switch the glass off below the threshold — fading
  // the GlassView's own opacity would stop the effect rendering (see AnimatedGlassView).
  const fadeStyle = useAnimatedStyle(() => ({ opacity: glassOpacity.value }));
  const glassProps = useAnimatedProps<GlassViewProps>(() => ({
    glassEffectStyle: glassOpacity.value < 0.01 ? 'none' : 'regular',
  }));

  // The bar itself is transparent; the glass (or fallback fill) is a faded layer
  // behind the always-opaque content, so the wordmark and buttons never dim.
  return (
    <View style={style} onLayout={onLayout}>
      {glassAvailable ? (
        <Animated.View style={[StyleSheet.absoluteFill, fadeStyle]} pointerEvents="none">
          <AnimatedGlassView
            style={StyleSheet.absoluteFill}
            animatedProps={glassProps}
            colorScheme={glassScheme}
            tintColor={glassTint}
          />
        </Animated.View>
      ) : (
        <Animated.View
          style={[StyleSheet.absoluteFill, fadeStyle, { backgroundColor: fallbackColor }]}
          pointerEvents="none"
        />
      )}
      {children}
    </View>
  );
}

type Palette = ReturnType<typeof useTheme>;

/** A small count chip — green "N ready" and purple "N done" share this shape. */
function CountPill({
  label,
  background,
  textColor,
  palette,
  shadowColor,
}: {
  label: string;
  background: string;
  textColor: string;
  palette: Palette;
  shadowColor: string;
}) {
  return (
    <View
      style={[
        styles.countPill,
        { backgroundColor: background, borderColor: palette.border, boxShadow: `3px 3px 0 ${shadowColor}` },
      ]}>
      <Text style={[styles.countText, { color: textColor }]}>{label}</Text>
    </View>
  );
}

function HeroCard({
  task,
  palette,
  shadowColor,
  onDone,
}: {
  task: GlideTask;
  palette: Palette;
  shadowColor: string;
  onDone: () => void;
}) {
  const enter = useSharedValue(0);

  useEffect(() => {
    enter.value = 0;
    enter.value = withTiming(1, { duration: 200, easing: Easing.out(Easing.cubic) });
  }, [task.id, enter]);

  // Quick ease-out settle (no spring overshoot); FadeIn handles opacity.
  const cardStyle = useAnimatedStyle(() => ({
    transform: [
      { translateY: interpolate(enter.value, [0, 1], [10, 0]) },
      { scale: interpolate(enter.value, [0, 1], [0.97, 1]) },
    ],
  }));

  return (
    <Animated.View
      key={task.id}
      entering={FadeIn.duration(220)}
      exiting={FadeOutUp.duration(280)}
      layout={LinearTransition.duration(240).easing(Easing.out(Easing.cubic))}
      style={[
        styles.hero,
        { backgroundColor: palette.surface, borderColor: palette.border, boxShadow: `6px 7px 0 ${shadowColor}` },
        cardStyle,
      ]}>
      <View style={[styles.nowPill, { backgroundColor: palette.accentSoft, borderColor: palette.border }]}>
        <Text style={[styles.nowText, { color: palette.accent }]}>NOW</Text>
      </View>
      <Text style={[styles.heroTitle, { color: palette.text }]}>{task.title}</Text>
      <PopButton
        label="Done"
        symbol="checkmark"
        background={palette.clear}
        textColor="#062313"
        shadowColor={shadowColor}
        borderColor={palette.border}
        onPress={onDone}
      />
    </Animated.View>
  );
}

function TaskRow({
  task,
  palette,
  shadowColor,
  onComplete,
}: {
  task: GlideTask;
  palette: Palette;
  shadowColor: string;
  onComplete: () => void;
}) {
  return (
    <Animated.View
      entering={FadeIn.duration(200)}
      exiting={FadeOut.duration(160)}
      layout={LinearTransition.duration(240).easing(Easing.out(Easing.cubic))}
      style={[
        styles.row,
        { backgroundColor: palette.surface, borderColor: palette.border, boxShadow: `3px 4px 0 ${shadowColor}` },
      ]}>
      <Pressable
        accessibilityRole="button"
        accessibilityLabel={`Done: ${task.title}`}
        hitSlop={10}
        onPressIn={selectionHaptic}
        onPress={onComplete}
        style={({ pressed }) => [
          styles.check,
          { borderColor: palette.border, backgroundColor: pressed ? palette.clear : 'transparent' },
        ]}>
        <SymbolView name="checkmark" size={14} tintColor={palette.text} weight="bold" fallback={null} />
      </Pressable>
      <Text style={[styles.rowTitle, { color: palette.text }]} numberOfLines={2}>
        {task.title}
      </Text>
    </Animated.View>
  );
}

function EmptyState({
  status,
  unsorted,
  palette,
  shadowColor,
}: {
  status: ActionListStatus;
  unsorted: number;
  palette: Palette;
  shadowColor: string;
}) {
  const { emoji, title, sub } =
    status === 'loading'
      ? { emoji: '🌀', title: 'Loading…', sub: '' }
      : unsorted > 0
        ? { emoji: '🗂️', title: 'Nothing ready yet', sub: `Plan to line up what's next` }
        : { emoji: '🎉', title: 'All clear', sub: '' };

  return (
    <Animated.View
      entering={FadeIn.duration(240)}
      style={[
        styles.empty,
        { backgroundColor: palette.surface, borderColor: palette.border, boxShadow: `6px 7px 0 ${shadowColor}` },
      ]}>
      <Text style={styles.emptyEmoji}>{emoji}</Text>
      <Text style={[styles.emptyTitle, { color: palette.text }]}>{title}</Text>
      {sub ? <Text style={[styles.emptySub, { color: palette.textSecondary }]}>{sub}</Text> : null}
    </Animated.View>
  );
}

const styles = StyleSheet.create({
  root: { flex: 1 },
  headerBar: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
  },
  footerBar: {
    position: 'absolute',
    bottom: 0,
    left: 0,
    right: 0,
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    paddingHorizontal: Spacing.four,
    paddingBottom: Spacing.three,
  },
  wordmark: {
    fontFamily: ROUNDED,
    fontSize: 28,
    fontWeight: '800',
    letterSpacing: -0.5,
  },
  badges: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: Spacing.two,
  },
  countPill: {
    borderWidth: 2.5,
    borderRadius: 999,
    paddingHorizontal: 14,
    paddingVertical: 6,
  },
  countText: {
    fontFamily: ROUNDED,
    fontSize: 14,
    fontWeight: '800',
  },
  scroll: {
    flex: 1,
  },
  scrollContent: {
    // The scroll view spans the full width (behind the glass bars), so the cards'
    // hard offset shadows render inside the viewport instead of being clipped; the
    // content is re-inset here. Top/bottom padding is set inline from the measured
    // bar heights so the first and last card clear the glass.
    gap: Spacing.four,
    paddingHorizontal: Spacing.four,
  },
  centered: {
    flex: 1,
    justifyContent: 'center',
    paddingHorizontal: Spacing.four,
  },
  hero: {
    borderRadius: 26,
    borderWidth: 3,
    padding: Spacing.four,
    gap: Spacing.three,
  },
  nowPill: {
    alignSelf: 'flex-start',
    borderWidth: 2.5,
    borderRadius: 999,
    paddingHorizontal: 12,
    paddingVertical: 5,
  },
  nowText: {
    fontFamily: ROUNDED,
    fontSize: 13,
    fontWeight: '800',
    letterSpacing: 1.5,
  },
  heroTitle: {
    fontFamily: ROUNDED,
    fontSize: 30,
    lineHeight: 36,
    fontWeight: '800',
    letterSpacing: -0.5,
    paddingVertical: Spacing.two,
  },
  section: {
    gap: Spacing.two,
  },
  sectionLabel: {
    fontFamily: ROUNDED,
    fontSize: 12,
    fontWeight: '800',
    letterSpacing: 2,
    marginLeft: 4,
  },
  list: {
    gap: Spacing.three,
  },
  row: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: Spacing.three,
    borderWidth: 2.5,
    borderRadius: 18,
    padding: Spacing.three,
  },
  check: {
    width: 30,
    height: 30,
    borderRadius: 999,
    borderWidth: 2.5,
    alignItems: 'center',
    justifyContent: 'center',
  },
  rowTitle: {
    flex: 1,
    fontFamily: ROUNDED,
    fontSize: 17,
    fontWeight: '800',
  },
  empty: {
    borderRadius: 26,
    borderWidth: 3,
    paddingVertical: Spacing.five,
    paddingHorizontal: Spacing.four,
    alignItems: 'center',
    gap: Spacing.two,
  },
  emptyEmoji: {
    fontSize: 56,
  },
  emptyTitle: {
    fontFamily: ROUNDED,
    fontSize: 24,
    fontWeight: '800',
  },
  emptySub: {
    fontFamily: ROUNDED,
    fontSize: 15,
    fontWeight: '600',
    textAlign: 'center',
  },
  actions: {
    flexDirection: 'row',
    gap: Spacing.three,
    paddingHorizontal: Spacing.four,
    paddingTop: Spacing.two,
  },
  flex: {
    flex: 1,
  },
});
