import { AutocompleteModel } from "@insite/client-framework/Types/ApiModels";
import parseQueryString from "@insite/client-framework/Common/Utilities/parseQueryString";
import { createHandlerChainRunner, Handler } from "@insite/client-framework/HandlerCreator";
import { getContentByVersionPath } from "@insite/client-framework/Services/ContentService";
import { getSearchRedirect } from "@insite/client-framework/Services/SearchService";
import {
    getSelectedCategoryPath,
    getSettingsCollection,
} from "@insite/client-framework/Store/Context/ContextSelectors";
import { getCurrentPage, getLocation } from "@insite/client-framework/Store/Data/Pages/PageSelectors";
import { useDisplayProducts } from "@insite/client-framework/Store/Pages/ProductList/Handlers/DisplayProducts";
import { Location } from "@insite/client-framework/Components/SpireRouter";
import { addSearchHistory, getSearchHistory } from "@insite/client-framework/Services/AutocompleteService";

type Parameter = {
    query: string;
    autocompleteModel: AutocompleteModel | undefined;
    searchPath: string;
    goToUrl: (url: string, track?: boolean) => void;
    searchHistoryEnabled?: boolean;
    searchHistoryLimit?: number;
};

type Props = {
    abortController?: AbortController;
    path: string;
    isSearchPage?: boolean;
    redirectUrl?: string | null;
    location: Location;
    onError?: (error: Error) => void;
};

type HandlerType = Handler<Parameter, Props>;

export const CheckAutocompleteModel: HandlerType = props => {
    if (props.parameter.autocompleteModel?.products?.length === 1) {
        props.redirectUrl = props.parameter.autocompleteModel.products[0].url;
    }
};

export const GetPath: HandlerType = props => {
    if (props.redirectUrl) {
        return;
    }

    const state = props.getState();
    props.location = getLocation(state);
    const pathnameLowerCase = props.location.pathname.toLowerCase();
    props.path =
        getSelectedCategoryPath(state) ||
        (props.location.pathname === getContentByVersionPath
            ? parseQueryString<{ categoryPath: string }>(props.location.search).categoryPath
            : "") ||
        (pathnameLowerCase.startsWith("/content/") || pathnameLowerCase.startsWith("/.spire/")
            ? ""
            : props.location.pathname);
    props.isSearchPage = pathnameLowerCase.startsWith(`/${props.parameter.searchPath.toLowerCase()}`);
};

export const StartDisplayProducts: HandlerType = props => {
    if (props.redirectUrl) {
        return;
    }

    // We can't start displayProducts here if we're already on PLP for a category
    // this causes the URL to change before we get to the search page
    // which results in an invalid product request being sent and override search results
    if (
        getCurrentPage(props.getState()).type === "ProductListPage" &&
        !props.getState().pages.productList.isSearchPage
    ) {
        return;
    }

    const [abortController, displayProducts] = useDisplayProducts();

    props.abortController = abortController;

    props.dispatch(
        displayProducts({
            path: props.path,
            query: props.parameter.query,
            isSearch: true,
            relevancy: true,
        }),
    );
};

export const CheckSearchHistory: HandlerType = props => {
    if (props.redirectUrl || !props.parameter.searchHistoryEnabled) {
        return;
    }

    const { query } = props.parameter;
    const searchHistory = getSearchHistory();
    const redirectUri = searchHistory.find(item => item.query === query)?.redirectUri;

    if (redirectUri && redirectUri !== props.location.pathname) {
        props.redirectUrl = redirectUri;
    }
};

export const CheckIfRedirectIsNeeded: HandlerType = async props => {
    if (props.redirectUrl) {
        return;
    }

    const { searchProvider } = getSettingsCollection(props.getState()).searchSettings;
    if (searchProvider === "Commerce Search v3") {
        return;
    }

    const getSearchRedirectResult = await getSearchRedirect({ term: props.parameter.query });
    if (getSearchRedirectResult.uri) {
        props.redirectUrl = getSearchRedirectResult.uri;
    }
};

export const AbortDisplayProductsIfNeeded: HandlerType = props => {
    if (props.redirectUrl && props.abortController) {
        props.abortController.abort("Outdated");

        props.dispatch({
            type: "Pages/ProductList/CancelLoadProducts",
        });
    }
};

export const GoToUrl: HandlerType = ({ redirectUrl, isSearchPage, parameter: { goToUrl, searchPath, query } }) => {
    if (redirectUrl) {
        goToUrl(redirectUrl);
    } else {
        const encodedQuery = encodeURIComponent(query);
        goToUrl(`/${searchPath}?query=${encodedQuery}`, false);

        if (isSearchPage) {
            // if we already on the Search page then scroll to top
            window.scrollTo(0, 0);
        }
    }
};

export const AddSearchHistory: HandlerType = ({
    redirectUrl,
    parameter: { query, searchHistoryEnabled, searchHistoryLimit },
}) => {
    if (!searchHistoryEnabled) {
        return;
    }

    addSearchHistory(query, searchHistoryLimit, false, redirectUrl);
};

export const chain = [
    CheckAutocompleteModel,
    GetPath,
    StartDisplayProducts,
    CheckSearchHistory,
    CheckIfRedirectIsNeeded,
    AbortDisplayProductsIfNeeded,
    GoToUrl,
    AddSearchHistory,
];

export const startSearch = createHandlerChainRunner(chain, "StartSearch");
