import Feather from '@expo/vector-icons/Feather'
import { LinearGradient } from 'expo-linear-gradient'
import { useRef } from 'react'
import {
  Platform,
  Pressable,
  type TextInput as RNTextInput,
  ScrollView,
  StyleSheet,
  type ViewStyle,
} from 'react-native'
import { ANDROID_HAS_NATIVE_BLUR, GlassSurface } from 'src/components/common/GlassSurface'
import { haptic } from 'src/lib/haptics'
import { Input, Menu, styled, Text, useTheme, View, XStack } from 'tamagui'
import { InflightBar } from './InflightBar'
import { menuSelectableStyle, useMenuSelectableTokens } from './selectable'
import type {
  IconName,
  MenuOverflow,
  MenuPrimary,
  MenuSearch,
  MenuSegments,
} from './types'

// MenuBar — Tamagui compound primitives shared by the unified bar and the
// root-level toolbar (detail routes). One styled vocabulary, one set of
// variants. Tuning the menu look anywhere in the app happens here.

export const ICON_SIZE = 22
export const ICON_SIZE_BOLD = 24
export const BUTTON_SIZE = 40
export const PRIMARY_SIZE = 44
export const BAR_HEIGHT = 52

// Context-row-only sizes. The top pill is shorter than the tab strip and its
// singular action icons shrink ~10% for visual balance.
export const CONTEXT_BAR_HEIGHT = 42
export const CONTEXT_ICON_SIZE = 20
export const CONTEXT_ICON_SIZE_BOLD = 22

export const SURFACE_RADIUS = 20

// ──────────────────────────────────────────────────────────────────────────
// Floating wrapper — pure positioning. Sits above the safe area; no clipping
// and no blur here (blur lives inside each pill so it's visible per-surface).
// ──────────────────────────────────────────────────────────────────────────

export function MenuBarFloating({
  bottom,
  children,
}: {
  bottom: number
  children: React.ReactNode
}) {
  const wrapStyle: ViewStyle = {
    position: 'absolute',
    left: 16,
    right: 16,
    bottom,
  }
  return (
    <View pointerEvents="box-none" style={wrapStyle}>
      {children}
    </View>
  )
}

// ──────────────────────────────────────────────────────────────────────────
// Pill — rounded rect that draws its own background, shadow, and an
// absolute-positioned BlurView with an accent-tinted overlay behind the
// content. Two layout variants: `hug` right-anchors + sizes to content;
// default stretches to the parent's full width.
//
// iOS: native UIVisualEffectView blur via expo-blur.
// Android: blur is intentionally disabled (see ANDROID_HAS_NATIVE_BLUR in
// GlassSurface.tsx) — Samsung One UI's compositor renders the SDK 31+
// `dimezisBlurViewSdk31Plus` path with horizontal banding. The pill falls
// back to a solid `$backgroundMenu` fill so the shadow-on-transparent
// artifact doesn't return. `GlassSurface` lives in src/components/common so
// sticky headers share this exact backing without re-importing expo-blur.
// ──────────────────────────────────────────────────────────────────────────

const PillRoot = styled(XStack, {
  name: 'MenuSurfacePill',
  borderRadius: SURFACE_RADIUS,
  paddingHorizontal: '$2',
  paddingVertical: '$2',
  alignItems: 'center',
  gap: '$2',
  minHeight: BAR_HEIGHT,
  overflow: 'hidden',
  variants: {
    surface: {
      translucent: {
        // On Android, paint the near-opaque `$backgroundMenu` token so the
        // shadow-on-transparent artifact doesn't return — native blur is
        // disabled for all Android (see ANDROID_HAS_NATIVE_BLUR). On iOS,
        // the BlurView draws the surface, so leave the root transparent so
        // the blur reads the content behind the pill.
        backgroundColor:
          Platform.OS === 'android' && !ANDROID_HAS_NATIVE_BLUR
            ? '$backgroundMenu'
            : 'transparent',
        shadowColor: '$shadowColor',
        shadowOffset: { width: 0, height: 4 },
        shadowOpacity: 0.18,
        shadowRadius: 10,
        // Android's Material elevation draws a softer bloom shadow than iOS.
        // Keep it modest so the pill reads as a surface rather than a halo.
        elevation: Platform.OS === 'android' ? 2 : 6,
      },
      solid: {
        backgroundColor: '$background',
        borderColor: '$borderColor',
        borderWidth: 1,
        shadowColor: '$shadowColor',
        shadowOffset: { width: 0, height: 4 },
        shadowOpacity: 0.35,
        shadowRadius: 8,
        elevation: 4,
      },
    },
    hug: {
      true: {
        flexGrow: 0,
        flexShrink: 1,
        maxWidth: '100%',
      },
      false: {
        alignSelf: 'stretch',
        flexGrow: 1,
      },
    },
    compact: {
      // Tight vertical padding is what actually shortens the pill — a 40pt
      // button inside `$2` (16pt) vertical padding is ~56pt tall, dominating
      // minHeight. 2pt padding lets a 40pt button render at ~44pt, matching
      // the intended 20% reduction.
      true: { minHeight: CONTEXT_BAR_HEIGHT, paddingVertical: 2 },
      false: { minHeight: BAR_HEIGHT },
    },
  } as const,
  defaultVariants: {
    surface: 'translucent',
    hug: false,
    compact: false,
  },
})

