import { useEffect, useMemo, useState } from 'react';
import { useFormContext } from './context';
import { FieldSetModel, FormStrategy, isFieldSetModel, isModelRef, } from './models';
import { useDestroyOnUnmount } from './utils';
import { get, isSome, or } from './maybe';
import { createModelNotFoundError, createUnexpectedModelTypeError, } from './error';
import isPlainObject from '../../utils/isPlainObject';
import { getFieldSetChildChangeObservable } from './listeners/set';
import { useObservableEagerState } from 'observable-hooks';
function useFieldSetModel(field, parent, strategy) {
    var model = useMemo(function () {
        var model;
        if (typeof field === 'string') {
            var m = parent.get(field);
            if (strategy === FormStrategy.View) {
                if (!m || !isFieldSetModel(m)) {
                    model = new FieldSetModel({});
                    var v = {};
                    var potential = parent.getPatchedValue(field);
                    if (isSome(potential)) {
                        var inner = get(potential);
                        if (isPlainObject(inner)) {
                            v = inner;
                        }
                    }
                    model.patchedValue = v;
                    parent.registerChild(field, model);
                }
                else {
                    model = m;
                }
            }
            else {
                if (!m) {
                    throw createModelNotFoundError(field);
                }
                else if (!isFieldSetModel(m)) {
                    throw createUnexpectedModelTypeError(field, 'FieldSetModel', m);
                }
                else {
                    model = m;
                }
            }
        }
        else if (isModelRef(field)) {
            var m = field.getModel();
            if (!m || !isFieldSetModel(m)) {
                model = new FieldSetModel({});
                model.patchedValue = or(field.patchedValue, function () {
                    return or(field.initialValue, function () { return ({}); });
                });
                field.setModel(model);
            }
            else {
                model = m;
            }
        }
        else {
            model = field;
        }
        return model;
    }, [field, parent, strategy]);
    return model;
}
export function useFieldSet(field, validators) {
    if (validators === void 0) { validators = []; }
    var _a = useFormContext(), parent = _a.parent, strategy = _a.strategy, form = _a.form;
    var model = useFieldSetModel(field, parent, strategy);
    if (strategy === FormStrategy.View &&
        (typeof field === 'string' || isModelRef(field))) {
        model.validators = validators;
    }
    var childContext = useMemo(function () { return ({
        strategy: strategy,
        form: form,
        parent: model,
    }); }, [strategy, form, model]);
    useObservableEagerState(model.error$);
    useDestroyOnUnmount(field, model, parent);
    return [childContext, model];
}
export function useNamedChildModel(fieldSet, name) {
    var _a = useState(fieldSet.get(name)), child = _a[0], setChild = _a[1];
    useEffect(function () {
        var $ = getFieldSetChildChangeObservable(fieldSet, name).subscribe(function (n) {
            setChild(fieldSet.get(n));
        });
        return function () { return $.unsubscribe(); };
    }, [fieldSet, name]);
    return child;
}
