import React, { useRef, useSyncExternalStore } from "react";

const initialState = {
    dataLoading: true,
    categories: [],
    services: [],
    locations: [],
    weekStartsOn: 0,
    toggleLimitedBooking: "Disable",
    limitedBookingMonths: 0,
    currencySymbol: "",
    currencyCode: "USD",
    timeFormat: "hh:mm A",
    slotsTimezone: false,
    gcalForCustomer: false,
    ocalForCustomer: false,
    zoomToggle: false,
    supportLinkText: "Need Help?",
    supportLinkUrl: "",
    showFormSupportLink: false,
    locationId: null,
    locationName: "",
    serviceId: null,
    serviceName: "",
    categorizedServices: [],
    serviceStopRerendering: false,
    serviceByLocaionLoading: true,
    staffId: null,
    staffName: "",
    servicePrice: 0,
    total: 0,
    staffsByServices: [],
    staffStopRerendering: false,
    staffsByServiceLoading: true,
    daysOpen: [],
    specialDate: [],
    holidays: [],
    DateLoading: true,
    slots: [],
    slotsLoading: true,
    timezone: "",
    InfoStopRerendering: false,
    slot: null,
    date: null,
    firstName: "",
    lastName: "",
    email: "",
    phone: "+1",
    note: "",
    appointmentId: null,
    gateway: "on-site",
    customerId: null,
};

let storeState = initialState;
const listeners = new Set();

const shallowEqual = (a, b) => {
    if (Object.is(a, b)) return true;
    if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
        return false;
    }
    const keysA = Object.keys(a);
    const keysB = Object.keys(b);
    if (keysA.length !== keysB.length) return false;
    for (const key of keysA) {
        if (!Object.prototype.hasOwnProperty.call(b, key) || !Object.is(a[key], b[key])) {
            return false;
        }
    }
    return true;
};

const subscribe = (listener) => {
    listeners.add(listener);
    return () => listeners.delete(listener);
};

const notify = () => {
    listeners.forEach((listener) => listener());
};

const setStoreState = (updater) => {
    const nextState = typeof updater === "function" ? updater(storeState) : { ...storeState, ...updater };
    if (shallowEqual(storeState, nextState)) {
        return;
    }
    storeState = nextState;
    notify();
};

const updateInput = (fieldOrObject, value) => {
    if (typeof fieldOrObject === "object" && fieldOrObject !== null) {
        setStoreState((prev) => ({ ...prev, ...fieldOrObject }));
        return;
    }

    setStoreState((prev) => {
        if (Object.is(prev[fieldOrObject], value)) {
            return prev;
        }
        return { ...prev, [fieldOrObject]: value };
    });
};

const resetStore = () => {
    storeState = initialState;
    notify();
};

const pick = (source, fields) => {
    if (!fields || fields.length === 0) return source;
    return fields.reduce((acc, key) => {
        acc[key] = source[key];
        return acc;
    }, {});
};

const useBookingStore = (selector = (s) => s, isEqual = shallowEqual) => {
    const lastSelectionRef = useRef();

    return useSyncExternalStore(
        subscribe,
        () => {
            const nextSelection = selector(storeState);
            if (lastSelectionRef.current && isEqual(lastSelectionRef.current, nextSelection)) {
                return lastSelectionRef.current;
            }
            lastSelectionRef.current = nextSelection;
            return nextSelection;
        },
        () => selector(storeState)
    );
};

const withBooking = (fields = []) => (WrappedComponent) => {
    const SelectorWrapper = (props) => {
        const state = useBookingStore((s) => pick(s, fields));
        return <WrappedComponent {...props} state={state} updateInput={updateInput} />;
    };

    SelectorWrapper.displayName = `WithBooking(${WrappedComponent.displayName || WrappedComponent.name || "Component"})`;
    return SelectorWrapper;
};

export { useBookingStore, withBooking, updateInput, resetStore, initialState };
