import API from "../../Utils/API/API";
import UiAnalytics from "../../Utils/Analytics";
import useTranslateValue from "../../Utils/Translation";
import Alert, { AlertType } from "../Alerts/Alert";
import Button, { ButtonStyleType } from "../Button/Button";
import ButtonTypes from "../Button/ButtonTypes";
import ErrorMessage from "../ErrorMessage/ErrorMessage";
import FormField from "./Fields/FormField";
import FormSummary from "./FormSummary";
import Steps from "./Steps/Steps";
import FormFieldSchemaType from "./Types/FormFieldSchemaType";
import Validation from "./Validation";
import OneUptimeDate from "../../../Types/Date";
import HashedString from "../../../Types/HashedString";
import ObjectID from "../../../Types/ObjectID";
import Typeof from "../../../Types/Typeof";
import React, { Fragment, forwardRef, useEffect, useImperativeHandle, useRef, useState, } from "react";
import useAsyncEffect from "use-async-effect";
export const DefaultValidateFunction = (_values) => {
    return {};
};
const BasicForm = forwardRef((props, ref) => {
    var _a, _b, _c, _d;
    const { translateString } = useTranslateValue();
    const isSubmitting = useRef(false);
    const [didSomethingChange, setDidSomethingChange] = useState(false);
    const [isLoading, setIsLoading] = useState(props.isLoading);
    const [formError, setFormError] = useState(null);
    const [isDropdownOptionsLoading, setIsDropdownOptionsLoading] = useState(false);
    useEffect(() => {
        setIsLoading(props.isLoading);
    }, [props.isLoading]);
    const getFormSteps = () => {
        if (props.summary && props.summary.enabled) {
            // add to last step
            return [
                ...(props.steps || [
                    {
                        id: props.summary.defaultStepName || "basic",
                        title: props.summary.defaultStepName || "Basic",
                        isSummaryStep: false,
                    },
                ]),
                {
                    id: "summary",
                    title: "Summary",
                    isSummaryStep: true,
                },
            ];
        }
        return props.steps;
    };
    const [formSteps, setFormSteps] = useState(getFormSteps());
    const isInitialValuesSet = useRef(false);
    const refCurrentValue = useRef(props.initialValues || {});
    const [currentFormStepId, setCurrentFormStepId] = useState(null);
    const isOnLastFormStep = !currentFormStepId ||
        !formSteps ||
        formSteps.length === 0 ||
        formSteps[formSteps.length - 1]
            .id === currentFormStepId;
    const submitButtonTextRaw = isOnLastFormStep
        ? props.submitButtonText || "Submit"
        : "Next";
    const submitButtonText = (_a = translateString(submitButtonTextRaw)) !== null && _a !== void 0 ? _a : submitButtonTextRaw;
    useEffect(() => {
        if (props.values) {
            refCurrentValue.current = props.values || {};
        }
    }, [props.values]);
    useEffect(() => {
        if (formSteps && formSteps.length > 0 && formSteps[0]) {
            setCurrentFormStepId(formSteps[0].id);
        }
    }, []);
    useEffect(() => {
        if (props.onIsLastFormStep) {
            props.onIsLastFormStep(isOnLastFormStep);
        }
        if (props.onFormStepChange && currentFormStepId) {
            props.onFormStepChange(currentFormStepId);
        }
    }, [currentFormStepId, formSteps, isOnLastFormStep]);
    const [currentValue, setCurrentValue] = useState(props.initialValues || {});
    const [errors, setErrors] = useState({});
    const [touched, setTouched] = useState({});
    useEffect(() => {
        var _a;
        setFormSteps((_a = getFormSteps()) === null || _a === void 0 ? void 0 : _a.filter((step) => {
            if (!step.showIf) {
                return true;
            }
            return step.showIf(refCurrentValue.current);
        }));
    }, [refCurrentValue.current]);
    const [formFields, setFormFields] = useState([]);
    const setFieldTouched = (fieldName, value) => {
        setTouched(Object.assign(Object.assign({}, touched), { [fieldName]: value }));
    };
    const validate = (values) => {
        const totalValidationErrors = Validation.validate({
            values,
            formFields,
            currentFormStepId,
            onValidate: props.onValidate || undefined,
        });
        if (props.onFormValidationErrorChanged) {
            props.onFormValidationErrorChanged(Object.keys(totalValidationErrors).length !== 0);
        }
        setErrors(totalValidationErrors);
        return totalValidationErrors;
    };
    useEffect(() => {
        setDidSomethingChange(true);
        validate(currentValue);
    }, [currentValue]);
    useImperativeHandle(ref, () => {
        return {
            setFieldTouched,
            setFieldValue,
            submitForm,
        };
    }, [currentValue, errors, touched, formFields]);
    useAsyncEffect(async () => {
        var _a;
        const fields = [
            ...props.fields.map((field) => {
                return Object.assign({ name: getFieldName(field) }, field);
            }),
        ];
        for (const item of fields) {
            // if this field is not the current step.
            let shouldSkip = false;
            if (currentFormStepId &&
                item.stepId &&
                item.stepId !== currentFormStepId) {
                shouldSkip = true;
            }
            if (((_a = props.summary) === null || _a === void 0 ? void 0 : _a.enabled) &&
                (!props.steps || props.steps.length === 0)) {
                // if summary is enabled and no steps are provided, then all fields belong to the same step and should not be skipped.
                shouldSkip = false;
                item.stepId = props.summary.defaultStepName || "basic";
            }
            if (shouldSkip) {
                continue;
            }
            if (item.fetchDropdownOptions) {
                setIsDropdownOptionsLoading(true);
                // If a dropdown has fetch optiosn then we need to fetch them
                try {
                    const options = await item.fetchDropdownOptions(refCurrentValue.current);
                    item.dropdownOptions = options;
                }
                catch (err) {
                    setFormError(API.getFriendlyMessage(err));
                }
            }
        }
        setIsDropdownOptionsLoading(false);
        setFormFields(fields);
    }, [props.fields, currentFormStepId]);
    const getFieldName = (field) => {
        const fieldName = field.overrideFieldKey
            ? field.overrideFieldKey
            : Object.keys(field.field || {})[0];
        return fieldName;
    };
    const setAllTouched = () => {
        const touchedObj = {};
        for (const field of formFields) {
            if (currentFormStepId &&
                field.stepId &&
                field.stepId !== currentFormStepId) {
                continue;
            }
            touchedObj[field.name] = true;
        }
        setTouched(Object.assign(Object.assign({}, touched), touchedObj));
    };
    const setFieldValue = (fieldName, value) => {
        const updatedValue = Object.assign(Object.assign({}, refCurrentValue.current), { [fieldName]: value });
        refCurrentValue.current = updatedValue;
        setCurrentValue(refCurrentValue.current);
        if (props.onChange && isInitialValuesSet.current) {
            props.onChange(refCurrentValue.current, (values) => {
                refCurrentValue.current = values;
                setCurrentValue(refCurrentValue.current);
            });
        }
    };
    const submitForm = () => {
        // check for any boolean values and if they don't exist in values - mark them as false.
        setAllTouched();
        const validationErrors = validate(refCurrentValue.current);
        isSubmitting.current = true;
        if (Object.keys(validationErrors).length > 0) {
            // errors on form, do not submit.
            return;
        }
        // if last step then submit.
        if ((formSteps &&
            formSteps.length > 0 &&
            formSteps[formSteps.length - 1].id === currentFormStepId) ||
            currentFormStepId === null) {
            const values = refCurrentValue.current;
            for (const field of formFields) {
                if (field.fieldType === FormFieldSchemaType.Toggle) {
                    const fieldName = field.name;
                    if (!values[fieldName]) {
                        values[fieldName] = false;
                    }
                }
                if (field.fieldType === FormFieldSchemaType.Email) {
                    const fieldName = field.name;
                    if (values[fieldName]) {
                        values[fieldName] = values[fieldName]
                            .toString()
                            .toLowerCase();
                    }
                }
                if (field.fieldType === FormFieldSchemaType.MultiSelectDropdown) {
                    const fieldName = field.name;
                    if (values[fieldName] &&
                        values[fieldName].length > 0 &&
                        values[fieldName][0]["value"]) {
                        values[fieldName] = values[fieldName].map((item) => {
                            return item.value;
                        });
                    }
                }
                if (field.fieldType === FormFieldSchemaType.Dropdown) {
                    const fieldName = field.name;
                    if (values[fieldName] &&
                        values[fieldName]["value"]) {
                        values[fieldName] = values[fieldName]["value"];
                    }
                }
                if (field.fieldType === FormFieldSchemaType.Password) {
                    const fieldName = field.name;
                    if (values[fieldName] &&
                        typeof values[fieldName] === Typeof.String) {
                        values[fieldName] = new HashedString(values[fieldName], false);
                    }
                }
            }
            UiAnalytics.capture("FORM SUBMIT: " + props.name);
            props.onSubmit(values, () => {
                setDidSomethingChange(false);
            });
        }
        else if (formSteps && formSteps.length > 0) {
            const steps = formSteps;
            const currentStepIndex = steps.findIndex((step) => {
                return step.id === currentFormStepId;
            });
            if (currentStepIndex > -1) {
                setCurrentFormStepId(steps[currentStepIndex + 1].id);
            }
        }
    };
    useEffect(() => {
        var _a, _b;
        if (isSubmitting.current) {
            return;
        }
        if (isInitialValuesSet.current) {
            return;
        }
        const values = Object.assign({}, props.initialValues);
        for (const field of formFields) {
            const fieldName = field.name;
            if (field.fieldType === FormFieldSchemaType.Date &&
                values[fieldName]) {
                values[fieldName] = OneUptimeDate.asDateForDatabaseQuery(values[fieldName]);
            }
            if (field.fieldType === FormFieldSchemaType.Dropdown &&
                values[fieldName]) {
                const flatDropdownOptions = ((_a = field.dropdownOptions) === null || _a === void 0 ? void 0 : _a.flatMap((item) => {
                    if ("options" in item &&
                        Array.isArray(item.options)) {
                        return item.options;
                    }
                    return [item];
                })) || [];
                const dropdownOption = flatDropdownOptions.find((option) => {
                    let valueToCompare = values[fieldName];
                    if (valueToCompare instanceof ObjectID) {
                        valueToCompare = valueToCompare.toString();
                    }
                    return option.value === valueToCompare;
                });
                values[fieldName] = (dropdownOption === null || dropdownOption === void 0 ? void 0 : dropdownOption.value) || null;
            }
            if (field.fieldType === FormFieldSchemaType.MultiSelectDropdown &&
                values[fieldName]) {
                const flatDropdownOptions = ((_b = field.dropdownOptions) === null || _b === void 0 ? void 0 : _b.flatMap((item) => {
                    if ("options" in item &&
                        Array.isArray(item.options)) {
                        return item.options;
                    }
                    return [item];
                })) || [];
                const dropdownOptions = flatDropdownOptions.filter((option) => {
                    let valueToCompare = [
                        ...values[fieldName],
                    ];
                    valueToCompare = valueToCompare.map((item) => {
                        if (item instanceof ObjectID) {
                            return item.toString();
                        }
                        return item;
                    });
                    return valueToCompare.includes(option.value);
                });
                values[fieldName] = dropdownOptions.map((option) => {
                    return option.value;
                });
            }
            // if the field is still null but has a default value then... have the default initial value
            if (field.defaultValue && values[fieldName] === undefined) {
                values[fieldName] = field.defaultValue;
            }
            if (field.getDefaultValue && values[fieldName] === undefined) {
                values[fieldName] = field.getDefaultValue(values);
            }
            isInitialValuesSet.current = true;
        }
        refCurrentValue.current = values;
        setCurrentValue(refCurrentValue.current);
    }, [props.initialValues, formFields]);
    const primaryButtonStyle = {};
    if (props.maxPrimaryButtonWidth) {
        primaryButtonStyle.marginLeft = "0px";
        primaryButtonStyle.width = "100%";
    }
    if (formError) {
        return React.createElement(ErrorMessage, { message: formError });
    }
    let showSubmitButton = !props.hideSubmitButton;
    if (props.showSubmitButtonOnlyIfSomethingChanged && didSomethingChange) {
        showSubmitButton = true;
    }
    return (React.createElement("div", { className: "row", id: props.id },
        React.createElement("div", { className: "col-lg-1" },
            React.createElement("div", null,
                props.title && (React.createElement("h1", { className: "text-lg text-gray-700 mt-5" }, (_b = translateString(props.title)) !== null && _b !== void 0 ? _b : props.title)),
                Boolean(props.description) && (React.createElement("div", { className: "text-sm text-gray-500 mb-5" }, typeof props.description === "string"
                    ? (_c = translateString(props.description)) !== null && _c !== void 0 ? _c : props.description
                    : props.description)),
                React.createElement("div", { className: "flex" },
                    formSteps && currentFormStepId && (React.createElement("div", { style: { flex: "0 1 auto" }, className: "mr-10 hidden lg:block" },
                        React.createElement(Steps, { currentFormStepId: currentFormStepId, steps: formSteps, formValues: refCurrentValue.current, onClick: (step) => {
                                setCurrentFormStepId(step.id);
                            } }))),
                    React.createElement("div", { className: `${formSteps && currentFormStepId ? "w-auto pt-6" : "w-full pt-1"}`, style: { flex: "1 1 auto" } },
                        props.error && (React.createElement("div", { className: "mb-3" },
                            React.createElement(Alert, { title: props.error, type: AlertType.DANGER }))),
                        React.createElement("div", null, (() => {
                            const currentStep = formSteps === null || formSteps === void 0 ? void 0 : formSteps.find((step) => {
                                return step.id === currentFormStepId;
                            });
                            const activeColumns = (currentStep === null || currentStep === void 0 ? void 0 : currentStep.columns) || props.showAsColumns || 1;
                            const fullRowSpan = `md:col-span-${activeColumns}`;
                            return (React.createElement("div", { className: `grid md:grid-cols-${activeColumns} grid-cols-1 gap-x-4 gap-y-3` },
                                formFields &&
                                    formFields
                                        .filter((field) => {
                                        if (currentFormStepId) {
                                            return field.stepId === currentFormStepId;
                                        }
                                        return true;
                                    })
                                        .filter((field) => {
                                        const currentValues = refCurrentValue.current;
                                        if (field.showIf &&
                                            !field.showIf(currentValues)) {
                                            return false;
                                        }
                                        return true;
                                    })
                                        .map((field, i) => {
                                        var _a, _b;
                                        const fieldName = getFieldName(field);
                                        return (React.createElement(Fragment, { key: fieldName },
                                            field.sectionTitle && (React.createElement("div", { className: `${fullRowSpan} mt-4 pt-5 first:mt-0 first:pt-0 border-t first:border-t-0 border-gray-200` },
                                                React.createElement("h3", { className: "text-base font-semibold text-gray-900" }, (_a = translateString(field.sectionTitle)) !== null && _a !== void 0 ? _a : field.sectionTitle),
                                                field.sectionDescription && (React.createElement("p", { className: "mt-1 text-sm text-gray-500" }, typeof field.sectionDescription ===
                                                    "string"
                                                    ? (_b = translateString(field.sectionDescription)) !== null && _b !== void 0 ? _b : field.sectionDescription
                                                    : field.sectionDescription)))),
                                            React.createElement("div", { className: field.spanFullRow
                                                    ? fullRowSpan
                                                    : undefined },
                                                React.createElement(FormField, { field: field, fieldName: fieldName, index: i, error: errors[fieldName] || "", touched: touched[fieldName] || false, isDisabled: isLoading ||
                                                        isDropdownOptionsLoading ||
                                                        false, currentValues: refCurrentValue.current, setFieldValue: setFieldValue, setFieldTouched: setFieldTouched, submitForm: submitForm, disableAutofocus: props.disableAutofocus || false, setFormValues: (values) => {
                                                        refCurrentValue.current = values;
                                                        setCurrentValue(refCurrentValue.current);
                                                    } }),
                                                field.footerElement,
                                                field.getFooterElement &&
                                                    field.getFooterElement(refCurrentValue.current))));
                                    }),
                                currentFormStepId === "summary" && (React.createElement(FormSummary, { formValues: refCurrentValue.current, formFields: formFields, formSteps: formSteps || undefined }))));
                        })()),
                        React.createElement("div", { className: "flex w-full justify-end" },
                            showSubmitButton && (React.createElement("div", { className: "mt-3", style: {
                                    width: props.maxPrimaryButtonWidth ? "100%" : " auto",
                                } },
                                React.createElement(Button, { title: submitButtonText, dataTestId: props.submitButtonText, onClick: () => {
                                        submitForm();
                                    }, id: `${props.id}-submit-button`, isLoading: isLoading || isDropdownOptionsLoading || false, buttonStyle: props.submitButtonStyleType || ButtonStyleType.PRIMARY, style: primaryButtonStyle }))),
                            props.onCancel && (React.createElement("div", null,
                                React.createElement(Button, { title: (_d = translateString(props.cancelButtonText || "Cancel")) !== null && _d !== void 0 ? _d : (props.cancelButtonText || "Cancel"), type: ButtonTypes.Button, id: `${props.id}-cancel-button`, disabled: isLoading || isDropdownOptionsLoading || false, buttonStyle: ButtonStyleType.NORMAL, onClick: () => {
                                        var _a;
                                        (_a = props.onCancel) === null || _a === void 0 ? void 0 : _a.call(props);
                                    } })))))),
                props.footer))));
});
BasicForm.displayName = "BasicForm";
export default BasicForm;
//# sourceMappingURL=BasicForm.js.map