import { useMemo } from 'react';
import { FieldModel, FormStrategy, isModelRef, isFieldModel, } from './models';
import { useFormContext } from './context';
import { useDestroyOnUnmount } from './utils';
import { or } from './maybe';
import { createModelNotFoundError, createUnexpectedModelTypeError, } from './error';
import { useObservableEagerState } from 'observable-hooks';
function isValueFactory(candidate) {
    return typeof candidate === 'function';
}
function useModelAndChildProps(ctx, field, defaultValue) {
    var model = useMemo(function () {
        var model;
        if (typeof field === 'string') {
            var _a = ctx !== null && ctx !== void 0 ? ctx : {}, strategy = _a.strategy, parent_1 = _a.parent;
            var m = parent_1.get(field);
            if (strategy === FormStrategy.View) {
                if (!m || !isFieldModel(m)) {
                    var v = or(parent_1.getPatchedValue(field), isValueFactory(defaultValue) ? defaultValue : function () { return defaultValue; });
                    model = new FieldModel(v);
                    parent_1.registerChild(field, model);
                }
                else {
                    model = m;
                }
            }
            else {
                if (!m) {
                    throw createModelNotFoundError(field);
                }
                else if (!isFieldModel(m)) {
                    throw createUnexpectedModelTypeError(field, 'FieldModel', m);
                }
                else {
                    model = m;
                }
            }
        }
        else if (isModelRef(field)) {
            var m = field.getModel();
            if (!m || !isFieldModel(m)) {
                var v = or(field.patchedValue, function () {
                    return or(field.initialValue, isValueFactory(defaultValue) ? defaultValue : function () { return defaultValue; });
                });
                model = new FieldModel(v);
                field.setModel(model);
            }
            else {
                model = m;
            }
        }
        else {
            model = field;
        }
        return model;
    }, [field, ctx === null || ctx === void 0 ? void 0 : ctx.parent, ctx === null || ctx === void 0 ? void 0 : ctx.strategy, ctx === null || ctx === void 0 ? void 0 : ctx.form]);
    return model;
}
export function useField(field, defaultValue, validators) {
    if (validators === void 0) { validators = []; }
    var ctx = useFormContext(typeof field !== 'string');
    var model = useModelAndChildProps(ctx, field, defaultValue);
    useObservableEagerState(model.value$);
    useObservableEagerState(model.error$);
    if ((ctx === null || ctx === void 0 ? void 0 : ctx.strategy) === FormStrategy.View &&
        (typeof field === 'string' || isModelRef(field))) {
        model.validators = validators;
    }
    useDestroyOnUnmount(field, model, ctx === null || ctx === void 0 ? void 0 : ctx.parent);
    return model;
}
