import { CSSProperties, forwardRef, memo, ReactNode } from 'react'
import { Color, colors } from '../../utils/colors.js'

type TextVariant =
  | 'title-regular'
  | 'title-medium'
  | 'title-bold'
  | 'heading-regular'
  | 'heading-medium'
  | 'heading-bold'
  | 'display-regular'
  | 'display-medium'
  | 'display-bold'
  | 'display-mono'
  | 'subheading-regular'
  | 'subheading-medium'
  | 'subheading-bold'
  | 'subtext-regular'
  | 'subtext-medium'
  | 'subtext-bold'
  | 'subtext-mono'

const SANS_FONT =
  '"Geist", "Inter",-apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue",sans-serif'
const MONOSPACE_FONT =
  '"Geist Mono","Roboto Mono",Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace'

const TEXT_VARIANT_STYLES: Record<TextVariant, CSSProperties> = {
  'title-regular': {
    fontFamily: SANS_FONT,
    fontSize: 28,
    lineHeight: '28px',
    fontWeight: 400,
    letterSpacing: -(28 * 0.01),
  },
  'title-medium': {
    fontFamily: SANS_FONT,
    fontSize: 28,
    lineHeight: '28px',
    fontWeight: 600,
    letterSpacing: -(28 * 0.02),
  },
  'title-bold': {
    fontFamily: SANS_FONT,
    fontSize: 28,
    lineHeight: '28px',
    fontWeight: 800,
    letterSpacing: -(28 * 0.02),
  },
  'heading-regular': {
    fontFamily: SANS_FONT,
    fontSize: 22,
    lineHeight: '22px',
    fontWeight: 300,
    letterSpacing: 0,
  },
  'heading-medium': {
    fontFamily: SANS_FONT,
    fontSize: 22,
    lineHeight: '22px',
    fontWeight: 500,
    letterSpacing: 0,
  },
  'heading-bold': {
    fontFamily: SANS_FONT,
    fontSize: 22,
    lineHeight: '22px',
    fontWeight: 800,
    letterSpacing: -(22 * 0.02),
  },
  'subheading-regular': {
    fontFamily: SANS_FONT,
    fontSize: 18,
    lineHeight: '18px',
    fontWeight: 300,
    letterSpacing: 18 * 0.01,
  },
  'subheading-medium': {
    fontFamily: SANS_FONT,
    fontSize: 18,
    lineHeight: '18px',
    fontWeight: 500,
    letterSpacing: 0,
  },
  'subheading-bold': {
    fontFamily: SANS_FONT,
    fontSize: 18,
    lineHeight: '18px',
    fontWeight: 700,
    letterSpacing: -(18 * 0.02),
  },
  'display-regular': {
    fontFamily: SANS_FONT,
    fontSize: 14,
    lineHeight: '14px',
    fontWeight: 400,
    letterSpacing: 14 * 0.01,
  },
  'display-medium': {
    fontFamily: SANS_FONT,
    fontSize: 14,
    lineHeight: '14px',
    fontWeight: 500,
    letterSpacing: 0,
  },
  'display-bold': {
    fontFamily: SANS_FONT,
    fontSize: 14,
    lineHeight: '14px',
    fontWeight: 700,
    letterSpacing: -(14 * 0.01),
  },
  'display-mono': {
    fontFamily: MONOSPACE_FONT,
    fontSize: 14,
    lineHeight: '14px',
    fontWeight: 400,
    letterSpacing: -(14 * 0.02),
  },
  'subtext-regular': {
    fontFamily: SANS_FONT,
    fontSize: 12,
    lineHeight: '12px',
    fontWeight: 400,
    letterSpacing: 12 * 0.01,
  },
  'subtext-medium': {
    fontFamily: SANS_FONT,
    fontSize: 12,
    lineHeight: '12px',
    fontWeight: 500,
    letterSpacing: 0,
  },
  'subtext-bold': {
    fontFamily: SANS_FONT,
    fontSize: 12,
    lineHeight: '12px',
    fontWeight: 700,
    letterSpacing: -(12 * 0.01),
  },
  'subtext-mono': {
    fontFamily: MONOSPACE_FONT,
    fontSize: 12,
    lineHeight: '12px',
    fontWeight: 400,
    letterSpacing: -(12 * 0.02),
  },
}

type TextProps = {
  children: ReactNode
  variant?: TextVariant
  align?: 'left' | 'center' | 'right'
  color?: 'inherit' | Color
  maxWidth?: number
  singleLine?: boolean
}

const Text = forwardRef<HTMLDivElement, TextProps>(
  (
    {
      children,
      variant = 'display-regular',
      color: colorProp = 'neutral100',
      align = 'left',
      maxWidth,
      singleLine,
    },
    ref,
  ) => {
    return (
      <div
        ref={ref}
        style={{
          position: 'relative',
          textAlign: align,
          color: colorProp === 'inherit' ? 'inherit' : colors[colorProp],
          lineHeight: 'normal',
          overflowWrap: 'break-word',
          textRendering: 'optimizeLegibility',
          WebkitFontSmoothing: 'antialiased',
          MozOsxFontSmoothing: 'grayscale',
          WebkitTextSizeAdjust: '100%',
          MozTextSizeAdjust: '100%',
          textSizeAdjust: '100%',
          maxWidth: maxWidth,
          ...((singleLine || maxWidth) && {
            textOverflow: 'ellipsis',
            whiteSpace: 'nowrap',
            overflow: 'hidden',
          }),
          ...TEXT_VARIANT_STYLES[variant],
        }}
      >
        {children}
      </div>
    )
  },
)

function measureTextWidth(text: string, variant: TextVariant) {
  const canvas = document.createElement('canvas')
  const ctx = canvas.getContext('2d')
  const variantStyles = TEXT_VARIANT_STYLES[variant]

  ctx.font = `${variantStyles.fontWeight} ${variantStyles.fontSize}px/${variantStyles.lineHeight} ${variantStyles.fontFamily}`
  ctx.letterSpacing = `${variantStyles.letterSpacing}px`

  return ctx.measureText(text).width + 0.5 // a little extra, just for good measure ;)
}

const MemoizedText = memo(Text)

export type { TextProps, TextVariant }
export { MemoizedText as Text, TEXT_VARIANT_STYLES, measureTextWidth }
