// Supporting files
import { createWebHistory, createRouter } from "vue-router";
import { storeActions } from "@/constants/store-actions";
import { storeMutations } from "../constants/store-mutations";
import { lazyLoadComponent } from "@/router/dynamic-routing/component-loader.js";
import { routingTable } from "@/router/router-constants/routing-table.js";
import { globalEvents, globalEventTypes } from "@/constants/events";
import { queryStrings } from "@/constants/query-strings";
import { getDeviceIdValue } from "@/helpers/heritage-integration/cookie-helper";

// Heritage integration
import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper";
import {
    updateOrCreateFunnelCookie,
    getFunnelCookie,
    updateSessionIdCookie,
} from "@/helpers/heritage-integration/cookie-helper";
import { loadSessionIfPresent, saveSession } from "@/helpers/heritage-integration/order-helper";
import {
    getPageToRouteExistingOrderTo,
    navigateToHeritageFunnel,
} from "@/helpers/heritage-integration/navigation-helper";

import baseMixin from "@/mixins/base-mixin";
import eventBus from "@/helpers/event-bus/event-bus";
import store from "@/store";
import analyticsMixin from "@/mixins/analytics-mixin";
import { experimentTriggers } from "../constants/experiments";
import { applicationConfig } from "../constants/application-config";

import review from "@/layouts/review/review";
import paymentMethod from "@/layouts/payment-method/payment-method";
const routes = [
    {
        path: "/payment-method", // This is a temporary route for testing.
        name: "payment-method",
        component: paymentMethod,
    },
    {
        path: "/",
        name: "root",
        async beforeEnter(to, from, next) {
            // If we have no query string, or we don't have the FmgPage query string.
            try {
                if (analyticsMixin.methods.noSession()) {
                    await analyticsMixin.methods.initSession();
                } else {
                    updateSessionIdCookie();
                }

                if (getFunnelCookie()?.SuppressConceptFunnel) {
                    await navigateToHeritageFunnel({ shouldSaveSession: false });
                    return next(false);
                }

                // If the saved session has timed out, clear the session, execute 404 logic.
                if (getFunnelCookie() !== null && !isSavedSessionStillActive()) {
                    await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
                    await GoToFunnelStartOn404(next);
                }

                // On entering the funnel "fresh", read cookie information, decide what to do next.
                if (from.redirectedFrom === undefined) {
                    // clear the saveSessionPromise - if it exists in the vuex store but a new instance was created
                    //   the saveSessionPromise will no longer point to a valid promise
                    baseMixin.methods.dispatchStoreAction(storeActions.RESET_SAVE_SESSION_PROMISE);

                    // Remove the parameter after quote release
                    const loadSessionResponse = await loadSessionIfPresent(
                        to.query.isInsurance != null
                            ? to.query.isInsurance == "true"
                                ? true
                                : false
                            : null
                    );

                    const pageToRedirectTo = await getPageToRouteExistingOrderTo(
                        to,
                        loadSessionResponse
                    );

                    // If getPageToRouteExistingOrderTo determines that the return user needs to
                    // go back to heritage funnel, send them there and stop our current navigation.
                    if (pageToRedirectTo === "heritage") {
                        await navigateToHeritageFunnel();
                        return next(false);
                    }

                    // Assign our fmgPage so it will load normally like the other pages.
                    to.query.fmgPage = pageToRedirectTo;
                }

                await runExperiments(to.query.fmgPage);

                // Process funnel cookie.
                updateOrCreateFunnelCookie();

                // If we already have our route, go to it.
                if (router.hasRoute(to.query.fmgPage)) {
                    // Since our route is already in scope, we can grab the component from it and call the arePagePrerequisitesValid function.
                    let component = router
                        .getRoutes()
                        .filter((x) => x.name === to.query.fmgPage)[0].components;

                    // If the component hasn't been loaded fully, load it before we check prerequisites.
                    if (component.default.methods === undefined) {
                        component = await component.default();
                    }

                    if (!arePagePrerequisitesValid(component)) {
                        await GoToFunnelStartOn404(next);
                    }

                    return next({ name: to.query.fmgPage, query: to.query, params: to.params });
                }

                // Get route info for the given url. Names will have a 1:1 relationship with names in the Cms.
                const routeData = await GetRouteInfoFromPageName(to.query.fmgPage);

                // Add our dynamic route.
                router.addRoute({
                    path: routeData[0].path, // Always the same path, because we control it with query strings.
                    name: routeData[0].name,
                    component: routeData[0].component,
                });

                // Call the next components arePagePrerequisitesValid method before load.
                // If it returns false, use the 404 logic.
                const nextComponent = await router
                    .getRoutes()
                    .filter((x) => x.name === routeData[0].name)[0]
                    .components.default();

                if (!arePagePrerequisitesValid(nextComponent)) {
                    await GoToFunnelStartOn404(next);
                }

                // Assign current query string parameters, as well as our fmgPage one.
                next({
                    name: routeData[0].name,
                    query: Object.assign(to.query, { fmgPage: routeData[0].name }),
                    params: to.params,
                });
            } catch (error) {
                console.log(error);

                // If we don't have a route, go to our 404 page.
                await GoToFunnelStartOn404(next);
            }
        },
    },
];

