import sleep from "@insite/client-framework/Common/Sleep";
import { Dictionary } from "@insite/client-framework/Common/Types";
import { setCategory } from "@insite/client-framework/OdpEvents/CategoryPageViewEvent";
import { createEvent, OdpEvent, sendEvent } from "@insite/client-framework/OdpEvents/OdpEvent";
import { sendPageViewEvent } from "@insite/client-framework/OdpEvents/PageViewEvent";
import { CatalogPage, Category, getCategoryById } from "@insite/client-framework/Services/CategoryService";
import { GetProductCollectionApiV2Parameter } from "@insite/client-framework/Services/ProductServiceV2";
import ApplicationState from "@insite/client-framework/Store/ApplicationState";
import { getCategoryState } from "@insite/client-framework/Store/Data/Categories/CategoriesSelectors";
import {
    getMergedAttributeTypeFacets,
    getProductsDataView,
} from "@insite/client-framework/Store/Data/Products/ProductsSelectors";
import { CategoryModel, ProductModel } from "@insite/client-framework/Types/ApiModels";
import PageProps from "@insite/client-framework/Types/PageProps";
import cloneDeep from "lodash/cloneDeep";
import isEqual from "lodash/isEqual";

export const sendSearchNavigationEvent = async (
    apiParameter: GetProductCollectionApiV2Parameter,
    unfilteredApiParameter: GetProductCollectionApiV2Parameter | undefined,
    lastParameter: GetProductCollectionApiV2Parameter | undefined,
    getState: () => ApplicationState,
    customize?: (odpEvent: OdpEvent) => void,
) => {
    let state = getState();
    let odpEvent = createEvent("navigation", state);
    odpEvent.properties["action"] = "search";

    let products = getProductsDataView(state, apiParameter)?.value;
    if (products) {
        odpEvent.properties["search_product_details"] = JSON.stringify(
            products.map(o => ({
                product_id: o.id,
            })),
        );
    }

    odpEvent.properties["search_term"] = apiParameter.search ?? "";

    let productsDataView = getProductsDataView(state, apiParameter);

    let searchFilters = [];

    if (odpEvent.properties["category"]) {
        searchFilters.push(`categories: ANY("${odpEvent.properties["category"]}")`);
    }

    let categoryId = apiParameter.categoryId;
    if (categoryId) {
        let categoryDisplay = "";
        while (categoryId) {
            let category: Readonly<Category> | undefined = getCategoryState(getState(), categoryId)?.value;

            while (!category) {
                await sleep(1000);
                category = getCategoryState(getState(), categoryId)?.value;
                if (!category) {
                    return;
                }
            }

            categoryDisplay = ` > ${category.shortDescription}${categoryDisplay}`;

            if (category.parentId === null) {
                break;
            }

            categoryId = category.parentId;
        }

        if (categoryDisplay.length > 0) {
            searchFilters.push(`categories: ANY("${categoryDisplay.substring(3)}")`);
            odpEvent.properties["category"] = categoryDisplay.substring(3);
        }
    }

    if (apiParameter.brandIds && apiParameter.brandIds.length > 0) {
        let filter = 'attributes.brandId: ANY("';
        filter += apiParameter.brandIds.join('", "');
        filter += '")';
        searchFilters.push(filter);
    }

    if (apiParameter.productLineIds && apiParameter.productLineIds.length > 0) {
        let filter = 'attributes.productLineId: ANY("';
        filter += apiParameter.productLineIds.join('", "');
        filter += '")';
        searchFilters.push(filter);
    }

    if (productsDataView && productsDataView.value) {
        odpEvent.properties["search_attribution_token"] = productsDataView.attributionToken;

        if (apiParameter.attributeValueIds && apiParameter.attributeValueIds.length > 0) {
            let dataByAttributeValueId: Dictionary<{ attributeName: string; value: string }> = {};
            for (let attributeType of getMergedAttributeTypeFacets(state, apiParameter, unfilteredApiParameter) ?? []) {
                for (let attributeValue of attributeType.attributeValueFacets ?? []) {
                    dataByAttributeValueId[attributeValue.attributeValueId] = {
                        attributeName: attributeType.name,
                        value: attributeValue.value,
                    };
                }
            }

            let attributes: Dictionary<string[]> = {};

            for (let attributeValueId of apiParameter.attributeValueIds) {
                let data = dataByAttributeValueId[attributeValueId];
                if (!data) {
                    // this shouldn't happen, but in case it does we don't want to blow up
                    continue;
                }
                if (!attributes[data.attributeName]) {
                    attributes[data.attributeName] = [];
                }

                attributes[data.attributeName].push(data.value);
            }

            for (let attribute in attributes) {
                let filter = `attributes.${attribute}: ANY("`;
                filter += attributes[attribute].join('", "');
                filter += '")';
                searchFilters.push(filter);
            }
        }
    }

    odpEvent.properties["search_filter"] = searchFilters.join(" AND ");

    if (productsDataView?.value && productsDataView.pagination) {
        odpEvent.properties["search_offset"] =
            (productsDataView.pagination.page - 1) * productsDataView.pagination.pageSize;

        let sort = productsDataView.pagination.sortType;
        // prettier-ignore
        let sendSort =
            sort === "2" ? "title"
            : sort === "3" ? "title desc"
            : sort === "4" ? "price,title"
            : sort === "5" ? "price desc,title"
            : sort === "9" ? "brands,title"
            : sort === "10" ? "brands desc,title"
            : "";

        if (sendSort !== "") {
            odpEvent.properties["search_order_by"] = sendSort;
        }
    }

    customize?.(odpEvent);

    sendEvent(odpEvent, state);

    if (lastParameter) {
        if (apiParameter.sort !== lastParameter.sort) {
            let sortEvent = cloneDeep(odpEvent);
            sortEvent.properties["action"] = "sort";
            sendEvent(sortEvent, state);
        }

        if (
            apiParameter.categoryId !== lastParameter.categoryId ||
            !isEqual(apiParameter.productLineIds, lastParameter.productLineIds) ||
            !isEqual(apiParameter.brandIds, lastParameter.brandIds) ||
            !isEqual(apiParameter.attributeValueIds, lastParameter.attributeValueIds)
        ) {
            let filterEvent = cloneDeep(odpEvent);
            filterEvent.properties["action"] = "filter";
            sendEvent(filterEvent, state);
        }
    }
};