export function MenuSurfacePill({
  surface = 'translucent',
  hug = false,
  compact = false,
  children,
  ...rest
}: React.ComponentProps<typeof PillRoot>) {
  return (
    <PillRoot surface={surface} hug={hug} compact={compact} {...rest}>
      <GlassSurface kind={surface} androidBlur="auto" />
      {children}
      <InflightBar />
    </PillRoot>
  )
}

// ──────────────────────────────────────────────────────────────────────────
// Icon — themed Feather wrapper. Tints follow the active Tamagui theme via
// useTheme rather than plain color props so theme switches re-tint instantly.
// ──────────────────────────────────────────────────────────────────────────

export function MenuIcon({
  name,
  size,
  tint,
}: {
  name: IconName
  size?: number
  tint?: string
}) {
  const theme = useTheme()
  const color = tint ?? (theme.onSurfacePrimary.val as string)
  return <Feather name={name} size={size ?? ICON_SIZE} color={color} />
}

// ──────────────────────────────────────────────────────────────────────────
// Pressables — leading/trailing icon button, primary pill, overflow trigger.
// Use react-native Pressable for press feedback cross-platform; haptics gated
// on Reduce Motion.
// ──────────────────────────────────────────────────────────────────────────

export function MenuIconButton({
  icon,
  onPress,
  disabled,
  selected,
  accessibilityLabel,
  accessibilityHint,
  reduceMotion,
  size,
  tint,
}: {
  icon: IconName
  onPress: () => void
  disabled?: boolean
  selected?: boolean
  accessibilityLabel: string
  accessibilityHint?: string
  reduceMotion?: boolean
  size?: number
  tint?: string
}) {
  const tokens = useMenuSelectableTokens()
  const style = menuSelectableStyle(selected ? 'selected' : 'rest', tokens)
  return (
    <Pressable
      onPress={() => {
        if (disabled) return
        if (!reduceMotion) haptic.selection()
        onPress()
      }}
      disabled={disabled}
      accessibilityRole="button"
      accessibilityLabel={accessibilityLabel}
      accessibilityHint={accessibilityHint}
      accessibilityState={{ disabled, selected }}
      hitSlop={8}
      style={({ pressed }) => ({
        width: BUTTON_SIZE,
        height: BUTTON_SIZE,
        borderRadius: BUTTON_SIZE / 2,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: style.backgroundColor,
        opacity: disabled ? 0.4 : pressed ? 0.6 : 1,
      })}
    >
      <MenuIcon name={icon} size={size} tint={selected ? style.iconTint : tint} />
    </Pressable>
  )
}

