import {
    implicit$FirstArg,
    noSerialize,
    NoSerialize,
    QRL,
    Signal,
    useSignal,
    useTask$,
    useVisibleTask$,
} from "@builder.io/qwik";
import { Atom, WritableAtom } from "nanostores";

function writeable<T>(store: Atom<T> | WritableAtom<T>): store is WritableAtom<T> {
    return typeof (store as WritableAtom<T>).set === 'function';
}

export function useNanostoreQrl<T>(qrl: QRL<WritableAtom<T> | Atom<T>>): Signal<T> {
    const signal = useSignal<T | undefined>(undefined); 
    const storeSignal = useSignal<NoSerialize<WritableAtom<T> | Atom<T>> | undefined>(undefined);

    useTask$(async ({ track }) => {
        let store: WritableAtom<T> | Atom<T> | undefined = storeSignal.value;

        if (!store) {
            const modified = await qrl.resolve();
            storeSignal.value = noSerialize(modified);
            store = modified;
        }

        if (signal.value === undefined && store.value !== undefined) {
            signal.value = store.value;
        }

        const v = track(signal);

        if (writeable(store) && v !== undefined && store.value !== v) {
            store.set(v);
        }
    });

    // eslint-disable-next-line qwik/no-use-visible-task
    useVisibleTask$(async ({ cleanup }) => {
        let store: WritableAtom<T> | Atom<T> | undefined = storeSignal.value;

        if (!store) {
            const modified = await qrl.resolve();
            storeSignal.value = noSerialize(modified);
            store = modified;
        }

        if (store.value !== undefined && signal.value !== store.value) {
            signal.value = store.value;
        }

        const unsub = store.subscribe((value) => {
            if (signal.value !== value) {
                signal.value = value;
            }
        });

        cleanup(unsub);
    });

    return signal as Signal<T>;
}

export const useNanostore$ = implicit$FirstArg(useNanostoreQrl);