import React, {
  useContext,
  useImperativeHandle,
  useRef,
  useState
} from 'react';
import _uniqueId from 'lodash/uniqueId';
import {
  Controller,
  DeepPartial,
  FieldValues,
  FormProvider,
  Path,
  useForm,
  useFormContext,
  UseFormReturn
} from 'react-hook-form';
import locale from 'antd/es/date-picker/locale/zh_CN';
import { Select as AntdSelect, DatePicker } from 'antd';
import 'moment/locale/zh-cn';
import Moment from 'moment';
import { Drawer, DrawerButton } from './drawer';

export interface IStepFormHandle<T> {
  getData: () => Promise<T | null>;
}

export interface IWizardData {
  [key: string]: any;
}

export interface IStepForm<T> {
  formRef: React.RefCallback<IStepFormHandle<T>>;
}

export const wizardContext = React.createContext<IWizardData>({});

interface IUseWizardReturn<T> {
  wizardData: IWizardData;
  formProps: UseFormReturn<T>;
}

interface IUseWizardProps<T> {
  formRef: React.ForwardedRef<IStepFormHandle<T>>;
  defaultValues?: DeepPartial<T>;
}

export const useWizard = <T,>({
  formRef,
  defaultValues
}: IUseWizardProps<T>): IUseWizardReturn<T> => {
  const wizardData = useContext(wizardContext);
  const formProps = useForm<T>({ defaultValues });
  const { trigger, getValues } = formProps;

  useImperativeHandle(formRef, () => {
    return {
      getData: async () => {
        if (await trigger()) {
          return getValues();
        }
        return null;
      }
    };
  });

  return { wizardData, formProps };
};

interface IFormContainer<T>
  extends React.PropsWithChildren<{
    drawerLabel?: string;
    drawer?: React.ReactNode;
    darkDrawer?: boolean;
    formProps: UseFormReturn<T>;
  }> {}

export const FormContainer = <T,>({
  drawerLabel,
  drawer,
  darkDrawer,
  formProps,
  children
}: IFormContainer<T>): React.ReactElement => {
  const [showDrawer, setShowDrawer] = useState(false);
  return (
    <FormProvider {...formProps}>
      <form onSubmit={evt => evt.preventDefault()}>
        {children}
        {drawerLabel !== undefined && (
          <>
            <DrawerButton
              label={drawerLabel}
              dark={darkDrawer}
              onClick={() => setShowDrawer(true)}
            />
            <Drawer
              show={showDrawer}
              dark={darkDrawer}
              title={drawerLabel}
              onClose={() => setShowDrawer(false)}
            >
              {drawer}
            </Drawer>
          </>
        )}
      </form>
    </FormProvider>
  );
};

export const Select = <T extends FieldValues>({
  label,
  name,
  placeholder,
  options,
  required,
  changeCallback
}: {
  label: string;
  name: Path<T>;
  placeholder?: string;
  options: { label?: string; value: string }[];
  required?: boolean;
  changeCallback?: () => void;
}): React.ReactElement => {
  const [id] = useState(() => _uniqueId(name));
  const { clearErrors, control } = useFormContext<T>();

  return (
    <Controller
      control={control}
      name={name}
      rules={{ required }}
      render={({ field: { onChange, value }, fieldState: { error } }) => {
        return (
          <div className="form-group">
            <label
              className={'form-label ' + (required ? 'field-required' : '')}
              htmlFor={id}
            >
              {label}
            </label>
            <div
              className={
                'form-input-wrapper ' + (error ? 'form-input-error' : '')
              }
            >
              <AntdSelect
                className="form-input-select"
                id={id}
                value={value}
                placeholder={placeholder}
                onChange={value => {
                  clearErrors(name);
                  onChange(value);
                  if (changeCallback) {
                    changeCallback();
                  }
                }}
                allowClear
              >
                {options.map(item => (
                  <AntdSelect.Option
                    className="form-input-option"
                    key={item.value}
                    value={item.value}
                  >
                    {item.label || item.value}
                  </AntdSelect.Option>
                ))}
              </AntdSelect>
              {error && <div className="form-input-errmsg">该项不能为空</div>}
            </div>
          </div>
        );
      }}
    />
  );
};

export const Input = <T extends FieldValues>({
  label,
  name,
  placeholder,
  required
}: {
  label: string;
  name: Path<T>;
  placeholder?: string;
  required?: boolean;
}): React.ReactElement => {
  const [id] = useState(() => _uniqueId(name));
  const { register, getFieldState, clearErrors, formState } = useFormContext<
    T
  >();
  const { error } = getFieldState(name, formState);
  const fieldProps = register(name, { required });
  const { onChange } = fieldProps;

  const handleChange = (evt: React.ChangeEvent<HTMLInputElement>) => {
    clearErrors(name);
    void onChange(evt);
  };

  return (
    <div className="form-group">
      <label
        className={'form-label ' + (required ? 'field-required' : '')}
        htmlFor={id}
      >
        {label}
      </label>
      <div
        className={'form-input-wrapper ' + (error ? 'form-input-error' : '')}
      >
        <input
          className="form-input"
          type="text"
          autoComplete="off"
          id={id}
          placeholder={placeholder}
          {...fieldProps}
          onChange={handleChange}
        />
        {error && <div className="form-input-errmsg">该项不能为空</div>}
      </div>
    </div>
  );
};

