import createEmojiRegex from 'emoji-regex'
import {Children, type ReactNode} from 'react'
import {
  Text as RNText,
  StyleSheet,
  type TextProps as RNTextProps,
  type TextStyle,
} from 'react-native'

import {
  atoms as a,
  FONT_FAMILY,
  FONT_FAMILY_DISPLAY,
  FONT_FAMILY_EMOJI,
  useTheme,
} from '@/sprinkles'

// scale with the user's type size, but bounded so layouts degrade
// gracefully at accessibility sizes
export const MAX_FONT_SCALE = 1.5

export type TextProps = RNTextProps & {
  // whether children may contain emoji; when set, emoji are wrapped in a
  // system-font span so apple color emoji metrics apply instead of inter's
  emoji?: boolean
}

export function normalizeTextStyles(
  styles: (TextStyle | false | null | undefined)[],
): TextStyle {
  const flat = StyleSheet.flatten(
    styles.filter(Boolean) as TextStyle[],
  )
  if (!flat) return {}
  const {lineHeight, fontSize, ...rest} = flat
  // convert leading multipliers (≤ 2) to px
  const resolvedLineHeight =
    lineHeight !== undefined &&
    lineHeight <= 2 &&
    fontSize !== undefined
      ? lineHeight * fontSize
      : lineHeight
  return {
    fontFamily: FONT_FAMILY,
    fontSize,
    lineHeight: resolvedLineHeight,
    ...rest,
  }
}

const EMOJI = createEmojiRegex()
// emoji-regex also matches ©, ®, ™, which render as plain text in ui copy;
// treat a bare one (no VS16) as text so it neither warns nor gets wrapped
const TEXT_SYMBOL = /^[©®™]$/

function hasRealEmoji(value: string) {
  return (
    value.match(EMOJI)?.some((e) => !TEXT_SYMBOL.test(e)) ?? false
  )
}

export function childHasEmoji(children: ReactNode) {
  let has = false
  Children.forEach(children, (child) => {
    if (typeof child === 'string' && hasRealEmoji(child)) has = true
  })
  return has
}

// split string children on emoji, rendering each in a system-font span so
// apple color emoji metrics apply instead of inter's clipping box
export function renderChildrenWithEmoji(
  children: ReactNode,
  style: TextStyle,
  emoji: boolean,
) {
  if (!emoji) return children
  return Children.map(children, (child) => {
    if (typeof child !== 'string') return child
    const emojis = child.match(EMOJI)
    if (!emojis) return child
    return child.split(EMOJI).map((part, i) => {
      const e = emojis[i]
      if (!e) return [part, null]
      if (TEXT_SYMBOL.test(e)) return [part, e]
      return [
        part,
        <RNText
          key={i}
          style={[style, {fontFamily: FONT_FAMILY_EMOJI}]}
        >
          {e}
        </RNText>,
      ]
    })
  })
}

export function Text({style, emoji, children, ...rest}: TextProps) {
  const t = useTheme()
  const s = normalizeTextStyles([
    a.text_sm,
    t.atoms.text,
    a.leading_snug,
    // StyleProp may be a nested array; flatten resolves it at runtime
    style as TextStyle,
  ])
  if (__DEV__ && !emoji && childHasEmoji(children)) {
    console.warn(
      `Text: emoji detected but emoji not enabled — add <Text emoji>: "${String(children)}"`,
    )
  }
  return (
    <RNText
      maxFontSizeMultiplier={MAX_FONT_SCALE}
      style={s}
      {...rest}
    >
      {renderChildrenWithEmoji(children, s, emoji ?? false)}
    </RNText>
  )
}

export function P({style, ...rest}: TextProps) {
  return (
    <Text style={[a.text_md, a.leading_normal, style]} {...rest} />
  )
}

function heading(defaultStyle: TextStyle) {
  return function Heading({style, ...rest}: TextProps) {
    return (
      <Text
        style={[
          {fontFamily: FONT_FAMILY_DISPLAY},
          defaultStyle,
          style,
        ]}
        {...rest}
      />
    )
  }
}

export const H1 = heading({
  fontSize: 48,
  lineHeight: 52,
  fontWeight: '600',
})
export const H2 = heading({
  fontSize: 32,
  lineHeight: 36,
  fontWeight: '600',
})
