import { includes, noop } from 'lodash'
import { Edit, Eye } from 'lucide-react'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
  Input,
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput,
  InputGroupTextarea,
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
  TextArea,
} from 'ui'
import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'

import { DATETIME_TYPES, JSON_TYPES, TEXT_TYPES } from '../SidePanelEditor.constants'
import { DateTimeInput } from './DateTimeInput'
import type { EditValue, RowField } from './RowEditor.types'
import { isValueTruncated } from './RowEditor.utils'

const TRUNCATE_DESCRIPTION =
  'Note: Value is too large to be rendered in the dashboard. Please expand the editor to edit the value'

export interface InputFieldProps {
  field: RowField
  errors: any
  isNewRow?: boolean
  isEditable?: boolean
  onUpdateField?: (changes: object) => void
  onEditJson?: (data: any) => void
  onEditText?: (data: EditValue) => void
  onSelectForeignKey?: () => void
}

export const InputField = ({
  field,
  errors,
  isNewRow = false,
  isEditable = true,
  onUpdateField = noop,
  onEditJson = noop,
  onEditText = noop,
  onSelectForeignKey = noop,
}: InputFieldProps) => {
  if (field.enums.length > 0) {
    const isArray = field.format[0] === '_'
    if (isArray) {
      return (
        <FormItemLayout
          isReactForm={false}
          layout="horizontal"
          label={field.name}
          labelOptional={field.format}
          description={field.comment}
          className="[&>div:first-child>span]:text-foreground-lighter"
          error={errors[field.name]}
        >
          <TextArea
            data-testid={`${field.name}-input`}
            className="text-sm"
            disabled={!isEditable}
            rows={5}
            value={field.value ?? ''}
            placeholder={
              field.defaultValue === null
                ? ''
                : typeof field.defaultValue === 'string' && field.defaultValue.length === 0
                  ? 'EMPTY'
                  : `Default: ${field.defaultValue}`
            }
            onChange={(event) => onUpdateField({ [field.name]: event.target.value })}
          />
        </FormItemLayout>
      )
    } else {
      return (
        <FormItemLayout
          isReactForm={false}
          layout="horizontal"
          label={field.name}
          labelOptional={field.format}
          description={field.comment}
          className="[&>div:first-child>span]:text-foreground-lighter"
        >
          <Select
            value={field.value ?? ''}
            onValueChange={(value: string) => onUpdateField({ [field.name]: value })}
            disabled={!isEditable}
          >
            <SelectTrigger>
              <SelectValue placeholder="---" />
            </SelectTrigger>
            <SelectContent>
              <SelectGroup>
                <SelectItem value={null as any}>---</SelectItem>
                {field.enums.map((value) => (
                  <SelectItem key={value} value={value}>
                    {value}
                  </SelectItem>
                ))}
              </SelectGroup>
            </SelectContent>
          </Select>
        </FormItemLayout>
      )
    }
  }

  if (field.foreignKey !== undefined) {
    return (
      <FormItemLayout
        isReactForm={false}
        layout="horizontal"
        label={field.name}
        labelOptional={field.format}
        description={
          <>
            <>
              {field.comment && (
                <span className="text-sm text-foreground-lighter">{field.comment} </span>
              )}
              <span className="text-sm text-foreground-lighter">
                {field.comment && '('}Has a foreign key relation to
              </span>
              <span className="text-code font-mono text-xs text-foreground-lighter">
                {field.foreignKey.target_table_schema}.{field.foreignKey.target_table_name}.
                {field.foreignKey.target_column_name}
              </span>
              {field.comment && <span className="text-sm text-foreground-lighter">{`)`}</span>}
            </>
          </>
        }
        className="[&>div:first-child>span]:text-foreground-lighter"
        error={errors[field.name]}
      >
        <InputGroup>
          <InputGroupInput
            data-testid={`${field.name}-input`}
            placeholder={field.defaultValue !== null ? `Default: ${field.defaultValue}` : 'NULL'}
            value={field.value ?? ''}
            disabled={!isEditable}
            onChange={(event) => onUpdateField({ [field.name]: event.target.value })}
          />
          <InputGroupAddon align="inline-end">
            {isEditable && (
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <InputGroupButton type="default" icon={<Edit />} className="px-1.5" />
                </DropdownMenuTrigger>
                <DropdownMenuContent align="end" className="w-28">
                  {field.isNullable && (
                    <DropdownMenuItem onClick={() => onUpdateField({ [field.name]: null })}>
                      Set to NULL
                    </DropdownMenuItem>
                  )}
                  <DropdownMenuItem onClick={onSelectForeignKey}>Select record</DropdownMenuItem>
                </DropdownMenuContent>
              </DropdownMenu>
            )}
          </InputGroupAddon>
        </InputGroup>
      </FormItemLayout>
    )
  }

  if (includes(TEXT_TYPES, field.format)) {
    const isTruncated = isValueTruncated(field.value)

    /**
     * Handle `undefined` as the default value of the input field
     * Otherwise, NULL should be treated as NULL, empty strings should be treated as empty strings
     */

    return (
      <FormItemLayout
        isReactForm={false}
        layout="horizontal"
        label={field.name}
        labelOptional={field.format}
        description={
          <>
            {field.comment && <p>{field.comment}</p>}
            {isTruncated && <p>{TRUNCATE_DESCRIPTION}</p>}
          </>
        }
        error={errors[field.name]}
      >
        <InputGroup>
          <InputGroupTextarea
            data-testid={`${field.name}-input`}
            className="text-sm"
            disabled={!isEditable || isTruncated}
            rows={5}
            value={field.value ?? ''}
            placeholder={
              field.value === null
                ? 'NULL'
                : field.value === '' ||
                    (typeof field.defaultValue === 'string' && field.defaultValue.length === 0)
                  ? 'EMPTY'
                  : `Default: ${field.defaultValue === null ? 'NULL' : field.defaultValue}`
            }
            onChange={(event) => onUpdateField({ [field.name]: event.target.value })}
          />
          <InputGroupAddon align="block-end">
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <InputGroupButton
                  data-testid={`${field.name}-field-actions`}
                  type="default"
                  icon={<Edit />}
                  className="ml-auto px-1.5"
                />
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end" className="w-28">
                {isEditable && (
                  <>
                    <DropdownMenuItem onClick={() => onUpdateField({ [field.name]: null })}>
                      Set to NULL
                    </DropdownMenuItem>
                    {isNewRow && (
                      <DropdownMenuItem onClick={() => onUpdateField({ [field.name]: undefined })}>
                        Set to Default
                      </DropdownMenuItem>
                    )}
                    <DropdownMenuSeparator />
                  </>
                )}

                <DropdownMenuItem
                  onClick={() => onEditText({ column: field.name, value: field.value || '' })}
                >
                  Expand editor
                </DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
          </InputGroupAddon>
        </InputGroup>
      </FormItemLayout>
    )
  }

  if (includes(JSON_TYPES, field.format)) {
    const isTruncated = isValueTruncated(field.value)

    return (
      <FormItemLayout
        isReactForm={false}
        layout="horizontal"
        label={field.name}
        labelOptional={field.format}
        description={
          <>
            {field.comment && <p>{field.comment}</p>}
            {isTruncated && <p>{TRUNCATE_DESCRIPTION}</p>}
          </>
        }
        error={errors[field.name]}
      >
        <InputGroup>
          <InputGroupInput
            data-testid={`${field.name}-input`}
            value={field.value ?? ''}
            disabled={!isEditable || isTruncated}
            placeholder={!!field?.defaultValue ? `Default: ${field.defaultValue}` : 'NULL'}
            onChange={(event) => onUpdateField({ [field.name]: event.target.value })}
          />
          <InputGroupAddon align="inline-end">
            <InputGroupButton
              type="default"
              htmlType="button"
              onClick={() => onEditJson({ column: field.name, value: field.value })}
              icon={isEditable ? <Edit /> : <Eye />}
            >
              {isEditable ? 'Edit' : 'View JSON'}
            </InputGroupButton>
          </InputGroupAddon>
        </InputGroup>
      </FormItemLayout>
    )
  }

  if (includes(DATETIME_TYPES, field.format)) {
    return (
      <DateTimeInput
        name={field.name}
        format={field.format}
        value={field.value ?? ''}
        isNullable={field.isNullable}
        description={
          <>
            {field.defaultValue && <p>Default: {field.defaultValue}</p>}
            {field.comment && <p>{field.comment}</p>}
          </>
        }
        onChange={(value) => onUpdateField({ [field.name]: value })}
        disabled={!isEditable}
      />
    )
  }

  if (field.format === 'bool') {
    const options = [
      { value: 'true', label: 'TRUE' },
      { value: 'false', label: 'FALSE' },
      ...(field.isNullable ? [{ value: 'null', label: 'NULL' }] : []),
    ]

    const defaultValue = field.value === null ? undefined : field.value

    return (
      <FormItemLayout
        isReactForm={false}
        layout="horizontal"
        label={field.name}
        labelOptional={field.format}
        description={field.comment}
      >
        <Select
          value={defaultValue === null ? 'null' : defaultValue}
          onValueChange={(value) => onUpdateField({ [field.name]: value })}
          disabled={!isEditable}
        >
          <SelectTrigger>
            <SelectValue placeholder="Select a value" />
          </SelectTrigger>
          <SelectContent>
            <SelectGroup>
              {options.map((option) => (
                <SelectItem key={option.value} value={option.value}>
                  {option.label}
                </SelectItem>
              ))}
            </SelectGroup>
          </SelectContent>
        </Select>
      </FormItemLayout>
    )
  }

  if (field.format === 'bytea') {
    return (
      <FormItemLayout
        isReactForm={false}
        layout="horizontal"
        label={field.name}
        labelOptional={field.format}
        description={
          <>
            {field.comment && <p>{field.comment}</p>}
            <p>Bytea columns are edited and displayed as hex in the dashboard</p>
          </>
        }
        error={errors[field.name]}
      >
        <Input
          data-testid={`${field.name}-input`}
          value={field.value ?? ''}
          placeholder={`\\x`}
          disabled={!isEditable}
          onChange={(event) => onUpdateField({ [field.name]: event.target.value })}
        />
      </FormItemLayout>
    )
  }

  const isTruncated = isValueTruncated(field.value)

  return (
    <FormItemLayout
      isReactForm={false}
      layout="horizontal"
      label={field.name}
      labelOptional={field.format}
      description={
        <>
          {field.comment && <p>{field.comment}</p>}
          {isTruncated && <p>{TRUNCATE_DESCRIPTION}</p>}
        </>
      }
      error={errors[field.name]}
    >
      <InputGroup>
        <InputGroupInput
          data-testid={`${field.name}-input`}
          value={field.value ?? ''}
          placeholder={
            field.isIdentity
              ? 'Automatically generated as identity'
              : field.defaultValue !== null
                ? `Default: ${field.defaultValue}`
                : 'NULL'
          }
          disabled={!isEditable || isTruncated}
          onChange={(event) => onUpdateField({ [field.name]: event.target.value })}
        />
        <InputGroupAddon align="inline-end">
          {isTruncated ? (
            <InputGroupButton
              type="default"
              htmlType="button"
              onClick={() => onEditJson({ column: field.name, value: field.value })}
              icon={isEditable ? <Edit /> : <Eye />}
            >
              {isEditable ? 'Edit' : 'View'}
            </InputGroupButton>
          ) : undefined}
        </InputGroupAddon>
      </InputGroup>
    </FormItemLayout>
  )
}
