"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/* eslint-disable @typescript-eslint/no-explicit-any */
const react_1 = __importDefault(require("react"));
const router_1 = require("next/router");
const hoist_non_react_statics_1 = __importDefault(require("hoist-non-react-statics"));
const react_i18next_1 = require("react-i18next");
const utils_1 = require("../utils");
const components_1 = require("../components");
exports.appWithTranslation = function (WrappedComponent) {
    const WrappedComponentWithSSR = react_i18next_1.withSSR()(WrappedComponent);
    const { config, i18n } = this;
    const consoleMessage = this.consoleMessage.bind(this);
    const clientLoadNamespaces = (lng, namespaces) => Promise.all(namespaces.filter(ns => !i18n.hasResourceBundle(lng, ns)).map(ns => i18n.reloadResources(lng, ns)));
    class AppWithTranslation extends react_1.default.Component {
        constructor(props) {
            super(props);
            if (!utils_1.isServer()) {
                const changeLanguageCallback = (prevLng, newLng) => {
                    const { router } = props;
                    const { pathname, asPath, query } = router;
                    const routeInfo = { pathname, query };
                    if (i18n.initializedLanguageOnce && typeof newLng === 'string' && prevLng !== newLng) {
                        const { as, href } = utils_1.lngPathCorrector(config, { as: asPath, href: routeInfo }, newLng);
                        router.replace(href, as, { shallow: config.shallowRender });
                    }
                };
                const changeLanguage = i18n.changeLanguage.bind(i18n);
                i18n.changeLanguage = async (newLng, callback = () => null) => {
                    const prevLng = i18n.language;
                    if (typeof newLng === 'string' && i18n.initializedLanguageOnce === true) {
                        const usedNamespaces = Object.entries(i18n.reportNamespaces.usedNamespaces)
                            .filter(x => x[1] === true)
                            .map(x => x[0]);
                        await clientLoadNamespaces(newLng, usedNamespaces);
                    }
                    return changeLanguage(newLng, () => {
                        changeLanguageCallback(prevLng, newLng);
                        callback(null, i18n.t);
                    });
                };
            }
        }
        static async getInitialProps(ctx) {
            let wrappedComponentProps = { pageProps: {} };
            if (WrappedComponent.getInitialProps) {
                wrappedComponentProps = await WrappedComponent.getInitialProps(ctx);
            }
            if (typeof wrappedComponentProps.pageProps === 'undefined') {
                consoleMessage('error', 'If you have a getInitialProps method in your custom _app.js file, you must explicitly return pageProps. For more information, see: https://github.com/zeit/next.js#custom-app');
            }
            /*
                Initiate vars to return
            */
            const req = ctx.ctx.req;
            let initialI18nStore = {};
            let initialLanguage = null;
            let i18nServerInstance = null;
            if (req && !req.i18n) {
                const { router } = ctx;
                const result = router.asPath.match(/^\/(.*?)\//);
                const lng = result ? result[1] : process.env.DEFAULT_LANGUAGE;
                req.i18n = i18n.cloneInstance({ initImmediate: false, lng });
                const res = ctx.ctx.res;
                const setContextLocale = (lng) => {
                    // SEE: i18n-express-middleware
                    req.language = req.locale = req.lng = lng;
                    if (res) {
                        res.locals = res.locals || {};
                        res.locals.language = lng;
                        res.locals.languageDir = i18n.dir(lng);
                    }
                };
                setContextLocale(lng);
                i18n.on('languageChanged', setContextLocale);
            }
            /*
                Step 1: Determine initial language
            */
            if (req && req.i18n) {
                initialLanguage = utils_1.lngFromReq(req);
                /*
                    Perform a lang change in case we're not on the right lang
                */
                await req.i18n.changeLanguage(initialLanguage);
            }
            else if (Array.isArray(i18n.languages) && i18n.languages.length > 0) {
                initialLanguage = i18n.language;
            }
            /*
                Step 2: Determine namespace dependencies
            */
            let namespacesRequired = config.ns;
            if (Array.isArray(wrappedComponentProps.pageProps.namespacesRequired)) {
                ({ namespacesRequired } = wrappedComponentProps.pageProps);
            }
            else {
                consoleMessage('warn', `You have not declared a namespacesRequired array on your page-level component: ${ctx.Component
                    .displayName ||
                    ctx.Component.name ||
                    'Component'}. This will cause all namespaces to be sent down to the client, possibly negatively impacting the performance of your app. For more info, see: https://github.com/isaachinman/next-i18next#4-declaring-namespace-dependencies`);
            }
            /*
                We must always send down the defaultNS, otherwise
                the client will trigger a request for it and issue
                the "Did not expect server HTML to contain a <h1> in <div>"
                error
            */
            if (typeof config.defaultNS === 'string' && !namespacesRequired.includes(config.defaultNS)) {
                namespacesRequired.push(config.defaultNS);
            }
            /*
                Step 3: Perform data fetching, depending on environment
            */
            if (req && req.i18n) {
                /*
                    Detect the languages to load based upon the fallbackLng configuration
                */
                const { fallbackLng } = config;
                const languagesToLoad = utils_1.lngsToLoad(initialLanguage, fallbackLng, config.otherLanguages);
                /*
                    Initialise the store with the languagesToLoad and
                    necessary namespaces needed to render this specific tree
                */
                languagesToLoad.forEach(lng => {
                    initialI18nStore[lng] = {};
                    namespacesRequired.forEach(ns => {
                        initialI18nStore[lng][ns] =
                            (req.i18n.services.resourceStore.data[lng] || {})[ns] || {};
                    });
                });
            }
            else if (Array.isArray(i18n.languages) && i18n.languages.length > 0) {
                /*
                    Load newly-required translations if changing route clientside
                */
                await clientLoadNamespaces(i18n.languages[0], namespacesRequired);
                initialI18nStore = i18n.store.data;
            }
            /*
                Step 4: Overwrite i18n.toJSON method to be able to serialize the instance
            */
            if (req && req.i18n) {
                req.i18n.toJSON = () => null;
                i18nServerInstance = req.i18n;
            }
            /*
                `pageProps` will get serialized automatically by NextJs
            */
            return {
                initialI18nStore,
                initialLanguage,
                i18nServerInstance,
                ...wrappedComponentProps
            };
        }
        render() {
            const { initialLanguage, initialI18nStore, i18nServerInstance } = this.props;
            return (react_1.default.createElement(react_i18next_1.I18nextProvider, { i18n: i18nServerInstance || i18n },
                react_1.default.createElement(components_1.NextStaticProvider, null,
                    react_1.default.createElement(WrappedComponentWithSSR, Object.assign({ initialLanguage: initialLanguage, initialI18nStore: initialI18nStore }, this.props)))));
        }
    }
    return hoist_non_react_statics_1.default(router_1.withRouter(AppWithTranslation), WrappedComponent, { getInitialProps: true });
};
