import type { ReactNode } from 'react';
import React, {
    createContext,
    useContext,
    useEffect,
    useMemo,
    useReducer,
    useState,
} from 'react';
import { keys } from 'lodash';
import { DeliveryStoreStatus } from 'src/data/globalTypes';
import {
    useDeliveryRestrictionsQuery,
    useDeliveryStoresV2Query,
    useTerritoriesQuery,
} from 'src/data/queries';
import deliveryRestrictionsReducer, {
    ACTIONS,
} from './deliveryRestrictionsReducer';
import {
    applyStoreDeliveryDeliveryRestrictions,
    applyStoreTerritoriesDeliveryRestrictions,
    applyStoreTerritoriesRestrictionGroups,
    applyTerritoryDeliveryRestrictions,
    getGroupedDeliveryRestrictions,
} from './helpers';
import {
    DELIVERY_TYPES,
    DeliveryRestrictionsContextType,
    DeliveryType,
} from './types';
import type { CountryxServiceGroup } from 'src/types';

const DEFAULT_STATE = {
    isChanged: false,
    stores: [],
    territories: [],
    storeTerritories: [],
    storeTerritoryGroups: {},
    storesLoading: true,
    territoriesLoading: true,
    storeTerritoriesLoading: true,
};

const DeliveryRestrictionsContext =
    createContext<DeliveryRestrictionsContextType | null>(null);

interface DeliveryRestrictionsProviderProps {
    productId: number;
    children: ReactNode;
}

export const DeliveryRestrictionsProvider = ({
    productId,
    children,
}: DeliveryRestrictionsProviderProps) => {
    const [state, dispatch] = useReducer(
        deliveryRestrictionsReducer,
        DEFAULT_STATE
    );

    const [loadingState, setLoadingState] = useState({
        stores: true,
        territories: true,
        storeTerritories: true,
    });

    const { data: deliveryStoreData, loading: deliveryStoreLoading } =
        useDeliveryStoresV2Query([
            DeliveryStoreStatus.ACTIVE,
            DeliveryStoreStatus.ONBOARDING,
            DeliveryStoreStatus.SUSPENDED,
        ]);
    const {
        data: deliveryRestrictionsData,
        loading: deliveryRestrictionsLoading,
    } = useDeliveryRestrictionsQuery(productId);
    const { data: territoriesData, loading: deliveryTerritoriesLoading } =
        useTerritoriesQuery();

    const deliveryRestrictions = useMemo(
        () => getGroupedDeliveryRestrictions(deliveryRestrictionsData),
        [deliveryRestrictionsData]
    );

    useEffect(() => {
        if (deliveryRestrictionsLoading || deliveryStoreLoading) {
            setLoadingState(prev => ({ ...prev, stores: true }));
            return;
        }

        dispatch({
            type: ACTIONS.POPULATE,
            payload: {
                deliveryType: DELIVERY_TYPES.STORES,
                deliveryData: applyStoreDeliveryDeliveryRestrictions(
                    deliveryStoreData,
                    deliveryRestrictions.store
                ),
            },
        });

        setLoadingState(prev => ({ ...prev, stores: false }));
    }, [
        deliveryStoreLoading,
        deliveryRestrictionsLoading,
        deliveryRestrictionsData,
    ]);

    useEffect(() => {
        if (deliveryRestrictionsLoading || deliveryTerritoriesLoading) {
            setLoadingState(prev => ({ ...prev, territories: true }));
            return;
        }

        dispatch({
            type: ACTIONS.POPULATE,
            payload: {
                deliveryType: DELIVERY_TYPES.TERRITORIES,
                deliveryData: applyTerritoryDeliveryRestrictions(
                    territoriesData,
                    deliveryRestrictions.territory
                ),
            },
        });

        setLoadingState(prev => ({ ...prev, territories: false }));
    }, [
        deliveryTerritoriesLoading,
        deliveryRestrictionsLoading,
        deliveryRestrictionsData,
    ]);

    useEffect(() => {
        if (
            deliveryRestrictionsLoading ||
            deliveryStoreLoading ||
            deliveryTerritoriesLoading
        ) {
            setLoadingState(prev => ({ ...prev, storeTerritories: true }));
            return;
        }

        const storeTerritories =
            deliveryStoreLoading || deliveryTerritoriesLoading
                ? []
                : applyStoreTerritoriesDeliveryRestrictions(
                      deliveryStoreData,
                      deliveryRestrictions.storeTerritory
                  );

        dispatch({
            type: ACTIONS.POPULATE,
            payload: {
                deliveryType: 'storeTerritories',
                deliveryData: storeTerritories,
                initialStoreTerritoryGroups:
                    applyStoreTerritoriesRestrictionGroups(storeTerritories),
            },
        });

        setLoadingState(prev => ({ ...prev, storeTerritories: false }));
    }, [
        deliveryStoreLoading,
        deliveryRestrictionsLoading,
        deliveryTerritoriesLoading,
        deliveryRestrictionsData,
    ]);

    const setSelected = (deliveryType: DeliveryType, ids: string[]) => {
        dispatch({
            type: ACTIONS.SET_SELECTED,
            payload: { deliveryType, ids },
        });
    };

    const selectAll = (deliveryType: DeliveryType) => {
        dispatch({
            type: ACTIONS.SELECT_ALL,
            payload: { deliveryType },
        });
    };

    const moveSingle = (deliveryType: DeliveryType, id: string) => {
        dispatch({
            type: ACTIONS.MOVE_SINGLE,
            payload: { deliveryType, id },
        });
    };

    const moveSelected = (deliveryType: DeliveryType) => {
        dispatch({
            type: ACTIONS.MOVE_SELECTED,
            payload: { deliveryType },
        });
    };

    const addStoreTerritoryGroup = (
        restrictionGroup: Omit<CountryxServiceGroup, 'id'>
    ) => {
        const ids = keys(state.storeTerritoryGroups);
        const nextId = (Math.max(0, ...ids.map(Number)) + 1).toString();

        dispatch({
            type: ACTIONS.STORE_TERRITORY_ADD,
            payload: { [nextId]: { id: nextId, ...restrictionGroup } },
        });
    };

    const editStoreTerritoryGroup = (
        restrictionGroup: CountryxServiceGroup
    ) => {
        dispatch({
            type: ACTIONS.STORE_TERRITORY_EDIT,
            payload: { restrictionGroup },
        });
    };

    const removeStoreTerritoryGroup = (id: string) => {
        dispatch({
            type: ACTIONS.STORE_TERRITORY_REMOVE,
            payload: { id },
        });
    };

    const restrictProductLevel = (
        ids: string[],
        deliveryType: DeliveryType
    ) => {
        dispatch({
            type: ACTIONS.RESTRICT_PRODUCT_LEVEL,
            payload: { ids, deliveryType },
        });
    };

    return (
        <DeliveryRestrictionsContext.Provider
            value={{
                ...state,
                storesLoading: loadingState.stores,
                territoriesLoading: loadingState.territories,
                storeTerritoriesLoading: loadingState.storeTerritories,
                setSelected,
                selectAll,
                moveSingle,
                moveSelected,
                addStoreTerritoryGroup,
                editStoreTerritoryGroup,
                removeStoreTerritoryGroup,
                restrictProductLevel,
            }}
        >
            {children}
        </DeliveryRestrictionsContext.Provider>
    );
};

export const useDeliveryRestrictionsContext = () => {
    const context = useContext(DeliveryRestrictionsContext);

    if (!context)
        throw new Error(
            'useDeliveryRestrictions can only be used within a DeliveryRestrictionsProvider'
        );

    return context;
};
