import { Button, Box, Input, Typography, Flex } from '@tidbcloud/uikit'
import { IconArrowDown, IconArrowUp, IconTrash01 } from '@tidbcloud/uikit/icons'
import React, { useCallback, useMemo } from 'react'
import { useFormContext, RegisterOptions, useFieldArray } from 'react-hook-form'

import { eventTracking } from 'dbaas/utils/tracking'

import { IArrayItem, IFormInputProps } from './types'

// TODO: support other types of input (like number, checkbox, radio)

const DefaultArrayItem: React.FC<React.PropsWithChildren<IFieldItemOptions & { index: number }>> = React.memo(
  (props) => {
    const handleUp = () => {
      props.onReorder(props.index, props.index - 1)
      eventTracking('ArrayFields Move Up Button Clicked', {
        ...(props.eventParams || {})
      })
    }

    const handleDown = () => {
      props.onReorder(props.index, props.index + 1)
      eventTracking('ArrayFields Move Down Button Clicked', {
        ...(props.eventParams || {})
      })
    }

    const handleDrop = () => {
      props.onDropIndex(props.index)
      eventTracking('ArrayFields Drop Button Clicked', {
        ...(props.eventParams || {})
      })
    }

    return (
      <Flex align="flex-start" mb={16}>
        {/* inner input or schema inputs */}
        {props.children}
        <Box mt={props.index === 0 ? 24 : 2}>
          {props.hasMoveUp && (
            <Button
              component={IconArrowUp}
              type="button"
              size="xs"
              variant="subtle"
              color="carbon.8"
              disabled={props.index === 0}
              onClick={handleUp}
            />
          )}
          {props.hasMoveDown && (
            <Button
              component={IconArrowDown}
              type="button"
              size="xs"
              variant="subtle"
              color="carbon.8"
              disabled={props.isLastItem}
              onClick={handleDown}
            />
          )}
          {props.hasRemove && (
            <Button
              data-test-name="array-item-remove"
              leftSection={<IconTrash01 size={15} />}
              size="xs"
              variant="subtle"
              color="carbon.8"
              disabled={props.clearable ? false : props.isLastItem}
              onClick={handleDrop}
            />
          )}
        </Box>
      </Flex>
    )
  }
)

interface IFieldItemOptions {
  onReorder: (origin: number, reorder: number) => void
  onDropIndex: (index: number) => void
  isLastItem?: boolean
  hasRemove?: boolean
  hasMoveUp?: boolean
  hasMoveDown?: boolean
  horizontalButtons?: boolean
  clearable?: boolean
  eventParams?: any
}

type IField = IFormInputProps

export interface IArrayFieldsProps {
  fieldsName: string // unique array fields name
  fieldProps: IField[] // input field props
  schema: { [name: string]: RegisterOptions } // input validation schema

  title?: string
  enableAdd?: boolean
  removable?: boolean
  orderable?: boolean
  clearable?: boolean
}

function ArrayFields({
  fieldsName,
  fieldProps,
  schema,

  title,
  enableAdd = true,
  removable = true,
  orderable = false,
  clearable = false
}: IArrayFieldsProps): React.ReactElement {
  const defaultItemOptions = useMemo(
    () => ({
      hasRemove: removable,
      hasMoveUp: orderable,
      hasMoveDown: orderable,
      horizontalButtons: true
    }),
    [removable, orderable]
  )
  // https://react-hook-form.com/api#useFormContext
  const {
    register,
    control,
    formState: { errors }
  } = useFormContext()

  // https://react-hook-form.com/api#useFieldArray
  const { fields, append, remove, swap } = useFieldArray({
    control, // control props comes from useForm (optional: if you are using FormContext)
    name: fieldsName // unique name for your Field Array
    // keyName: "id", default to "id", you can change the key name
  })

  const handleReorder = useCallback(
    (originIndex: number, newIndex: number) => {
      swap(originIndex, newIndex)
    },
    [swap]
  )

  const handleDropIndex = useCallback(
    (index: number) => {
      remove(index)
    },
    [remove]
  )

  const handleAddClick = () => {
    const newItem = fieldProps.reduce((obj, item) => Object.assign(obj, { [item.name]: '' }), {}) as IArrayItem

    append(newItem)
    eventTracking('Add Ip Address Button Clicked')
  }

  return (
    <Box mb={24}>
      {title && (
        <Typography variant="headline-sm" fz={16}>
          tilte
        </Typography>
      )}

      <div>
        {fields.map((item, index: number) => {
          if (!item) return null

          const { id } = item
          const itemProps = {
            index,
            isLastItem: fields.length === 1,
            clearable,
            onReorder: handleReorder,
            onDropIndex: handleDropIndex,
            ...defaultItemOptions
          }

          return (
            <DefaultArrayItem key={`array-item-${id}`} {...itemProps}>
              {fieldProps.map((field: IField) => {
                const name = field.name
                // https://react-hook-form.com/api#useFieldArray
                return (
                  <Box key={`input-field-${id}-${name}`} sx={{ flex: 1, margin: 0, marginRight: 16 }}>
                    {index === 0 && <label htmlFor={`${fieldsName}[${index}].${name}`}>{field.label}</label>}
                    <Input
                      {...register(`${fieldsName}[${index}].${name}`, schema[name])}
                      placeholder={field.placeholder}
                      defaultValue={item[`${name}`]}
                    />
                    {/* for input validation message */}
                    <Typography variant="body-lg" c={!!errors?.[fieldsName]?.[index]?.[name] ? 'red' : 'carbon.6'}>
                      {errors?.[fieldsName]?.[index]?.[name]?.message}
                    </Typography>
                  </Box>
                )
              })}
            </DefaultArrayItem>
          )
        })}
      </div>

      {enableAdd && (
        <Box ta="right">
          <Button type="button" size="sm" variant="default" mr={6} onClick={handleAddClick}>
            Add Item
          </Button>
        </Box>
      )}
    </Box>
  )
}

export default React.memo(ArrayFields)
