import React, { Dispatch, SetStateAction, useEffect } from "react";
import { Form, FormInstance } from "antd";

import { noop } from "./utilities";
import { FormProps, useForm } from "antd/lib/form/Form";

// TODO: Should this also have an `onCancel` handler for when the user pages back?
/**
 * The properties of the multistage form
 *
 * @member {React.ReactElement} controls The controls element of the form, for navigating stages
 * @member {FormSubmitCallback} onFinish An event handler fired on form submission
 * @member {FormSubmitCallback} onFinishFailure An event handler fired on form finish handler failure
 * @member {Dispatch<SetStateAction<FormInstance>>} setForm A setter for setting a form instance
 */
export interface MultistageFormStageProps extends FormProps {
  accumulativeFormState?: any;
  controls?: React.ReactElement;
  initialStageValues?: any;
  stageState?: any;
  onFinish?: (values: any) => any;
  onFinishSuccess?: (values: any) => void;
  onFinishFailure?: (error: Error) => void;
  setForm?: Dispatch<SetStateAction<FormInstance | undefined>>;
  // setStageState?: Dispatch<SetStateAction<any>>;
  setStageForm?: Dispatch<SetStateAction<FormInstance | undefined>>;
}

/**
 * Creates a form stage for a multistage form
 *
 * @param {MultistageFormProps} props The propeties for the form stage
 * @returns {React.ReactElement<MultistageFormStageProps>} The form stage element
 */
export const MultistageFormStage: React.FC<MultistageFormStageProps> = ({
  accumulativeFormState,
  children,
  controls,
  initialStageValues, // Internal property???
  initialValues,
  stageState,
  onFinish = noop,
  onFinishSuccess = noop, // Internal property
  onFinishFailure = noop, // Internal property
  setForm = noop,
  // setStageState = noop, // Internal property
  setStageForm = noop, // Internal property
  ...props
}) => {
  const [form] = useForm();

  useEffect(() => {
    setForm(form);
    setStageForm(form);

    // Override the initialState with any accumulated values. i.e., should the
    //   user have already added values, moved on and then returned to this stage,
    //   the accumulative values should override the initial values
    const state = {
      ...initialValues,
      ...accumulativeFormState,
    };
    // setStageState(state);

    form.setFieldsValue(state);
  }, [form, accumulativeFormState, initialValues, setForm, setStageForm]);

  const onStageFinish = async (values: any) => {
    try {
      // Merge both the form's state for set values, and the stageState set by the user.
      // This should really be one or the other, and I'm in favour of moving towards fully
      // managed state in the future using another form library. For now, this offsets the
      // deficiencies with Antd's forms
      const mergedFormState = { ...values, ...stageState };
      const finalFormState = await onFinish(mergedFormState);

      onFinishSuccess(finalFormState);
    } catch (error) {
      const resolvedError =
        error instanceof Error
          ? error
          : new Error(`MultistageFormStage finish error; payload: ${error}`);

      onFinishFailure(resolvedError);
    }
  };

  const accumulativeInitialValues = React.useMemo(
    () => ({
      ...initialStageValues,
      ...initialValues,
    }),
    [initialStageValues, initialValues]
  );

  return (
    <Form
      form={form}
      initialValues={accumulativeInitialValues}
      onFinish={onStageFinish}
      {...props}
    >
      {children}
      {controls}
    </Form>
  );
};
