import { useState, useRef } from 'react'
import PropTypes from 'prop-types'
import { DatePicker, Input } from 'antd'
import dayjs from 'dayjs'
import FunctionIcon from '../../Icons/FunctionIcon'

const OPERATOR_KEYS = ['+', '-']

const TIME_UNITS = {
  s: 'second',
  m: 'minute',
  h: 'hour',
  d: 'day',
  w: 'week',
  M: 'month',
  y: 'year'
}

const TERM_REGEX = /([+-])(\d+(?:\.\d+)?)([smhdwMy])/g
const RELATIVE_TERM_START = /[+-]\d+(?:\.\d+)?[smhdwMy]/
const DATE_EXPR_FORMAT = 'YYYY-MM-DD HH:mm:ss'

function formatExprBase(date) {
  if (date == null || !dayjs(date).isValid()) return ''
  return dayjs(date).format(DATE_EXPR_FORMAT)
}

function applyTimeExpr(fallbackBase, expr) {
  const trimmed = String(expr).trim()
  if (!trimmed) return null

  const termStart = trimmed.search(RELATIVE_TERM_START)
  if (termStart === -1) return null

  let base
  let termsPart

  if (termStart === 0) {
    base =
      fallbackBase && dayjs(fallbackBase).isValid()
        ? dayjs(fallbackBase)
        : dayjs()
    termsPart = trimmed
  } else {
    const baseStr = trimmed.slice(0, termStart).trim()
    const parsed = dayjs(baseStr)
    base = parsed.isValid()
      ? parsed
      : fallbackBase && dayjs(fallbackBase).isValid()
        ? dayjs(fallbackBase)
        : dayjs()
    termsPart = trimmed.slice(termStart)
  }

  const termsStr = termsPart.replace(/\s/g, '')
  const terms = [...termsStr.matchAll(TERM_REGEX)]
  if (!terms.length) return null

  const consumed = terms.map((t) => t[0]).join('')
  if (consumed !== termsStr) return null

  let result = base
  for (const [, sign, amount, unit] of terms) {
    const unitKey = TIME_UNITS[unit]
    if (!unitKey) return null
    const delta = sign === '+' ? parseFloat(amount) : -parseFloat(amount)
    result = result.add(delta, unitKey)
  }

  return result
}

const TimeEdit = ({
  value,
  onChange,
  onBlur,
  disabled,
  ...rest
}) => {
  const [isExprMode, setIsExprMode] = useState(false)
  const [exprValue, setExprValue] = useState('')
  const inputRef = useRef(null)

  const dayjsValue =
    value == null ? null : dayjs.isDayjs(value) ? value : dayjs(value)
  const pickerValue =
    dayjsValue && dayjsValue.isValid() ? dayjsValue : null

  const switchToExprMode = (initialValue) => {
    setIsExprMode(true)
    setExprValue(initialValue)
    setTimeout(() => {
      const input = inputRef.current?.getElementsByTagName('input')[0]
      input?.focus()
    }, 0)
  }

  const exitExprMode = (result) => {
    setIsExprMode(false)
    setExprValue('')
    if (result != null && result.isValid()) {
      onChange?.(result)
    }
  }

  const commitExpr = () => {
    const expr = exprValue.trim()
    if (expr && expr.match(/[+-]/) && expr.match(/[smhdwMy]/)) {
      const result = applyTimeExpr(pickerValue, expr)
      if (result != null) {
        exitExprMode(result)
        return
      }
    }
    exitExprMode(null)
  }

  const handleDatePickerKeyDown = (e) => {
    if (OPERATOR_KEYS.includes(e.key)) {
      e.preventDefault()
      e.stopPropagation()
      switchToExprMode(formatExprBase(pickerValue) + e.key)
    }
  }

  const handleInputKeyDown = (e) => {
    if (e.key === 'Enter' || e.key === '=') {
      e.preventDefault()
      const result = applyTimeExpr(pickerValue, exprValue)
      if (result != null) {
        exitExprMode(result)
      }
    }
  }

  const handleInputBlur = (e) => {
    commitExpr()
    onBlur?.(e)
  }

  if (isExprMode) {
    return (
      <div className='input-number-cal' ref={inputRef}>
        <Input
          value={exprValue}
          onChange={(e) => setExprValue(e.target.value)}
          onKeyDown={handleInputKeyDown}
          onBlur={handleInputBlur}
          disabled={disabled}
          placeholder='+1d, -2m, +1h'
          style={{ width: '100%' }}
        />
        <div className='input-number-cal-icon'>
          <FunctionIcon style={{ fontSize: 24 }} />
        </div>
      </div>
    )
  }

  return (
    <DatePicker
      showTime
      style={{ width: '100%' }}
      value={pickerValue}
      onChange={onChange}
      onKeyDown={handleDatePickerKeyDown}
      disabled={disabled}
      {...rest}
    />
  )
}

TimeEdit.propTypes = {
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
  onChange: PropTypes.func,
  onBlur: PropTypes.func,
  disabled: PropTypes.bool
}

export default TimeEdit