const router = createRouter({
    history: createWebHistory("/fmg/"),
    routes,
    //Cause "page" to begin at the top when route chanages.
    scrollBehavior(to, from, savedPosition) {
        // always scroll to top
        return { top: 0 };
    },
});

//---------------------------------------------------------- Router Functions ----------------------------------------------------------

router.beforeEach(async (to, from, next) => {
    // set lastNavigationPage here to capture state before API calls for analytics.
    // use current page url query string name when to.name is "root" (due to unresolved navigation in beforeEach)
    router.lastNavigationPage = to.name == "root" ? analyticsMixin.methods.getPageName() : to.name;

    next();
});

router.afterEach(async (to, from) => {
    // Update lastPageVisited in the store
    store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name);

    // If saving on navigation is requested, check for saved SessionId or EmailAddress to determine if saving is appropriate
    if (eval(to.params.isSavingNavigation)) {
        if (
            store.getters.applicationUser.savedSessionId ||
            store.getters.order.customer?.emailAddress
        ) {
            await saveSession({});
        }
    }

    // Push page view to GA
    analyticsMixin.methods.pushPageViewToGA();

    // Push experiments to Data Layer
    analyticsMixin.methods.pushExperimentsToDataLayer();
});

router.navigateWithoutSaving = (
    scenario,
    currentRoute,
    optionalQuery = {},
    optionalParams = {},
    optionalPageData = {}
) => {
    navigate(scenario, currentRoute, false, optionalQuery, optionalParams, optionalPageData);
};

router.navigateWithSaving = (
    scenario,
    currentRoute,
    optionalQuery = {},
    optionalParams = {},
    optionalPageData = {}
) => {
    navigate(scenario, currentRoute, true, optionalQuery, optionalParams, optionalPageData);
};

router.navigateToExternalUrl = (url, optionalQuery = {}) => {
    navigateToUrl(url, optionalQuery);
};

//Use this navigation when you need to call next() explicitly. beforeRouteEnter is a good example.
router.overrideNavigation = (
    scenario,
    currentRoute,
    next,
    isSavingNavigation,
    optionalQuery = {},
    optionalParams = {},
    optionalPageData
) => {
    navigate(
        scenario,
        currentRoute,
        isSavingNavigation,
        optionalQuery,
        optionalParams,
        optionalPageData
    );
    next();
};

router.getNextPage = () => nextPageName;

// PRIVATE VARIABLES

var nextPageName;

// PRIVATE FUNCTIONS

