import { LinearGradient } from '@tamagui/linear-gradient'
import { useSegments } from 'expo-router'
import { useMemo } from 'react'
import { View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { BAR_HEIGHT } from 'src/components/menu/MenuBar'
import { TAB_BAR_HEIGHT } from 'src/components/menu/tabBarMetrics'
import { SMOOTHSTEP_SAMPLES, SMOOTHSTEP_STOPS } from 'src/lib/curves'
import { useTheme, useThemeName } from 'tamagui'

// Peak alpha at the screen edge. Dark mode runs hotter because the
// luminance gap between dark content and `$background` is small — at 0.5
// the fade was barely perceptible on dark imagery.
const PEAK_ALPHA_LIGHT = 0.5
const PEAK_ALPHA_DARK = 0.75

// Bottom-edge horizontal modulation. The bottom isn't uniform: peak alpha
// dips in the center (to `CENTER_DIP_RATIO * peak`) and rises at the
// corners (to `SIDE_RISE_RATIO * peak`). Visually: a "saddle" where the
// middle of the bottom reads a bit clearer than the corners but still
// carries visible wash. Top stays uniform; only the bottom carries the
// modulation.
const CENTER_DIP_RATIO = 0.65 // bottom-center alpha = peak × 0.65
const SIDE_RISE_RATIO = 1.2 // bottom-corner alpha = peak × 1.20

// Overshoot past the chrome zone; the smoothstep tail dies into content
// runway instead of terminating at the boundary. Split per-edge: the
// bottom reaches further up than the top drops down so content approaching
// the menu has a longer visible fade to dissolve through.
const OVERSHOOT_RATIO_TOP = 0.15
const OVERSHOOT_RATIO_BOTTOM = 0.3

// Dark-mode only: shift the gradient's destination RGB toward black by
// this fraction (multiplicative per channel). `$background` in dark mode
// (`#2A2A2A`) sits close enough to content luminance that a plain
// fade-to-$background reads as barely-there against dark imagery — pulling
// the RGB further toward black deepens every stop without changing the
// alpha curve, so the fade still dies to fully transparent at the far end
// but the opaque end is noticeably darker than the background. Light mode
// keeps `$background` exactly; the contrast is already high enough there.
const DARKEN_FACTOR_DARK = 0.25

// 7-stop smoothstep curve sourced from `src/lib/curves.ts` so the same
// samples drive every perceptually-eased fade in the app. `smoothstep(t)`
// has zero derivative at both endpoints — the gradient meets opaque and
// transparent without a perceptible inflection, which is what kills the
// "bulbous" feel of a flat linear interpolation. Native gradient engines
// do linear interpolation between adjacent stops, so the curve has to be
// hand-stepped.
const STOPS = SMOOTHSTEP_STOPS
const SMOOTHSTEP = SMOOTHSTEP_SAMPLES

// Absolutely-positioned top/bottom gradients that fade content into the
// active theme's `$background`. Height-driven and chrome-agnostic: the caller
// passes the rendered `topHeight` / `bottomHeight` in px. `ScreenFadeEdges`
// (below) is the chrome-aware wrapper that derives those heights from the
// safe-area insets + menu/tab bars for the standard page layout; other
// surfaces with bespoke chrome (e.g. the List Organizer's edge-to-edge
// columns + glass capsules) call `FadeEdges` directly with their own
// geometry. Either way the perceptually-eased curve math lives here once.
//
// Top: a uniform Tamagui LinearGradient with a 7-stop smoothstep profile.
//
// Bottom: three gradients layered inside a single `<View>` via React
// Native's `experimental_backgroundImage` (RN 0.76+, native CSS gradient
// support, no new dependency). Multi-image CSS paints layers in
// list-order, first-listed on top:
//
//   1. `radial-gradient(... at 0% 100%)`  — left-corner boost
//   2. `radial-gradient(... at 100% 100%)` — right-corner boost
//   3. `linear-gradient(to top, ...)`      — uniform vertical base
//
// Why two radials instead of one centered below: a single radial centered
// at bottom-center places its peak *ring* at p=0.5, which also crosses the
// horizontal midline midway up the strip — producing an "arch" glow where
// the middle of the strip reads brighter than the sides at every
// elevation above the bottom edge. Anchoring one radial at each corner
// pulls the boost fully outward: at every y in the strip, the edges are
// brighter than the center, and both radials decay toward the opposite
// side so they don't overlap meaningfully through the middle.
//
// Blend math: at a corner, the base linear contributes `centerAlpha` and
// the corresponding corner radial contributes `cornerBoost`. Alpha
// compositing (SRC_OVER, how CSS multi-image stacks) yields
// `1 − (1−centerAlpha)(1−cornerBoost)`, which we want to equal `sideAlpha`
// — so we solve for `cornerBoost = (sideAlpha − centerAlpha) / (1 −
// centerAlpha)`. That keeps the corner totals exactly on target rather
// than approximately so.
//
// Same-hue alpha interpolation throughout: every stop keeps the
// background's RGB and varies only alpha, so the fade stays on a single
// hue instead of routing through the `transparent` keyword's implicit
// black.
//
// Stacking relative to the screen: rendered between the scroller and the
// sticky header in `ScreenScrollContainer`, and below the floating menu
// (rendered by the parent layout, which paints last). Both elements
// carry `pointerEvents='none'` so scroll and menu hits pass through.
export function FadeEdges({
  topHeight,
  bottomHeight,
}: {
  topHeight: number
  bottomHeight: number
}) {
  const theme = useTheme()
  const themeName = useThemeName()
  const bgHex = theme.background.val as string
  const isDark = themeName === 'dark'
  const peakAlpha = isDark ? PEAK_ALPHA_DARK : PEAK_ALPHA_LIGHT
  const darkenFactor = isDark ? DARKEN_FACTOR_DARK : 0
  const centerAlpha = peakAlpha * CENTER_DIP_RATIO
  const sideAlpha = peakAlpha * SIDE_RISE_RATIO
  // Alpha that, composited over `centerAlpha`, yields exactly `sideAlpha`.
  const cornerBoost = (sideAlpha - centerAlpha) / (1 - centerAlpha)

  const { topColors, bottomBackgroundImage } = useMemo(() => {
    const { r, g, b } = parseHex(bgHex)
    const dr = Math.round(r * (1 - darkenFactor))
    const dg = Math.round(g * (1 - darkenFactor))
    const db = Math.round(b * (1 - darkenFactor))
    const rgba = (a: number) => `rgba(${dr}, ${dg}, ${db}, ${a.toFixed(4)})`

    // Top: opaque (peak) at the screen edge → transparent into content.
    const topColors = SMOOTHSTEP.map((s) => rgba(peakAlpha * (1 - s)))

    // Shared helper: turn a smoothstep-curve alpha schedule into a CSS
    // stop list string. `alphaAt(i)` maps each of the 7 stop indices to
    // its target alpha; positions are evenly spaced.
    const stopsToCss = (alphaAt: (i: number) => number) =>
      STOPS.map((p, i) => `${rgba(alphaAt(i))} ${(p * 100).toFixed(2)}%`).join(', ')

    // Base linear: transparent at top, `centerAlpha` at bottom. Same
    // smoothstep curve as the top gradient; reversed so the opaque end
    // lands at 100%.
    const baseLinear = `linear-gradient(to top, ${stopsToCss(
      (i) => centerAlpha * (1 - SMOOTHSTEP[i])
    )})`

    // Corner radial: `cornerBoost` at p=0 (the corner itself), decaying on
    // a smoothstep curve to 0 at p=1 (the far end of the ellipse). Ellipse
    // size `50% 100%` keeps each corner's boost strictly within its own
    // half-width — the radials don't meaningfully overlap in the middle of
    // the bottom edge, which is what preserves the center dip.
    const cornerStops = stopsToCss((i) => cornerBoost * (1 - SMOOTHSTEP[i]))
    const leftCorner = `radial-gradient(ellipse 50% 100% at 0% 100%, ${cornerStops})`
    const rightCorner = `radial-gradient(ellipse 50% 100% at 100% 100%, ${cornerStops})`

    // Multi-image: first-listed paints on top.
    const bottomBackgroundImage = `${leftCorner}, ${rightCorner}, ${baseLinear}`

    return { topColors, bottomBackgroundImage }
  }, [bgHex, peakAlpha, centerAlpha, cornerBoost, darkenFactor])

  return (
    <>
      <LinearGradient
        position="absolute"
        top={0}
        left={0}
        right={0}
        height={topHeight}
        colors={topColors}
        locations={STOPS as unknown as number[]}
        start={[0, 0]}
        end={[0, 1]}
        pointerEvents="none"
      />
      <View
        pointerEvents="none"
        style={{
          position: 'absolute',
          bottom: 0,
          left: 0,
          right: 0,
          height: bottomHeight,
          experimental_backgroundImage: bottomBackgroundImage,
        }}
      />
    </>
  )
}

// Chrome-aware wrapper for the standard page layout: derives the fade heights
// from the safe-area insets, the floating menu bar, and (on tab routes) the
// tab strip, then renders `FadeEdges`. Mounted by `ScreenScrollContainer` so
// every feed / settings / grid screen fades into `$background` below the
// status bar and above the floating chrome. The overshoot lets the smoothstep
// tail die into content runway instead of terminating at the chrome boundary.
export function ScreenFadeEdges() {
  const insets = useSafeAreaInsets()
  const segments = useSegments()
  const onTabRoute = segments.some((s) => s === '(tabs)')
  const topChrome = insets.top
  const bottomChrome = insets.bottom + BAR_HEIGHT + (onTabRoute ? TAB_BAR_HEIGHT : 0)
  return (
    <FadeEdges
      topHeight={Math.round(topChrome * (1 + OVERSHOOT_RATIO_TOP))}
      bottomHeight={Math.round(bottomChrome * (1 + OVERSHOOT_RATIO_BOTTOM))}
    />
  )
}

function parseHex(hex: string): { r: number; g: number; b: number } {
  const h = hex.replace('#', '')
  if (h.length === 3) {
    return {
      r: parseInt(h[0] + h[0], 16),
      g: parseInt(h[1] + h[1], 16),
      b: parseInt(h[2] + h[2], 16),
    }
  }
  return {
    r: parseInt(h.slice(0, 2), 16),
    g: parseInt(h.slice(2, 4), 16),
    b: parseInt(h.slice(4, 6), 16),
  }
}