export const Textarea = <T extends FieldValues>({
  label,
  name,
  placeholder,
  required
}: {
  label: string;
  name: Path<T>;
  placeholder?: string;
  required?: boolean;
}): React.ReactElement => {
  const [id] = useState(() => _uniqueId(name));
  const { register, getFieldState, clearErrors, formState } = useFormContext<
    T
  >();
  const { error } = getFieldState(name, formState);
  const fieldProps = register(name, { required });
  const { onChange } = fieldProps;

  const handleChange = (evt: React.ChangeEvent<HTMLTextAreaElement>) => {
    clearErrors(name);
    void onChange(evt);
  };

  return (
    <div className="form-group">
      <label
        className={'form-label ' + (required ? 'field-required' : '')}
        htmlFor={id}
      >
        {label}
      </label>
      <div
        className={'form-input-wrapper ' + (error ? 'form-input-error' : '')}
      >
        <textarea
          className="form-input"
          autoComplete="off"
          id={id}
          placeholder={placeholder}
          {...fieldProps}
          onChange={handleChange}
        />
        {error && <div className="form-input-errmsg">该项不能为空</div>}
      </div>
    </div>
  );
};

const NumberInput: React.FunctionComponent<{
  id?: string;
  value?: number;
  onChange: (value: number) => void;
}> = ({ id, value, onChange }) => {
  const valStr = value !== undefined ? value.toString() : '';
  const [draft, setDraft] = useState(valStr);
  const [isEditing, setIsEditing] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);

  // getDerivedProps
  if (!isEditing && valStr !== draft) {
    setDraft(valStr);
  }

  return (
    <div className="form-input-number">
      <button type="button" onClick={() => onChange((value || 0) - 1)} />
      <input
        className="form-input"
        type="text"
        autoComplete="off"
        ref={inputRef}
        id={id}
        value={draft}
        onFocus={() => {
          inputRef.current?.select();
          setIsEditing(true);
        }}
        onBlur={() => {
          const newVal = parseInt(draft, 10);
          if (!isNaN(newVal)) {
            onChange(newVal);
          }
          setIsEditing(false);
        }}
        onChange={evt => setDraft(evt.target.value)}
      />
      <button type="button" onClick={() => onChange((value || 0) + 1)} />
    </div>
  );
};

export const FormNumberInput = <T extends FieldValues>({
  label,
  name,
  positive,
  required
}: {
  label: string;
  name: Path<T>;
  positive?: boolean;
  required?: boolean;
}): React.ReactElement => {
  const [id] = useState(() => _uniqueId(name));
  const { clearErrors, control } = useFormContext<T>();

  return (
    <Controller
      control={control}
      name={name}
      rules={{ required }}
      render={({ field: { onChange, value }, fieldState: { error } }) => {
        return (
          <div className="form-group">
            <label
              className={'form-label ' + (required ? 'field-required' : '')}
              htmlFor={id}
            >
              {label}
            </label>
            <div
              className={
                'form-input-wrapper ' + (error ? 'form-input-error' : '')
              }
            >
              <NumberInput
                id={id}
                onChange={value => {
                  clearErrors(name);
                  onChange(positive ? Math.max(value, 0) : value);
                }}
                value={value}
              />
              {error && <div className="form-input-errmsg">该项不能为空</div>}
            </div>
          </div>
        );
      }}
    />
  );
};

export const FormDatetimePicker = <T extends FieldValues>({
  label,
  name,
  placeholder,
  required
}: {
  label: string;
  name: Path<T>;
  placeholder?: string;
  required?: boolean;
}): React.ReactElement => {
  const [id] = useState(() => _uniqueId(name));
  const { clearErrors, control } = useFormContext<T>();

  return (
    <Controller
      control={control}
      name={name}
      rules={{ required }}
      render={({ field: { onChange, value }, fieldState: { error } }) => {
        return (
          <div className="form-group">
            <label
              className={'form-label ' + (required ? 'field-required' : '')}
              htmlFor={id}
            >
              {label}
            </label>
            <div
              className={
                'form-input-wrapper ' + (error ? 'form-input-error' : '')
              }
            >
              <DatePicker
                className="form-input-date"
                id={id}
                locale={locale}
                value={value ? Moment(value) : null}
                onChange={(date, dataStr) => {
                  clearErrors(name);
                  onChange(dataStr);
                }}
                placeholder={placeholder ?? ''}
                showTime
              />
              {error && <div className="form-input-errmsg">该项不能为空</div>}
            </div>
          </div>
        );
      }}
    />
  );
};
