import { useEffect, useReducer, useState, type Dispatch, type Reducer, type StateUpdater } from "preact/hooks";
import { loadObject, saveObject } from "@common/storage";
import { storedStateKey, storedReducerKey } from "@common/hooks/useStored";

export const remoteStateKey = (key: string) => `useRemoteState.${GAME}.${key}`;
export const remoteReducerKey = (key: string) => `useRemoteReducer.${GAME}.${key}`;

export const useRemoteState = <T>(key: string, initialValue: T) => {
    const storedKey = remoteStateKey(key);

    const [value, setValue] = useState<T>(initialValue);

    useEffect(() => {
        loadObject<T>(storedKey, initialValue).then(setValue);
    }, [storedKey]);

    useEffect(() => {
        saveObject(storedKey, value);
    }, [storedKey, value]);

    return [value, setValue] as [T, Dispatch<StateUpdater<T>>];
};

const hydrate = Symbol('hydrate');

type HydrateAction<T> = { type: typeof hydrate; payload: T };

const isHydrateAction = <T>(action: unknown): action is HydrateAction<T> =>
    typeof action === 'object' && action !== null && 'type' in action && action.type === hydrate;

const wrapReducer = <T, A>(reducer: Reducer<T, A>): Reducer<T, A | HydrateAction<T>> =>
    (state, action) => {
        if (isHydrateAction<T>(action)) {
            return action.payload;
        }
        return reducer(state, action);
    };

/** Copies a useStoredState value into the useRemoteState slot and syncs it remotely. */
export const importStoredState = <T>(key: string): boolean => {
    const raw = localStorage.getItem(storedStateKey(key));
    if (!raw) return false;
    saveObject(remoteStateKey(key), JSON.parse(raw) as T);
    return true;
};

/** Copies a useStoredReducer value into the useRemoteReducer slot and syncs it remotely. */
export const importStoredReducer = <T>(key: string): boolean => {
    const raw = localStorage.getItem(storedReducerKey(key));
    if (!raw) return false;
    saveObject(remoteReducerKey(key), JSON.parse(raw) as T);
    return true;
};

export const useRemoteReducer = <T, A>(key: string, reducer: Reducer<T, A>, initialValue: T) => {
    const storedKey = remoteReducerKey(key);

    const getInitialValue = (): T => {
        try {
            const storedValue = localStorage.getItem(storedKey);
            return storedValue ? JSON.parse(storedValue) : initialValue;
        } catch {
            return initialValue;
        }
    };

    const [state, dispatch] = useReducer(wrapReducer(reducer), undefined, getInitialValue);

    useEffect(() => {
        loadObject<T>(storedKey, initialValue).then((loaded) => {
            dispatch({ type: hydrate, payload: loaded });
        });
    }, [storedKey]);

    useEffect(() => {
        saveObject(storedKey, state);
    }, [storedKey, state]);

    return [state, dispatch] as [T, Dispatch<A>];
};
