import { Image } from 'expo-image'
import { useMemo } from 'react'
import type { CustomEmoji } from 'src/lib/api-types'
import { Text, type TextProps } from 'tamagui'

const SHORTCODE_SPLIT_RE = /(:[a-zA-Z0-9_]{2,}:)/g

export type EmojiTextVariant = 'display' | 'body' | 'subhead' | 'caption'

// Maps the semantic variant onto the Halflight type ramp. `display` is the
// canonical "post-header / profile-header account name" slot (17pt, 600).
// Picking a variant beats piping `fontSize="$headline"` + `fontWeight="600"`
// through every call site.
const VARIANT_PROPS: Record<EmojiTextVariant, Partial<TextProps>> = {
  display: { fontSize: '$headline', fontWeight: '600' },
  body: { fontSize: '$body' },
  subhead: { fontSize: '$subhead', fontWeight: '500' },
  caption: { fontSize: '$caption' },
}

export interface EmojiTextProps extends Omit<TextProps, 'text'> {
  text: string
  emojis?: Array<CustomEmoji>
  /** Semantic typography slot. When set, pulls the right size + weight from
   * the Halflight ramp so call sites don't hand-roll both. Callers can still
   * override individual props (fontSize / fontWeight) on top of the variant. */
  variant?: EmojiTextVariant
  /** Pixel size for inline emoji images. Defaults to `fontSize * 1.2` when
   * fontSize is numeric, otherwise 18. */
  emojiSize?: number
}

export default function EmojiText({
  text,
  emojis,
  emojiSize,
  variant,
  ...rest
}: EmojiTextProps) {
  const emojiMap = useMemo(() => {
    if (!emojis || emojis.length === 0) return null
    const map = new Map<string, CustomEmoji>()
    for (const emoji of emojis) {
      if (emoji?.shortcode) map.set(emoji.shortcode, emoji)
    }
    return map.size > 0 ? map : null
  }, [emojis])

  // Variant props come first so call-site props (fontSize/fontWeight/color)
  // still win via the spread order. Default `color="$color"` means the text
  // tracks theme automatically; call sites override for secondary/tertiary tones.
  const merged: TextProps = {
    color: '$color',
    ...(variant ? VARIANT_PROPS[variant] : null),
    ...rest,
  }

  if (!text || !emojiMap || !text.includes(':')) {
    return <Text {...merged}>{text}</Text>
  }

  const fontSize = merged.fontSize
  const size =
    emojiSize ?? (typeof fontSize === 'number' ? Math.round(fontSize * 1.2) : 18)

  const segments = text.split(SHORTCODE_SPLIT_RE)
  let renderedAny = false
  const nodes = segments.map((segment, i) => {
    if (i % 2 === 1) {
      const shortcode = segment.slice(1, -1)
      const emoji = emojiMap.get(shortcode)
      if (emoji) {
        renderedAny = true
        return (
          <Text key={i}>
            <Image
              source={{ uri: emoji.url }}
              style={{
                width: size,
                height: size,
                marginBottom: -Math.round(size * 0.15),
              }}
              contentFit="contain"
              cachePolicy="memory-disk"
              accessibilityLabel={`:${shortcode}:`}
            />
          </Text>
        )
      }
    }
    return segment ? <Text key={i}>{segment}</Text> : null
  })

  if (!renderedAny) {
    return <Text {...merged}>{text}</Text>
  }

  return <Text {...merged}>{nodes}</Text>
}
