import * as SplashScreen from 'expo-splash-screen'
import {useEffect, useState, type PropsWithChildren} from 'react'
import {AccessibilityInfo, StyleSheet} from 'react-native'
import Animated, {
  Easing,
  useAnimatedStyle,
  useSharedValue,
  withTiming,
} from 'react-native-reanimated'
import {scheduleOnRN} from 'react-native-worklets'

import {FONT_FAMILY_DISPLAY, useTheme} from '@/sprinkles'

const INTRO_MS = 400
const OUTRO_MS = 1100
const APP_START_SCALE = 1.08
const REDUCED_FADE_MS = 220
const WORDMARK = 'Crumbs'

export function Splash({
  isReady,
  children,
}: PropsWithChildren<{isReady: boolean}>) {
  'use no memo'
  const t = useTheme()
  const [overlayMounted, setOverlayMounted] = useState(true)
  const [reduceMotion, setReduceMotion] = useState<
    boolean | undefined
  >(undefined)

  const markOpacity = useSharedValue(0)
  const markScale = useSharedValue(0.92)
  const overlayOpacity = useSharedValue(1)
  const appOpacity = useSharedValue(0)
  const appScale = useSharedValue(APP_START_SCALE)

  useEffect(() => {
    let alive = true
    AccessibilityInfo.isReduceMotionEnabled().then(
      (v) => alive && setReduceMotion(v),
    )
    return () => {
      alive = false
    }
  }, [])

  const ready = isReady && reduceMotion !== undefined

  useEffect(() => {
    if (!ready) return
    let cancelled = false
    const unmount = () => setOverlayMounted(false)

    const run = async () => {
      // native splash gone; identical js overlay underneath -> seamless
      try {
        await SplashScreen.hideAsync()
      } catch {}
      if (cancelled) return

      if (reduceMotion) {
        appScale.set(1)
        appOpacity.set(1)
        overlayOpacity.set(
          withTiming(0, {duration: REDUCED_FADE_MS}, (f) => {
            'worklet'
            if (f) scheduleOnRN(unmount)
          }),
        )
        return
      }

      // intro: wordmark fades + scales in
      markScale.set(
        withTiming(1, {
          duration: INTRO_MS,
          easing: Easing.out(Easing.cubic),
        }),
      )
      markOpacity.set(
        withTiming(1, {duration: INTRO_MS}, (f) => {
          'worklet'
          if (!f) return
          // outro: overlay (+mark) out, app zooms in
          appOpacity.set(withTiming(1, {duration: OUTRO_MS}))
          appScale.set(
            withTiming(1, {
              duration: OUTRO_MS,
              easing: Easing.out(Easing.cubic),
            }),
          )
          markScale.set(withTiming(1.04, {duration: OUTRO_MS}))
          markOpacity.set(withTiming(0, {duration: OUTRO_MS * 0.6}))
          overlayOpacity.set(
            withTiming(0, {duration: OUTRO_MS}, (d) => {
              'worklet'
              if (d) scheduleOnRN(unmount)
            }),
          )
        }),
      )
    }

    void run()
    return () => {
      cancelled = true
    }
  }, [
    ready,
    reduceMotion,
    appOpacity,
    appScale,
    markOpacity,
    markScale,
    overlayOpacity,
  ])

  const appStyle = useAnimatedStyle(() => ({
    opacity: appOpacity.get(),
    transform: [{scale: appScale.get()}],
  }))
  const overlayStyle = useAnimatedStyle(() => ({
    opacity: overlayOpacity.get(),
  }))
  const markStyle = useAnimatedStyle(() => ({
    opacity: markOpacity.get(),
    transform: [{scale: markScale.get()}],
  }))

  return (
    <Animated.View style={styles.flex}>
      <Animated.View style={[styles.flex, appStyle]}>
        {children}
      </Animated.View>
      {overlayMounted && (
        <Animated.View
          pointerEvents="none"
          style={[
            styles.overlay,
            {backgroundColor: t.palette.bg},
            overlayStyle,
          ]}
        >
          <Animated.Text
            style={[
              styles.mark,
              t.atoms.text,
              {fontFamily: FONT_FAMILY_DISPLAY},
              markStyle,
            ]}
          >
            {WORDMARK}
          </Animated.Text>
        </Animated.View>
      )}
    </Animated.View>
  )
}

const styles = StyleSheet.create({
  flex: {flex: 1},
  overlay: {
    ...StyleSheet.absoluteFill,
    alignItems: 'center',
    justifyContent: 'center',
    zIndex: 1000,
  },
  mark: {fontSize: 44, lineHeight: 48, fontWeight: '600'},
})
