import waitFor from "@insite/client-framework/Common/Utilities/waitFor";
import { createHandlerChainRunner, Handler } from "@insite/client-framework/HandlerCreator";
import {
    ProductRecommendationModel,
    ProductRecommendationWidgetModel,
    sendTracking,
} from "@insite/client-framework/Services/ProductRecommendationsService";
import log from "@insite/client-framework/Store/Components/ProductRecommendations/Handlers/Log";
import {
    enableProductRecommendations,
    peeriusApiVersion,
    peeriusSiteName,
    peeriusTrackingUrl,
} from "@insite/client-framework/Store/Components/ProductRecommendations/ProductRecommendationsSelector";
import isEqual from "lodash/isEqual";

type HandlerType = Handler<{ prepareTrackData: ProductRecommendationModel }>;

let lastTrackedJsonStr = "";

export const ValidateContext: HandlerType = ({ getState }) => {
    if (typeof window === "undefined" || !enableProductRecommendations(getState())) {
        return false;
    }
};

export const SetInitialCallbacksIfNeeded: HandlerType = ({ getState, dispatch }) => {
    if (window.PeeriusCallbacks) {
        return;
    }

    const peeriusCallbacks = {
        track: { site: peeriusSiteName(getState()) },
        smartRecs: (widgets: ProductRecommendationWidgetModel[]) => {
            dispatch(log({ message: "Recommended Products: ", data: widgets }));

            if (!isEqual(getState().components.productRecommendations.widgets, widgets)) {
                dispatch({ type: "Components/ProductRecommendations/SetWidgets", widgets });
            }
        },
        info: (jsonData: any[]) => {},
        apiVersion: peeriusApiVersion(getState()),
    };

    window.PeeriusCallbacks = peeriusCallbacks;
};

export const SetTrack: HandlerType = ({ getState, dispatch, parameter: { prepareTrackData } }) => {
    const track = { ...prepareTrackData, site: peeriusSiteName(getState()) };

    const trackedJsonStr = JSON.stringify(track);
    if (trackedJsonStr === lastTrackedJsonStr) {
        // prevent events duplication
        return false;
    }

    lastTrackedJsonStr = trackedJsonStr;
    dispatch(log({ message: "Prepare Track: ", data: track }));
    window.PeeriusCallbacks.track = track;
};

export const InsertPeeriusScriptIfNeeded: HandlerType = ({ getState, dispatch }) => {
    if (getState().components.productRecommendations.isPeeriusLoaded) {
        return;
    }

    let script = document.querySelector(`script[src="${peeriusTrackingUrl(getState())}"]`) as HTMLScriptElement;
    if (script) {
        return;
    }

    script = document.createElement("script");
    script.setAttribute("type", "text/javascript");
    script.setAttribute("src", peeriusTrackingUrl(getState()));
    script.onload = () => {
        dispatch({ type: "Components/ProductRecommendations/SetIsPeeriusLoaded" });
    };
    document.head.appendChild(script);

    // we should not send a separate tracking request after adding the script,
    // as loading the script itself does the tracking. so we should leave
    return false;
};

export const WaitForPeeriusIsLoaded: HandlerType = async ({ getState }) => {
    await waitFor(() => getState().components.productRecommendations.isPeeriusLoaded);
};

export const SendTracking: HandlerType = () => {
    const json = encodeURIComponent(JSON.stringify(window.PeeriusCallbacks.track));
    sendTracking(json);
};

export const chain = [
    ValidateContext,
    SetInitialCallbacksIfNeeded,
    SetTrack,
    InsertPeeriusScriptIfNeeded,
    WaitForPeeriusIsLoaded,
    SendTracking,
];

const prepareTrack = createHandlerChainRunner(chain, "PrepareTrack");
export default prepareTrack;
