import React, { useCallback, useEffect, useRef, useState } from 'react'

import Mdash from '../Mdash/Mdash'
import Tooltip, { type TooltipProps } from '../Tooltip/Tooltip'
import { trimText } from '../../services/HelperServiceTyped'
import { cn } from '../../services/cn'

export type TrimTextProps = {
  /** The text that you need truncated */
  text: string
  /** The number of characters before the text is truncated */
  limit?: number
  /** Position the tooltip will open */
  position?: TooltipProps['position']
  /** Optional class to add to the text */
  customClass?: string
  /** Optional prop to add a test id to the TrimText for QA testing */
  qaTestId?: string
}

const TrimText = ({
  text,
  limit,
  position,
  customClass = '',
  qaTestId = 'trim-text',
}: TrimTextProps): React.JSX.Element => {
  const divRef = useRef<HTMLDivElement>(null)
  const spanRef = useRef<HTMLSpanElement>(null)
  const [isTruncated, setIsTruncated] = useState(false)

  // Memoized function that checks if text is truncated by comparing scroll width (total content width) vs visible width
  const checkTruncation = useCallback(() => {
    if (divRef.current && spanRef.current) {
      const isTextTruncated =
        divRef.current?.scrollWidth > divRef.current.clientWidth ||
        spanRef.current?.scrollWidth > spanRef.current.clientWidth
      setIsTruncated(isTextTruncated)
    }
  }, [])

  // Defer the execution of checkTruncation to the next event loop iteration
  // to ensure the DOM is fully rendered before checking truncation
  setTimeout(() => {
    checkTruncation()
  }, 0)

  useEffect(() => {
    const handleResize = () => {
      checkTruncation()
    }
    window.addEventListener('resize', handleResize)

    // Create observer to watch for size changes
    const resizeObserver = new ResizeObserver(checkTruncation)
    if (divRef.current) {
      resizeObserver.observe(divRef.current)
      if (spanRef.current) {
        resizeObserver.observe(spanRef.current)
      }
    }
    return () => {
      window.removeEventListener('resize', handleResize)
      resizeObserver.disconnect()
    }
  }, [checkTruncation, text, divRef, spanRef])

  if (!text) return <Mdash />
  const trimmedText = trimText(text, limit ? limit : text.length)

  return limit ? (
    trimmedText.length <= (limit ? limit : text?.length) ? (
      <span className={cn('text-xs', customClass)} qa-test-id={qaTestId}>
        {text}
      </span>
    ) : (
      <Tooltip
        tooltipContent={text}
        position={position ? position : 'bottom'}
        qaTestId={qaTestId}
      >
        <span className={cn('text-xs', customClass)}>{trimmedText}</span>
      </Tooltip>
    )
  ) : (
    <div
      ref={divRef}
      className={cn(
        'block max-w-full overflow-hidden text-xs text-ellipsis whitespace-nowrap',
        customClass,
      )}
      qa-test-id={qaTestId}
    >
      {isTruncated ? (
        <Tooltip tooltipContent={text} position={position || 'bottom'}>
          <span
            ref={spanRef}
            className='inline-block max-w-full overflow-hidden text-ellipsis whitespace-nowrap'
          >
            {text}
          </span>
        </Tooltip>
      ) : (
        <span ref={spanRef}>{text}</span>
      )}
    </div>
  )
}

export default TrimText
