/* eslint-disable no-use-before-define */
import { createWebHistory, createRouter } from 'vue-router';
import lazyLoadComponent from '@/router/dynamic-routing/component-loader';
import issPageValues from '@/router/router-constants/issPage-values';
import routerTitles from '@/router/router-constants/router-titles';
import routingTable from '@/router/router-constants/routing-table';
import { useMainStore } from '@/store';
import eventBus from '@/helpers/event-bus/event-bus';
import { globalEvents, globalEventTypes } from '@/constants/events';
import baseMixin from '@/mixins/base-mixin';
import { isSavedSessionStillActive } from '@/helpers/session-helper';
import { getDeviceIdValue, getISSCookie, updateOrCreateISSCookie, updateSessionIdCookie } from '@/helpers/cookie-helper';
import { experimentTriggers } from '@/constants/experiments';
import applicationConfig from '@/constants/application-config';
import showIssLoadingModal from '@/helpers/loading-modal-helper';

import analyticsMixin from '@/mixins/analytics-mixin';
import { saveSession } from '@/helpers/order-helper.js';
import routerParams from '@/router/router-constants/router-params';
import canBailoutNavigateBack from '@/helpers/bailout-helper';
import submitType from '@/constants/submit-type';
import navigationScenarios from './router-constants/navigation-scenarios';

const routes = [
    {
        path: '/',
        name: 'root',
        async beforeEnter(to, from, next) {
            try {
                const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage;
                const fromQueryPage = from.query?.issPage;

                if ((issPageToUse === issPageValues.ACCESS_DENIED
                    || (issPageToUse !== issPageValues.ENTRY_PAGE && !useMainStore().issConfig.parentAccountNumber))
                    && process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost'
                ) {
                    return await GoToAccessIsDenied(next);
                }

                // Do not run these for the main entry page - as it is not part of the user flow.
                if (issPageToUse !== issPageValues.ENTRY_PAGE) {
                    if (analyticsMixin.methods.noSession()) {
                        await analyticsMixin.methods.initSession();
                    } else {
                        updateSessionIdCookie();
                    }

                    await runExperiments(issPageToUse); // fmg has this further down
                }

                // Intercept all navigation if a submitted order exists in storage
                if (useMainStore().hasSubmittedOrder()) {
                    if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
                        return await GoToConfirmationPage(next, useMainStore().getSubmittedOrder());
                    }
                }

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

                // Process ISS cookie.
                // Skip if Entry Page or Refreshing Welcome page
                if (issPageToUse !== issPageValues.ENTRY_PAGE
                    && !(issPageToUse === issPageValues.WELCOME_PAGE && fromQueryPage === undefined)) {
                    updateOrCreateISSCookie();
                }

                if (router.hasRoute(issPageToUse)) {
                    // Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function.
                    let component = router.getRoutes().filter((x) => x.name === issPageToUse)[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 GoToStartOn404(next);
                    }

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

                const routeData = await GetRouteInfoFromPageName(issPageToUse);

                if (routeData[0].name.toLowerCase() === 'error') {
                    throw new Error('Page not found!');
                }

                // 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)) {
                    const tempMsgCopy = 'Pre Requisites failed, need to handle.  Default is Welcome Page.';
                    const tempMsgHeadline = `${issPageToUse}: pre-req failed...`;
                    await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline);
                }

                // Assign current query string parameters, as well as our issPage one.
                next({
                    name: routeData[0].name,
                    query: Object.assign(to.query, { issPage: routeData[0].name }),
                    params: to.params
                });
            } catch (error) {
                window.console.warn(error);
                await GoToStartOn404(next);
            }
            return null;
        }
    }
];

const router = createRouter({
    history: createWebHistory('/'),
    routes,
    scrollBehavior() {
        // always scroll to top
        return { top: 0 };
    }
});

router.beforeEach(async (to, from) => {
    const fromQueryPage = from.query?.issPage;
    if (fromQueryPage === undefined) {
        showIssLoadingModal(true);
    }

    const toQueryPage = to.query?.issPage;
    const notToPayInAdvanceReturn = toQueryPage !== issPageValues.PAYMENT_RETURN;
    const isInIframe = window !== window.top || fromQueryPage === issPageValues.PAYMENT_PAGE;

    // isFromPaymentPageToOrderConfirmation workaround for navigating from an iframe but
    // isInIframe evaluates to false for some reason when navigating from payment to confirmation
    const isFromPaymentPageToOrderConfirmation =
        fromQueryPage === issPageValues.PAYMENT_PAGE && toQueryPage === issPageValues.ORDER_CONFIRMATION;

    if ((isInIframe && notToPayInAdvanceReturn) || isFromPaymentPageToOrderConfirmation) {
        // need to set window.top.location.href directly when navigating out of an iframe
        // especially when navigating with browser buttons
        const newUrl = `${window.top.location.origin}${to.href}`;
        window.top.location.href = newUrl;
        return false;
    }

    const store = useMainStore();
    // Prevent navigating backwards if we enter a bailout that we are not allowed to go back on
    if (store.isBailout && from.name === issPageValues.BAILOUT_PAGE && to.name !== 'root' && to.name !== issPageValues.CONTACT_CONFIRMATION
        && !canBailoutNavigateBack()) {
        return false;
    }

    return true;
});

