import { useLayoutEffect, useMemo, useState } from "react";
import { useOneUIConfig } from "../../context/OneUIProvider";
import { FormMode, } from "./FormField/FormField.types";
export function useFormAnswers(defaultAnswers, mode) {
    const [answers, setAnswers] = useState(() => {
        const clone = {
            ...defaultAnswers,
        };
        if (mode === FormMode.READ_ONLY)
            return clone;
        return clone;
    });
    return {
        answers,
        setAnswers,
    };
}
export function useFormState(currentQuestions, { answers, setAnswers }) {
    const formConfig = useOneUIConfig("component.form");
    const { isValid: isQuestionsAnswered } = useMemo(() => {
        return areAllQuestionsAnswered(currentQuestions, answers, formConfig.requiredLabel, formConfig.extensions);
    }, [answers, currentQuestions]);
    const onAnswerAction = (questionType, id, answer) => {
        switch (questionType) {
            case "file":
                const { onFileUpload } = formConfig;
                setAnswers((prev) => {
                    const file = answer;
                    if (file)
                        return {
                            ...prev,
                            [id]: onFileUpload(id, file),
                        };
                    delete prev[id];
                    return { ...prev };
                });
                break;
            default:
                setAnswers((prev) => {
                    if (prev[id] === answer)
                        return prev;
                    return {
                        ...prev,
                        [id]: answer,
                    };
                });
        }
    };
    const isFilesUploaded = useFileUploads(currentQuestions, answers);
    return {
        answers,
        isQuestionsAnswered,
        isFilesUploaded,
        onAnswerAction,
        questions: currentQuestions,
    };
}
export function useForm(currentQuestions, defaultAnswers, mode) {
    const answers = useFormAnswers(defaultAnswers, mode);
    return useFormState(currentQuestions, answers);
}
function useFileUploads(questions, answers) {
    const fileUploadQuestions = useMemo(() => questions.filter((a) => a.type === "file"), [questions]);
    const [fileUploadStatus, setFileUploadStatus] = useState({});
    useLayoutEffect(() => {
        const unsubs = [];
        for (let fileQuestion of fileUploadQuestions) {
            const uploadTask = answers[fileQuestion.id];
            if (!uploadTask)
                setFileUploadStatus((prev) => ({
                    ...prev,
                    [fileQuestion.id]: "running",
                }));
            else if (typeof uploadTask === "boolean") {
                setFileUploadStatus((prev) => ({
                    ...prev,
                    [fileQuestion.id]: "success",
                }));
            }
            else
                unsubs.push(uploadTask.on("state_changed", (r) => {
                    setFileUploadStatus((prev) => ({
                        ...prev,
                        [fileQuestion.id]: r.totalBytes === r.bytesTransferred ? "success" : "running",
                    }));
                }));
        }
        return () => {
            unsubs.forEach((unsub) => unsub());
        };
    }, [answers, fileUploadQuestions]);
    const areAllFileUploaded = useMemo(() => {
        return fileUploadQuestions.reduce((r, question) => {
            const uploadStatus = fileUploadStatus[question.id];
            return r && uploadStatus === "success";
        }, true);
    }, [fileUploadStatus, fileUploadQuestions]);
    return areAllFileUploaded;
}
export function useFieldErrors(currentQuestions, answers, showAllErrors) {
    const { requiredLabel, extensions } = useOneUIConfig("component.form");
    const errorMap = useMemo(() => {
        const ans = (question) => answers[question.id];
        const errorsMap = {};
        for (let question of currentQuestions) {
            const _isValidated = () => {
                return isValidated(ans(question), !!question.optional, question.validator, requiredLabel);
            };
            const updateDefaultError = () => {
                const validation = _isValidated();
                errorsMap[question.id] = validation.error;
            };
            switch (question.type) {
                case "check":
                case "rawcheck":
                    updateDefaultError();
                    if (question.validator)
                        errorsMap[question.id] = question.validator(ans(question));
                    break;
                case "number":
                case "text":
                case "select":
                case "radio":
                case "file":
                    updateDefaultError();
                    const validationResult = _isValidated();
                    if (validationResult.error)
                        errorsMap[question.id] = validationResult.error;
                    break;
                default:
                    const extendedSupport = extensions?.[question.type];
                    if (extendedSupport) {
                        const validationResultExtend = extendedSupport.validator
                            ? extendedSupport.validator(ans(question), question)
                            : _isValidated();
                        if (validationResultExtend.error)
                            errorsMap[question.id] = validationResultExtend.error;
                    }
                    else {
                        updateDefaultError();
                    }
            }
        }
        return errorsMap;
    }, [currentQuestions, answers]);
    const currentErrors = useMemo(() => {
        return Object.entries(errorMap).reduce((r, [k, v]) => ({
            ...r,
            [k]: showAllErrors ? v : v !== requiredLabel ? v : undefined,
        }), {});
    }, [showAllErrors, errorMap]);
    return currentErrors;
}
export function areAllQuestionsAnswered(currentQuestions, answers, requiredLabel, extensions) {
    const isValid = currentQuestions.reduce((answeredAll, question) => {
        const ans = (question) => answers[question.id];
        const isFieldAnswered = (() => {
            switch (question.type) {
                case "accept":
                    const checks = ans(question) || [];
                    return question.accept.reduce((r, { optional }, idx) => r && (optional ? true : checks[idx]), true);
                case "check":
                case "rawcheck":
                    if (question.optional)
                        return true;
                    if (question.validator)
                        return question.validator(ans(question)) === true;
                    const checkmarks = ans(question) || [];
                    return checkmarks.includes(true);
                case "number":
                case "text":
                case "select":
                case "radio":
                case "file":
                    if (!answers[question.id])
                        return !!question.optional;
                    const validationResult = isValidated(ans(question), !!question.optional, question.validator, requiredLabel);
                    return validationResult.isValid && answeredAll;
                default:
                    const extendedSupport = extensions?.[question.type];
                    if (extendedSupport) {
                        const validationResultExtend = extendedSupport.validator
                            ? extendedSupport.validator(ans(question), question)
                            : isValidated(ans(question), !!question.optional, question.validator, requiredLabel);
                        return validationResultExtend.isValid;
                    }
                    else
                        return question.validator
                            ? !!question.validator(ans(question))
                            : !!ans(question);
            }
        })();
        const result = answeredAll && isFieldAnswered;
        return result;
    }, true);
    return { isValid: !!isValid };
}
export const isValidated = (_answer, isOptional, validator, requiredFieldLabel) => {
    const answer = Array.isArray(_answer) ? _answer[0] : _answer;
    if (!answer && !isOptional)
        return {
            isValid: false,
            error: requiredFieldLabel,
        };
    if (!validator)
        if (Array.isArray(_answer)) {
            const [_, isValid, error] = _answer;
            return {
                isValid,
                error,
            };
        }
        else {
            return {
                isValid: true,
                error: "",
            };
        }
    const validationResult = validator(_answer);
    if (typeof validationResult === "string")
        return {
            isValid: false,
            error: validationResult,
        };
    return {
        isValid: validationResult,
        error: "",
    };
};
//# sourceMappingURL=Form.hook.js.map