import { useMemo, useRef, useState } from 'react';
import { ErrorService } from '../../components/services/ErrorService';
import { useForm } from '../useForm';
import { useFormWatchFields } from '../useFormWatchFields';
import { CHANGE_STEP_REASON } from './constants';
import { compareSteps, getWatchFields } from './utils';
/**
 * Хук useFormWizard предназначен для управления многошаговыми формами.
 *
 * @example
 * const allSteps = [
 *   { id: 1, label: 'Step 1', component: StepOneComponent },
 *   { id: 2, label: 'Step 2', component: StepTwoComponent }
 * ];
 *
 * const handleSubmit = (formValues) => {}
 *
 * const formWizardProps = useFormWizard({ steps: allSteps, onSubmit: handleSubmit});
 *
 * <FormWizard {...formWizardProps} />
 */
export const useFormWizard = ({ initialStepId, defaultValues, isDisabled, steps, autoSaveStrategy, onChangeStep, onAutoSave, onSubmit, onAutoSaveError, }) => {
    const calculateInitialVisibleSteps = () => steps.filter(({ shouldSkip }) => shouldSkip?.calculate
        ? !shouldSkip.calculate(defaultValues)
        : true);
    const initialVisibleSteps = useMemo(() => calculateInitialVisibleSteps(), []);
    // Ref предназначен для хранения предыдущего состояния шагов
    // Позволяет оптимизировать повторные вызовы setVisibleSteps
    const prevVisibleStepsRef = useRef(initialVisibleSteps);
    const [visibleSteps, setVisibleSteps] = useState(initialVisibleSteps);
    const [currentStepId, setCurrentStepId] = useState(initialStepId || steps[0].id);
    const validationSchemaForStep = steps.find((step) => Object.is(step.id, currentStepId))?.validationSchema;
    const form = useForm({
        defaultValues,
        disabled: isDisabled,
        validationSchema: validationSchemaForStep,
    });
    const indexStep = useMemo(() => currentStepId
        ? visibleSteps.findIndex((step) => Object.is(step.id, currentStepId))
        : 0, [currentStepId, visibleSteps]);
    const calculatedSteps = steps.filter((step) => Boolean(step.shouldSkip));
    const watchedFields = useMemo(() => getWatchFields(calculatedSteps), [calculatedSteps]);
    const calculateVisibleSteps = () => {
        const currentVisibleSteps = steps.filter(({ shouldSkip }) => shouldSkip?.calculate ? !shouldSkip.calculate(form.getValues()) : true);
        if (!compareSteps(currentVisibleSteps, prevVisibleStepsRef.current)) {
            setVisibleSteps(currentVisibleSteps);
        }
        prevVisibleStepsRef.current = currentVisibleSteps;
    };
    useFormWatchFields({
        name: watchedFields,
        control: form.control,
        callback: calculateVisibleSteps,
    });
    const isFirstStep = indexStep === 0;
    const isLastStep = indexStep === visibleSteps.length - 1;
    const autoSave = () => {
        if (onAutoSave && Object.is(autoSaveStrategy, 'changeStep')) {
            return onAutoSave(form.getValues()).catch((error) => {
                onAutoSaveError?.(error);
                return Promise.reject(error);
            });
        }
        return;
    };
    const handleClickStep = (index) => {
        if (index < 0 || index >= visibleSteps.length) {
            return;
        }
        setCurrentStepId(visibleSteps[index].id);
    };
    const handlePrevStep = () => {
        if (isFirstStep) {
            return;
        }
        const prevStepId = visibleSteps[indexStep - 1].id;
        setCurrentStepId(prevStepId);
        onChangeStep?.(prevStepId, { reason: CHANGE_STEP_REASON.PrevStep });
    };
    const handleNextStep = () => {
        if (isLastStep) {
            return;
        }
        const nextStepId = visibleSteps[indexStep + 1].id;
        const autosavePromise = autoSave();
        const changeStepResult = onChangeStep?.(nextStepId, {
            reason: CHANGE_STEP_REASON.NextStep,
            autosavePromise,
        });
        // Если changeStepResult является promise, то необходимо заблокировать переход шага, пока не завершится promise
        if (changeStepResult?.then) {
            return changeStepResult
                .then(() => {
                setCurrentStepId(nextStepId);
            })
                .catch((error) => {
                ErrorService.getInstance().captureException(error, {
                    place: 'useFormWizard',
                    currentStepId,
                    nextStepId,
                });
            });
        }
        setCurrentStepId(nextStepId);
    };
    const handleSubmit = (formValues) => {
        if (isLastStep) {
            return onSubmit(formValues);
        }
        return handleNextStep();
    };
    const handleSubmitError = (errors) => {
        const currentStep = steps.find((step) => Object.is(step.id, currentStepId));
        if (currentStep?.onSubmitError) {
            currentStep.onSubmitError(errors);
        }
    };
    const stepsToRender = useMemo(() => visibleSteps.map(({ validationSchema, ...toRenderProps }) => toRenderProps), [visibleSteps]);
    return {
        form,
        currentStepId,
        isFirstStep,
        isLastStep,
        steps: stepsToRender,
        setCurrentStepId,
        onClickStep: handleClickStep,
        onPrevStep: handlePrevStep,
        onSubmit: handleSubmit,
        onSubmitError: handleSubmitError,
    };
};