router.afterEach(async (to, from) => {
    /*eslint-disable-line*/
    const store = useMainStore();
    // Update lastPageVisited in the store
    store.updateLastPageVisited(to.name);

    if (from.redirectedFrom === undefined) {
        store.clearSaveSessionPromise();
    }

    const skipSaveSession = !!router.options.history.state[routerParams.SKIP_SAVE_SESSION];
    if (from.name !== undefined && !skipSaveSession && !store.hasSubmittedOrder()) {
        await saveSession({});
    }

    if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
        document.title = routerTitles[to.query.issPage] || 'Safelite Solutions®';

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

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

        // Push values to GA
        analyticsMixin.methods.pushValueToGA();
    }
});

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

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

// 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 useMainStore().getRouteInfo(pageName);

    const jsonFromResponse = JSON.parse(response.data.Result);
    const 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;
}

// 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.navigate = (
    scenario,
    currentRoute,
    optionalQuery = {},
    optionalParams = {},
    optionalPageData = {}
) => {
    navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData);
};

router.navigateWithSpinner = (
    scenario,
    currentRoute
) => {
    navigate(scenario, currentRoute, undefined, undefined, undefined, true);
};

// Navigate to the next route, depending on the scenario.
function navigate(
    scenario,
    currentRoute,
    optionalQuery = {},
    optionalParams = {},
    optionalPageData = {},
    forceSpinner = false
) {
    if (!scenario) {
        window.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);

    if (!matchingScenarioMap) {
        window.console.error('No matching scenario found. Please review the routing table.');
        return;
    }

    if (forceSpinner) {
        showIssLoadingModal(true);
    }

    if (scenario === navigationScenarios.CLICKED_BACK_PREVIOUS) {
        // Update page to the prevous page in the router.
        // If we need to worry about typing this in from outside of the app,
        // we'll need to pre check history.length or if there is a previous page stored in state.
        router.go(-1);
    } else if (matchingScenarioMap.destinationIssPageValue) {
        // Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object
        const existingPageDataForPage = useMainStore().pageData(matchingScenarioMap.destinationIssPageValue);
        baseMixin.methods.savePageDataToStore(
            matchingScenarioMap.destinationIssPageValue,
            Object.keys(optionalPageData).length > 0
                ? optionalPageData
                : existingPageDataForPage ?? {}
        );

        // We're always pushing the same path, just changing query strings.
        // Make sure our optional query strings get combined with our issPage one.
        router.push({
            name: 'root',
            query: Object.assign(optionalQuery, {
                issPage: matchingScenarioMap.destinationIssPageValue
            }),
            state: optionalParams
        });
    } else if (matchingScenarioMap.destinationUrl) {
        navigateToUrl(matchingScenarioMap.destinationUrl, optionalQuery);
    }
}

// Navigate to an external url.
function navigateToUrl(url, optionalQuery = {}) {
    // possibly show some loading screen in the future here.
    const externalUrl = new URL(url);
    // eslint-disable-next-line no-restricted-syntax, guard-for-in
    for (const queryKey in optionalQuery) {
        externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
    }

    window.location.assign(externalUrl);
}

// Get navigation map depending on the scenario and the current 'page' you're on.
function getNavigationMap(scenario, currentRoute) {
    const issPageValue = currentRoute.query.issPage;
    try {
        const matchedQueryValue = routingTable(useMainStore())
            .filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0);

        const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined;

        return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined;
    } catch (e) {
        window.console.error(e);
        return undefined;
    }
}

async function GoToAccessIsDenied(next) {
    const errorPageName = issPageValues.ACCESS_DENIED;
    router.addRoute({
        path: '/',
        name: errorPageName,
        component: lazyLoadComponent(errorPageName)
    });

    next({
        name: errorPageName,
        query: { issPage: errorPageName }
    });
}

async function GoToConfirmationPage(next, order) {
    const nextPageName = getConfirmationPageFromOrder(order);
    router.addRoute({
        path: '/',
        name: nextPageName,
        component: lazyLoadComponent(nextPageName)
    });

    next({
        name: nextPageName,
        query: { issPage: nextPageName }
    });
}

function getConfirmationPageFromOrder(order) {
    switch (order.submitType) {
        case submitType.SAFELITE:
            return issPageValues.ORDER_CONFIRMATION;
        case submitType.TPA:
            return issPageValues.TPA_CONFIRMATION;
        case submitType.BAILOUT:
            return issPageValues.CONTACT_CONFIRMATION;
        default:
            return issPageValues.ORDER_CONFIRMATION;
    }
}

async function GoToStartOn404(next, msgCopy = null, msgHeadline = null) {
    const errorPageName = issPageValues.WELCOME_PAGE;
    router.addRoute({
        path: '/',
        name: errorPageName,
        component: lazyLoadComponent(errorPageName)
    });

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

    next({
        name: errorPageName,
        query: { issPage: errorPageName }
    });
}

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

// Run SiteEntry and PageEntry triggers for experiments
async function runExperiments(nextPage) {
    const store = useMainStore();

    if (!store.applicationUser.triggeredSiteEntry) {
        await store.runExperimentsForTrigger({
            deviceId: getDeviceIdValue(),
            triggerEvent: experimentTriggers.SITE_ENTRY,
            triggerValue: applicationConfig.SITE_ENTRY_TRIGGER_VALUE
        });
    }

    await store.runExperimentsForTrigger({
        deviceId: getDeviceIdValue(),
        triggerEvent: experimentTriggers.PAGE_ENTRY,
        triggerValue: nextPage
    });
}

export default router;