// Navigate to the next route, depending on the scenario.
async function navigate(
    scenario,
    currentRoute,
    isSavingNavigation,
    optionalQuery = {},
    optionalParams = {},
    optionalPageData = {}
) {
    if (!scenario) {
        console.error("No scenario provided. Please review the routing table.");
        return;
    }

    // Match our maps up and navigate if we have a destination.
    const matchingScenarioMap = getNavigationMap(scenario, currentRoute);
    const destinationFmgPageValue = matchingScenarioMap.destinationFmgPageValue;

    if (destinationFmgPageValue !== undefined) {
        // We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one.

        nextPageName = destinationFmgPageValue;

        // Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object
        const existingPageDataForPage = store.getters.pageData(destinationFmgPageValue);
        baseMixin.methods.savePageDataToStore(
            destinationFmgPageValue,
            Object.keys(optionalPageData).length > 0
                ? optionalPageData
                : existingPageDataForPage ?? {}
        );

        optionalParams.isSavingNavigation = isSavingNavigation;

        // add querystring params to the route. if they are already in the store then no need to add them
        var queryStringsObject = {
            fmgPage: destinationFmgPageValue,
        };

        const queryString = window.location.search;
        const urlParams = new URLSearchParams(queryString);
        const lowerCaseParams = new URLSearchParams();
        for (const [name, value] of urlParams) {
            lowerCaseParams.append(name.toLowerCase(), value);
        }
        const hasZip = lowerCaseParams.has(queryStrings.ZIP_CODE);
        const zip = lowerCaseParams.get(queryStrings.ZIP_CODE);

        if (
            hasZip &&
            (store.getters.order.serviceLocation.zipCode === undefined ||
                store.getters.order.serviceLocation.zipCode == null) &&
            (store.getters.order.vehicle.registration.zipCode === undefined ||
                store.getters.order.vehicle.registration.zipCode == null)
        ) {
            queryStringsObject[queryStrings.ZIP_CODE] = zip;
        }

        router.push({
            name: "root",
            query: Object.assign(optionalQuery, queryStringsObject),
            params: optionalParams,
        });
    } else if (matchingScenarioMap.destinationUrl !== undefined) {
        navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery);
    }
}

// Get navigation map depending on the scenario and the current 'page' you're on.
function getNavigationMap(scenario, currentRoute) {
    const fmgPageValue = currentRoute.query.fmgPage;
    const matchedQueryValue = routingTable(store)
        .filter(
            (item) =>
                item.fmgPageValue === fmgPageValue &&
                item.maps.filter((map) => map.scenario === scenario).length > 0
        )
        .map((m) => m.maps.filter((map) => map.scenario === scenario))[0]
        .filter((x) => x.filter === true || x.filter === undefined);

    return matchedQueryValue[0];
}

//---------------------------------------------------------- Private Functions ----------------------------------------------------------

// Navigate to an external url.
function navigateToUrl(url, optionalQuery = {}) {
    // possibly show some loading screen in the future here.
    var externalUrl = new URL(url);

    for (const queryKey in optionalQuery) {
        externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
    }

    window.location.assign(externalUrl);
}

// Get route information by page name.
// This will reach out to the Cms and there is a 1:1 relationship between page names and route names.
async function GetRouteInfoFromPageName(pageName) {
    const response = await store.dispatch(storeActions.GET_ROUTE_INFO_ACTION, {
        pageName: pageName,
    });
    const jsonFromResponse = JSON.parse(response.data.Result);
    let routeData = [];

    // Add our route data and return our array.
    Object.keys(jsonFromResponse).forEach((key) => {
        routeData.push({
            path: "/",
            name: `${key}`,
            component: lazyLoadComponent(jsonFromResponse[key].LayoutName),
        });
    });

    return routeData;
}

// Go to our start page on a 404.
async function GoToFunnelStartOn404(next) {
    const apiResponse = await store.dispatch(storeActions.GET_HOMEPAGE_NAME);
    const homepageName = apiResponse.data.Result;

    // Put item on the bus
    eventBus.addEventToBus(
        globalEvents.Categories.GLOBAL_ALERT,
        globalEvents.SubCategories.PAGE_NOT_FOUND,
        {
            isDismissible: true,
            messageCopy: "You can get a quote by starting on this page.",
            messageHeadline: "We're sorry, something went wrong.",
            type: globalEventTypes.Danger,
        }
    );

    next({
        path: "/",
        query: { fmgPage: homepageName },
    });
}

// Checks arePagePrerequisitesValid on the component passed in.
function arePagePrerequisitesValid(component) {
    return component.default.methods.arePagePrerequisitesValid();
}

// Run SiteEntry and PageEntry triggers for experiments
async function runExperiments(nextPage) {
    if (!store.getters.applicationUser.triggeredSiteEntry) {
        await baseMixin.methods.dispatchStoreAction(storeActions.RUN_EXPERIMENTS_FOR_TRIGGER, {
            userId: getDeviceIdValue(),
            triggerEvent: experimentTriggers.SITE_ENTRY,
            triggerValue: applicationConfig.SITE_ENTRY_TRIGGER_VALUE,
        });
    }

    await baseMixin.methods.dispatchStoreAction(storeActions.RUN_EXPERIMENTS_FOR_TRIGGER, {
        userId: getDeviceIdValue(),
        triggerEvent: experimentTriggers.PAGE_ENTRY,
        triggerValue: nextPage,
    });
}

export default router;
