import { useEffect, useState } from 'react';
import { asapScheduler, of } from 'rxjs';
import { observeOn, switchMap } from 'rxjs/operators';
import noop from '../../../utils/noop';
import { useFormContext } from '../context';
import { isModel, isModelRef } from '../models';
import { getFieldSetChildChangeObservable } from './set';
export function useModelValue(model) {
    return useModelObservable(model, getValueObservable);
}
export function useModelValid(model) {
    return useModelObservable(model, getValidObservable);
}
export function useFieldValue(field) {
    return useFieldObservable(field, getValueObservable);
}
export function FieldValue(props) {
    var _a = props, name = _a.name, model = _a.model, children = _a.children;
    var value = useFieldValue(model || name);
    if (children) {
        return children(value);
    }
    return value;
}
export function useFieldValid(field) {
    return useFieldObservable(field, getValidObservable);
}
export function FieldValid(props) {
    var _a = props, name = _a.name, model = _a.model, children = _a.children;
    var value = useFieldValid(model || name);
    if (children) {
        return children(value);
    }
    return value;
}
function getValueObservable(model) {
    return model.value$;
}
function getValidObservable(model) {
    return model.valid$;
}
function useFieldObservable(field, observable) {
    var ctx = useFormContext(typeof field !== 'string');
    var _a = useState(function () {
        if (typeof field === 'string') {
            var m = ctx.parent.get(field);
            return isModel(m) ? m : null;
        }
        else if (isModel(field) ||
            isModelRef(field)) {
            return field;
        }
        else {
            return null;
        }
    }), model = _a[0], setModel = _a[1];
    useEffect(function () {
        if (typeof field !== 'string') {
            setModel(isModel(field) ||
                isModelRef(field)
                ? field
                : null);
            return noop;
        }
        var m = ctx.parent.get(field);
        if (isModel(m)) {
            setModel(m);
        }
        var $ = getFieldSetChildChangeObservable(ctx.parent, field).subscribe(function (name) {
            var candidate = ctx.parent.get(name);
            if (isModel(candidate)) {
                setModel(candidate);
            }
        });
        return function () { return $.unsubscribe(); };
    }, [field, ctx, ctx === null || ctx === void 0 ? void 0 : ctx.parent]);
    return useModelObservable(model, observable);
}
function useModelObservable(model, observable) {
    var _a = useState(function () {
        if (isModel(model)) {
            return observable(model).value;
        }
        else if (isModelRef(model)) {
            var inner = model.getModel();
            return inner ? observable(inner).value : null;
        }
        else {
            return null;
        }
    }), value = _a[0], setValue = _a[1];
    useEffect(function () {
        if (isModelRef(model)) {
            var $_1 = model.model$
                .pipe(observeOn(asapScheduler), switchMap(function (it) {
                if (isModel(it)) {
                    return observable(it);
                }
                return of(null);
            }))
                .subscribe(setValue);
            return function () { return $_1.unsubscribe(); };
        }
        else if (isModel(model)) {
            var $_2 = observable(model).subscribe(setValue);
            return function () { return $_2.unsubscribe(); };
        }
        else {
            return noop;
        }
    }, [model, observable]);
    return value;
}
