import { Image } from 'expo-image'
import { memo } from 'react'
import { useFavicon } from 'src/hooks/useFavicon'
import {
  faviconUrlFor,
  markFaviconAvailable,
  markFaviconUnavailable,
} from 'src/lib/favicon'
import SiteAvatar, { type SiteAvatarSize } from './SiteAvatar'

interface InlineProps {
  variant?: 'inline'
  host: string
}
interface ChipProps {
  variant: 'chip'
  host: string
  /** Chip size — defaults to `xxs` (16 px). Use `xs` for heavier surfaces
   *  like the profile website row, where a 16 px chip would look orphaned
   *  next to a `fontSize="$5"` headline. */
  size?: SiteAvatarSize
}

type Props = InlineProps | ChipProps

const INLINE_SIZE = 14
// Lift the icon off the baseline so its vertical centre aligns with the
// text's x-height. RN inline-image layout puts the image's bottom + the
// marginBottom value on the text baseline, so a positive value raises the
// icon. At 14 px against $5 body text the icon naturally floats too low
// (its centre sits roughly at the descender line); ~3 px of lift brings
// the icon's midline up onto the lowercase glyph midline.
const INLINE_BASELINE_OFFSET = Math.round(INLINE_SIZE * 0.2)
// Breathing room from the first glyph. `marginRight` on an inline image
// inside <Text> is silently dropped on iOS (RN materializes the image as an
// NSTextAttachment whose bounds come from width × height only). Widen the
// bounding box by this amount and pin the artwork to the left with
// `contentPosition` so the extra width becomes a real visible gap before
// the next glyph on both platforms.
const INLINE_TEXT_GAP = 5

function FaviconInner(props: Props) {
  const { host } = props
  const { status, enabled } = useFavicon(host)

  if (!host) return null

  // Chip variant — always renders something (image or initial fallback) so
  // the host-row layout in LinkPreviewCard / ProfileHeader never reflows.
  // When the pref is off we skip the URL so SiteAvatar shows just the
  // initial letter and no probe fires.
  if (props.variant === 'chip') {
    const showImage = enabled && status === 'available'
    return (
      <SiteAvatar
        size={props.size ?? 'xxs'}
        shape="rounded"
        borderStyle="default"
        priority="low"
        url={showImage ? faviconUrlFor(host) : null}
        initial={host.charAt(0)}
        onLoad={() => markFaviconAvailable(host)}
        onError={() => markFaviconUnavailable(host)}
        accessibilityLabel={`${host} icon`}
      />
    )
  }

  // Inline variant — bare <Image> so it composes inside <RNText>. Returns
  // null for unknown/unavailable/off so the visible link text doesn't carry
  // wasted layout for hosts that can't paint an icon. The probe fires from
  // the useFavicon effect on the unknown branch; when it lands, the host's
  // MMKV entry flips and the cell re-renders with the favicon inline.
  if (!enabled || status !== 'available') return null

  return (
    <Image
      source={{ uri: faviconUrlFor(host) }}
      style={{
        width: INLINE_SIZE + INLINE_TEXT_GAP,
        height: INLINE_SIZE,
        marginBottom: INLINE_BASELINE_OFFSET,
      }}
      contentFit="contain"
      contentPosition="left center"
      cachePolicy="memory-disk"
      priority="low"
      onError={() => markFaviconUnavailable(host)}
      onLoad={() => markFaviconAvailable(host)}
      accessibilityLabel={`${host} icon`}
    />
  )
}

export const Favicon = memo(FaviconInner)
