import { useCallback, useEffect, useMemo, useState, useRef } from "react";
import { BehaviorSubject, ReplaySubject, interval, of } from "rxjs";
import { fromFetch } from "rxjs/fetch";
import { distinctUntilChanged, catchError, map, switchMap, take, tap, shareReplay, } from "rxjs/operators";
export const useObservable = (val, type = "behavior") => {
    const subject = useMemo(() => {
        if (type === "behavior") {
            return new BehaviorSubject(val);
        }
        else {
            return new ReplaySubject(1);
        }
    }, [type]);
    const initial = useRef(true);
    useEffect(() => {
        if (!initial.current) {
            subject.next(val);
        }
        else {
            initial.current = false;
        }
    }, [val, subject]);
    return subject;
};
export const useObsRef = (obs) => {
    const ref = useRef();
    useEffect(() => {
        const sub = obs?.subscribe((val) => {
            ref.current = val;
        });
        return () => {
            sub?.unsubscribe();
        };
    }, [obs]);
    return ref;
};
const getObsVal = (obs) => {
    let val;
    const sub = obs?.subscribe((v) => {
        val = v;
    });
    sub?.unsubscribe();
    return val;
};
export const useObsState = (obs, init) => {
    const [value, setValue] = useState(init ? getObsVal(obs) : undefined);
    useEffect(() => {
        if (!obs) {
            setValue(undefined);
        }
        const sub = obs?.subscribe((val) => {
            setValue(() => val);
        });
        return () => {
            sub?.unsubscribe();
        };
    }, [obs]);
    return value;
};
export const useSubject = (initial, type) => {
    // @ts-ignore
    const ref = useRef(initial);
    const subject = useMemo(() => 
    // @ts-expect-error
    type === "replay"
        ? new ReplaySubject(1)
        : new BehaviorSubject(ref.current), [type]);
    useEffect(() => {
        if (ref.current && type === "replay") {
            subject.next(ref.current);
        }
    }, [type, subject]);
    return subject;
};
export const useState$ = (initValue) => {
    const state$ = useSubject(initValue);
    const setState = useCallback((value) => {
        state$.next(value);
    }, [state$]);
    return useMemo(() => [state$, setState], [state$, setState]);
};
export const useFetchCreator = (params) => {
    const pars = useRef(params);
    const query$ = useSubject();
    const fetch$ = useMemo(() => query$.pipe(distinctUntilChanged((prev, next) => {
        if (prev === next) {
            return true;
        }
        const pA = Array.isArray(prev) ? prev : [prev, undefined];
        const nA = Array.isArray(next) ? next : [next, undefined];
        return pA.every((val, i) => val && nA[i] ? val === nA[i] : !val && !nA[i]);
    }), tap(() => {
        pars.current.onBefore?.();
    }), switchMap((q) => {
        const [query, body] = Array.isArray(q)
            ? q
            : [q ?? undefined, undefined];
        if (!query) {
            return of(undefined);
        }
        const { validate, onError, retryTimeout, ...rest } = pars.current;
        return fromFetch(query, { body, ...rest }).pipe(switchMap((response) => {
            if (!response.ok) {
                throw new Error(`status ${response.status}`);
            }
            return response.text();
        }), distinctUntilChanged(), map((raw) => {
            const json = JSON.parse(raw);
            if (!validate(json)) {
                throw new Error(`unexpected result, got ${JSON.stringify(json)}`);
            }
            return json;
        }), catchError((err, caught) => !onError?.(err)
            ? of(undefined)
            : interval(retryTimeout).pipe(take(1), switchMap(() => caught))), tap(() => {
            pars.current.onAfter?.();
        }));
    }), shareReplay(1)), [query$]);
    return useCallback((query, body) => {
        if (!body) {
            query$.next(query);
        }
        else {
            query$.next([query, body]);
        }
        return fetch$;
    }, [query$, fetch$]);
};
//# sourceMappingURL=rxjs.js.map