// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useState, useEffect, useRef } from 'react';
import memoizeOne from 'memoize-one';
import { useAdapter } from '../adapter/CallAdapterProvider';
/**
 * @private
 */
export const useAdaptedSelector = (selector, selectorProps) => {
    return useSelectorWithAdaptation(selector, adaptCompositeState, selectorProps);
};
/**
 * @private
 */
export const useSelectorWithAdaptation = (selector, adaptState, selectorProps) => {
    var _a;
    const adapter = useAdapter();
    // Keeps track of whether the current component is mounted or not. If it has unmounted, make sure we do not modify the
    // state or it will cause React warnings in the console. https://skype.visualstudio.com/SPOOL/_workitems/edit/2453212
    const mounted = useRef(false);
    useEffect(() => {
        mounted.current = true;
        return () => {
            mounted.current = false;
        };
    });
    const callId = (_a = adapter.getState().call) === null || _a === void 0 ? void 0 : _a.id;
    const [props, setProps] = useState(selector(adaptState(adapter.getState()), selectorProps !== null && selectorProps !== void 0 ? selectorProps : {
        callId
    }));
    const propRef = useRef(props);
    propRef.current = props;
    useEffect(() => {
        const onStateChange = (state) => {
            var _a;
            if (!mounted.current) {
                return;
            }
            const newProps = selector(adaptState(state), selectorProps !== null && selectorProps !== void 0 ? selectorProps : {
                callId: (_a = state.call) === null || _a === void 0 ? void 0 : _a.id
            });
            if (propRef.current !== newProps) {
                setProps(newProps);
            }
        };
        adapter.onStateChange(onStateChange);
        return () => {
            adapter.offStateChange(onStateChange);
        };
    }, [adaptState, adapter, selector, selectorProps]);
    return props;
};
const memoizeState = memoizeOne((userId, deviceManager, calls, latestErrors, latestNotifications, displayName, alternateCallerId, environmentInfo) => ({
    userId,
    incomingCalls: {},
    incomingCallsEnded: {},
    callsEnded: {},
    deviceManager,
    callAgent: {
        displayName
    },
    calls,
    latestErrors,
    latestNotifications: latestNotifications !== null && latestNotifications !== void 0 ? latestNotifications : {},
    alternateCallerId,
    environmentInfo
}));
const memoizeCalls = memoizeOne((call) => call ? {
    [call.id]: call
} : {});
const adaptCompositeState = (compositeState) => {
    return memoizeState(compositeState.userId, compositeState.devices, memoizeCalls(compositeState.call), 
    // This is an unsafe type expansion.
    // compositeState.latestErrors can contain properties that are not valid in CallErrors.
    //
    // But there is no way to check for valid property names at runtime:
    // - The set of valid property names is built from types in the @azure/communication-calling.
    //   Thus we don't have a literal array of allowed strings at runtime.
    // - Due to minification / uglification, the property names from the objects at runtime can't be used
    //   to compare against permissible values inferred from the types.
    //
    // This is not a huge problem -- it simply means that our adapted selector will include some extra operations
    // that are unknown to the UI component and data binding libraries. Generic handling of the errors (e.g.,
    // just displaying them in some UI surface) will continue to work for these operations. Handling of
    // specific operations (e.g., acting on errors related to permission issues) will ignore these operations.
    compositeState.latestErrors, compositeState.latestNotifications, compositeState.displayName, compositeState.alternateCallerId, compositeState.environmentInfo);
};
//# sourceMappingURL=useAdaptedSelector.js.map