'use client'

import type { ChangeEvent, FC } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import * as React from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { checkKeys } from '@/utils/var'
import VarHighlight from '../../app/configuration/base/var-highlight'

// regex to match the {{}} and replace it with a span
const regex = /\{\{([^}]+)\}\}/g

export const getInputKeys = (value: string) => {
  const keys = value.match(regex)?.map((item) => {
    return item.replace('{{', '').replace('}}', '')
  }) || []
  const keyObj: Record<string, boolean> = {}
  // remove duplicate keys
  const res: string[] = []
  keys.forEach((key) => {
    if (keyObj[key])
      return

    keyObj[key] = true
    res.push(key)
  })
  return res
}

type IBlockInputProps = {
  value: string
  className?: string // wrapper class
  highLightClassName?: string // class for the highlighted text default is text-blue-500
  readonly?: boolean
  onConfirm?: (value: string, keys: string[]) => void
}

const BlockInput: FC<IBlockInputProps> = ({
  value = '',
  className,
  readonly = false,
  onConfirm,
}) => {
  const { t } = useTranslation()
  // current is used to store the current value of the contentEditable element
  const [currentValue, setCurrentValue] = useState<string>(value)
  useEffect(() => {
    setCurrentValue(value)
  }, [value])

  const contentEditableRef = useRef<HTMLTextAreaElement>(null)
  const [isEditing, setIsEditing] = useState<boolean>(false)
  useEffect(() => {
    if (isEditing && contentEditableRef.current) {
      // TODO: Focus at the click position
      if (currentValue)
        contentEditableRef.current.setSelectionRange(currentValue.length, currentValue.length)

      contentEditableRef.current.focus()
    }
  }, [isEditing])

  const style = cn({
    'block size-full border-0 px-4 py-2 text-sm break-all text-gray-900 outline-0': true,
    'block-input--editing': isEditing,
  })

  const renderSafeContent = (value: string) => {
    const parts = value.split(/(\{\{[^}]+\}\}|\n)/g)
    return parts.map((part, index) => {
      const variableMatch = /^\{\{([^}]+)\}\}$/.exec(part)
      if (variableMatch) {
        return (
          <VarHighlight
            key={`var-${index}`}
            name={variableMatch[1]!}
          />
        )
      }
      if (part === '\n')
        return <br key={`br-${index}`} />

      return <span key={`text-${index}`}>{part}</span>
    })
  }

  // Not use useCallback. That will cause out callback get old data.
  const handleSubmit = (value: string) => {
    if (onConfirm) {
      const keys = getInputKeys(value)
      const result = checkKeys(keys)
      if (!result.isValid) {
        toast.error(t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }))
        return
      }
      onConfirm(value, keys)
    }
  }

  const onValueChange = useCallback((e: ChangeEvent<HTMLTextAreaElement>) => {
    const value = e.target.value
    setCurrentValue(value)
    handleSubmit(value)
  }, [])

  // Prevent rerendering caused cursor to jump to the start of the contentEditable element
  const TextAreaContentView = () => {
    return (
      <div className={cn(style, className)} data-testid="block-input-content">
        {renderSafeContent(currentValue || '')}
      </div>
    )
  }

  const placeholder = ''
  const editAreaClassName = 'focus:outline-hidden bg-transparent text-sm'

  const textAreaContent = (
    <div className={cn(readonly ? 'max-h-[180px] pb-5' : 'h-[180px]', 'overflow-y-auto')} onClick={() => !readonly && setIsEditing(true)}>
      {isEditing
        ? (
            <div className="h-full px-4 py-2">
              <textarea
                ref={contentEditableRef}
                className={cn(editAreaClassName, 'block size-full resize-none')}
                placeholder={placeholder}
                onChange={onValueChange}
                value={currentValue}
                onBlur={() => {
                  blur()
                  setIsEditing(false)
                // click confirm also make blur. Then outer value is change. So below code has problem.
                // setTimeout(() => {
                //   handleCancel()
                // }, 1000)
                }}
              />
            </div>
          )
        : <TextAreaContentView />}
    </div>
  )

  return (
    <div className={cn('block-input w-full overflow-y-auto rounded-xl border-none bg-white')} data-testid="block-input">
      {textAreaContent}
      {/* footer */}
      {!readonly && (
        <div className="flex pb-2 pl-4">
          <div className="h-[18px] rounded-md bg-gray-100 px-1 text-xs leading-[18px] text-gray-500">{currentValue?.length}</div>
        </div>
      )}

    </div>
  )
}

export default React.memo(BlockInput)