// Primary CTA pill — shares the floating-CTA visual vocabulary with the
// camera FAB in MenuBarUnified.tsx: accent vertical gradient, accent glow
// shadow on iOS, `$onAccent` icon tint. Both affordances are the screen's
// single most prominent action, so they render as visual siblings rather
// than competing with `$interactiveActive` as a third "primary" look.
//
// `primary.selected` (fed from the intent's `toggled` flag) renders the
// inverted / outlined state: transparent fill, accent border, accent icon.
// This keeps the affordance legible when its action has already been taken
// (rare; most commit intents don't opt in to a toggled state).
export function MenuPrimaryButton({
  primary,
  reduceMotion,
  boldText,
}: {
  primary: MenuPrimary
  reduceMotion?: boolean
  boldText?: boolean
}) {
  const theme = useTheme()
  const accent = theme.accent.val as string
  const accentPressed = theme.accentPressed.val as string
  const accentGlow = theme.accentGlow.val as string
  const onAccent = theme.onAccent.val as string
  const iconTint = primary.selected ? accent : onAccent
  return (
    <Pressable
      onPress={() => {
        if (primary.disabled) return
        if (!reduceMotion) haptic.light()
        primary.onPress()
      }}
      disabled={primary.disabled}
      accessibilityRole="button"
      accessibilityLabel={primary.accessibilityLabel}
      accessibilityHint={primary.accessibilityHint}
      accessibilityState={{ disabled: primary.disabled, selected: primary.selected }}
      hitSlop={6}
      style={({ pressed }) => ({
        width: PRIMARY_SIZE,
        height: PRIMARY_SIZE,
        borderRadius: PRIMARY_SIZE / 2,
        alignItems: 'center',
        justifyContent: 'center',
        overflow: 'hidden',
        backgroundColor: primary.selected ? 'transparent' : accent,
        borderWidth: primary.selected || boldText ? 1 : 0,
        borderColor: primary.selected || boldText ? accent : 'transparent',
        ...(primary.selected
          ? null
          : {
              shadowColor: accentGlow,
              shadowOffset: { width: 0, height: 3 },
              shadowOpacity: 1,
              shadowRadius: 8,
              elevation: Platform.OS === 'android' ? 3 : 0,
            }),
        opacity: primary.disabled ? 0.5 : pressed ? 0.85 : 1,
        transform: [{ scale: pressed ? 0.94 : 1 }],
      })}
    >
      {primary.selected ? null : (
        <LinearGradient
          colors={[accent, accentPressed]}
          start={[0, 0]}
          end={[0, 1]}
          style={StyleSheet.absoluteFill}
        />
      )}
      <MenuIcon
        name={primary.icon}
        tint={iconTint}
        size={boldText ? ICON_SIZE_BOLD : ICON_SIZE}
      />
    </Pressable>
  )
}

// ──────────────────────────────────────────────────────────────────────────
// Overflow — Tamagui Menu (zeego on native, animated panel on web). Same JSX
// across platforms.
// ──────────────────────────────────────────────────────────────────────────

export function MenuOverflowButton({
  overflow,
  reduceMotion,
  boldText,
}: {
  overflow: MenuOverflow
  reduceMotion?: boolean
  boldText?: boolean
}) {
  const theme = useTheme()
  return (
    <Menu>
      <Menu.Trigger>
        <View
          width={BUTTON_SIZE}
          height={BUTTON_SIZE}
          borderRadius={BUTTON_SIZE / 2}
          alignItems="center"
          justifyContent="center"
          hitSlop={8}
          accessibilityRole="button"
          accessibilityLabel={overflow.accessibilityLabel ?? 'More actions'}
        >
          <MenuIcon
            name="more-vertical"
            size={boldText ? ICON_SIZE_BOLD : ICON_SIZE}
            tint={theme.onSurfaceMuted.val as string}
          />
        </View>
      </Menu.Trigger>
      <Menu.Content>
        {overflow.items.map((item, i) => (
          <Menu.Item
            key={`${item.label}-${i}`}
            destructive={item.destructive}
            disabled={item.disabled}
            onSelect={() => {
              if (!reduceMotion) haptic.selection()
              item.onPress()
            }}
          >
            <Menu.ItemTitle>{item.label}</Menu.ItemTitle>
          </Menu.Item>
        ))}
      </Menu.Content>
    </Menu>
  )
}

// ──────────────────────────────────────────────────────────────────────────
// Segments — pill-styled toggle row inside the bar.
// ──────────────────────────────────────────────────────────────────────────

