import React, { useImperativeHandle, useRef, useState } from "react";

import TextField from "./TextField";
import NumberField from "./NumberField";
import DisplayField from "./DisplayField";
import SelectField from "./SelectField";
import RadioInput from "./RadioInput";
import SwitchField from "./SwitchField";
import DateTimeField from "./DateTimeField";
import { validationService, getValidators } from "./constants";

import "../../assets/modules/_form-field.scss";
import DateField from "./DateField";

const formFields = {
    text: TextField,
    number: NumberField,
    display: DisplayField,
    select: SelectField,
    radio: RadioInput,
    switch: SwitchField,
    date: DateField,
    "date-time": DateTimeField
};

const FormField = React.forwardRef(
    (
        {
            controlled = false,
            type = "text",
            id = `uniqId-${+new Date()}`,
            value = "",
            onChange,
            disabled,
            required,
            validationProfiles, // registered common validation methods
            validator, // custom validation method
            inputValidations, // validate user input before setting it in state
            liveValidations, // validate on every input update
            ...restProps
        },
        ref
    ) => {
        // options that are to be passed ahead
        let fieldOpts = {
            type,
            disabled,
            ...restProps
        };

        const inputRef = useRef();
        const [_value, setValue] = useState("");
        const [errorMsg, setErrorMsg] = useState("");

        const focus = () => inputRef.current?.focus();

        const getVal = () => (controlled ? value : _value);

        const setVal = val => setValue(val);

        const setError = msg => setErrorMsg(msg);

        const clearError = () => setErrorMsg("");

        const val = val => {
            if (val === undefined) return getVal();
            return setVal(val);
        };

        const isValid = () => {
            if ((required === false && getVal() === "") || disabled) {
                clearError();
                return true;
            }
            let validators = [
                ...(validator ? [validator] : []),
                ...getValidators(validationProfiles ? ["default", ...validationProfiles] : ["default"])
            ];
            let validationResults = validationService.validate(validators, {
                value: getVal(),
                config: fieldOpts
            });
            let errors = validationResults.filter(v => !v.valid);
            if (errors.length) setError(errors[0].errorMsg);
            return errorMsg ? false : errors.length === 0; /* In case error is set explicitly */
        };

        const isInputValid = value => {
            let validators = getValidators(inputValidations);
            let validationResults = validationService.validate(validators, {
                value,
                config: fieldOpts
            });
            return validationResults.every(v => v.valid === true);
        };

        const liveValidate = value => {
            let liveValidators = getValidators(liveValidations || ["default"]);
            let validationResults = validationService.validate(liveValidators, {
                value,
                config: fieldOpts
            });
            let errors = validationResults.filter(v => v.valid === false);
            if (errors.length) setError(errors[0].errorMsg);
        };

        const onChangeInput = async (newVal, notFromUser) => {
            if (inputValidations && !isInputValid(newVal)) return;

            clearError();
            if (controlled) {
                if (liveValidations) liveValidate(newVal);
                if (onChange) onChange(newVal);
                return;
            }
            setVal(newVal);
            if (liveValidations) liveValidate(newVal);
            if (onChange && !notFromUser) onChange(newVal);
        };

        useImperativeHandle(ref, () => ({
            focus,
            isValid,
            val,
            setError,
            clearError
        }));

        let Field = formFields[type];
        if (!Field) {
            Field = formFields["text"];
            console.error(`FormField type : ${type} not found, falling back to 'text'`);
        }
        return (
            <Field
                ref={inputRef}
                value={controlled ? value : _value}
                onChangeInput={onChangeInput}
                errorMsg={errorMsg} // later accomodate warning and default texts
                {...fieldOpts}
            />
        );
    }
);

export default FormField;