export function MenuSegmentsRow({
  segments,
  boldText,
}: {
  segments: MenuSegments
  boldText?: boolean
}) {
  const theme = useTheme()
  const tokens = useMenuSelectableTokens()
  const selectedStyle = menuSelectableStyle('selected', tokens)
  // Horizontal scroller with `flexShrink: 1` (NOT `flexGrow: 1`) on the outer
  // style: sizes to content when it fits inside the hug pill, shrinks and
  // scrolls internally when the pill clamps against its maxWidth. Putting
  // `flexGrow: 1` on the outer style forces the pill to fill its wrapper.
  //
  // Label / icon colors follow the four-tone contract documented in
  // selectable.ts: unselected options read in `$onSurfaceMuted` to match the
  // unselected tab-strip icons, selected option pill fills with `$accentMuted`
  // (50% accent) and paints the label + icon in `$onAccent` so the filled
  // pill reads cleanly on light mode.
  return (
    <ScrollView
      horizontal
      showsHorizontalScrollIndicator={false}
      bounces
      style={{ flexShrink: 1 }}
      contentContainerStyle={{
        alignItems: 'center',
        gap: 4,
        paddingHorizontal: 2,
      }}
    >
      {segments.options.map((opt) => {
        const active = opt.value === segments.value
        return (
          <Pressable
            key={opt.value}
            onPress={() => {
              if (active) return
              haptic.selection()
              segments.onChange(opt.value)
            }}
            accessibilityRole="tab"
            accessibilityLabel={opt.label}
            accessibilityState={{ selected: active }}
            hitSlop={4}
            style={({ pressed }) => ({
              paddingHorizontal: 10,
              paddingVertical: 6,
              borderRadius: 999,
              backgroundColor: active ? selectedStyle.backgroundColor : 'transparent',
              opacity: pressed ? 0.7 : 1,
              flexDirection: 'row',
              alignItems: 'center',
              gap: 4,
              flexShrink: 0,
            })}
          >
            {opt.icon ? (
              <MenuIcon
                name={opt.icon}
                size={boldText ? 16 : 14}
                tint={
                  active ? selectedStyle.iconTint : (theme.onSurfaceMuted.val as string)
                }
              />
            ) : null}
            <Text
              numberOfLines={1}
              color={active ? selectedStyle.iconTint : '$onSurfaceMuted'}
              fontWeight={boldText ? 'bold' : '600'}
              fontSize={boldText ? '$3' : '$2'}
            >
              {opt.label}
            </Text>
          </Pressable>
        )
      })}
    </ScrollView>
  )
}

// Icon-only variant. Same toggle semantics as MenuSegmentsRow but every
// option renders as a circular icon button — selected state is communicated
// purely through tint (active glyph `$interactiveActive`, inactive
// `$colorTertiary`), no pill fill, no label.
export function MenuSegmentsIconRow({
  segments,
  boldText,
}: {
  segments: MenuSegments
  boldText?: boolean
}) {
  const tokens = useMenuSelectableTokens()
  const iconSize = boldText ? CONTEXT_ICON_SIZE_BOLD : CONTEXT_ICON_SIZE
  const activeStyle = menuSelectableStyle('active', tokens)
  const restStyle = menuSelectableStyle('rest', tokens)
  return (
    <XStack alignItems="center" flexShrink={1}>
      {segments.options.map((opt) => {
        const active = opt.value === segments.value
        if (!opt.icon) return null
        return (
          <Pressable
            key={opt.value}
            onPress={() => {
              if (active) return
              haptic.selection()
              segments.onChange(opt.value)
            }}
            accessibilityRole="tab"
            accessibilityLabel={opt.label}
            accessibilityState={{ selected: active }}
            hitSlop={4}
            style={({ pressed }) => ({
              width: BUTTON_SIZE,
              height: BUTTON_SIZE,
              borderRadius: BUTTON_SIZE / 2,
              alignItems: 'center',
              justifyContent: 'center',
              opacity: pressed ? 0.6 : 1,
            })}
          >
            <MenuIcon
              name={opt.icon}
              size={iconSize}
              tint={active ? activeStyle.iconTint : restStyle.iconTint}
            />
          </Pressable>
        )
      })}
    </XStack>
  )
}

// ──────────────────────────────────────────────────────────────────────────
// Search — inline morph; expands to fill the segments/middle slot when active.
// ──────────────────────────────────────────────────────────────────────────

export function MenuSearchField({
  search,
  autoFocus,
}: {
  search: MenuSearch
  autoFocus?: boolean
}) {
  const theme = useTheme()
  const inputRef = useRef<RNTextInput>(null)
  return (
    <XStack
      flexGrow={1}
      flexShrink={1}
      alignItems="center"
      bg="$backgroundTertiary"
      borderRadius="$10"
      paddingHorizontal="$3"
      height={BUTTON_SIZE}
      gap="$2"
    >
      <MenuIcon name="search" size={16} tint={theme.onSurfaceMuted.val as string} />
      <Input
        ref={inputRef as any}
        unstyled
        flexGrow={1}
        flexShrink={1}
        size="$3"
        color="$color"
        placeholderTextColor={theme.onSurfaceMuted.val as any}
        placeholder={search.placeholder}
        value={search.value}
        onChangeText={search.onChangeText}
        returnKeyType="search"
        autoFocus={autoFocus}
      />
    </XStack>
  )
}
