import localsHelper from '../utils/localsHelper.js';
import { isBooleanFlagEnabledOrMissing } from '../services/featureFlagService.js';
import { getFeatureFlagEvaluationSubject, FEATURE_FLAGS } from '../utils/featureFlagsHelper.js';
import setUpNunjucksFilters from '../setUpNunjucksFilters.js';
import { errorRequestHandler } from '../routes/index.js';
import { getAllMyReports, getAllMyBookmarks } from '../utils/reportStoreHelper.js';
import logger from '../utils/logger.js';
import { getDefinitionsPath } from '../utils/definitionUtils.js';

/**
 * Middleware helper to populate all locals configuration
 * to enable DPR lib to operate correctly within a service.
 *
 * @param {Services} services
 * @param {string} layoutPath
 * @param {Environment} env
 * @param {DprConfig} [config]
 * @return {*}  {RequestHandler}
 */
const setupResources = (services, layoutPath, env, config) => {
    return async (req, res, next) => {
        populateValidationErrors(req, res);
        try {
            // 1. Initialise our feature flags to app.locals
            await setFeatureFlags(res, services.featureFlagService);
            // 2. Initialise definitions, collections and definitions path
            await populateDefinitions(services, req, res, config);
            // 3. Initialise enabled service flags to app.locals
            await initialiseServices(services, res);
            // 4. Populate user reports state with up to date list
            await populateRequestedReports(services, res);
            setUpDprPaths(res);
            setupRequestAwareNunjucks(env, res);
            setUpNunjucksFilters(env);
            return next();
        }
        catch (error) {
            return errorRequestHandler(layoutPath)(error, req, res, next);
        }
    };
};
const setupRequestAwareNunjucks = (env, res) => {
    env.addGlobal('getLocals', () => ({ locals: { ...res.locals, ...res.app.locals } }));
};
/**
 * Sets the feature flags to locals
 *
 * @param {Response} res
 * @param {FeatureFlagService} featureFlagService
 */
const setFeatureFlags = async (res, featureFlagService) => {
    if (res.app.locals['featureFlags'] === undefined) {
        res.app.locals['featureFlags'] = {
            flags: {},
        };
    }
    const subject = getFeatureFlagEvaluationSubject(res);
    const currentFlags = res.app.locals['featureFlags'].flags;
    const newFlags = await featureFlagService.evaluateBooleanFlags(FEATURE_FLAGS, subject);
    const hasChanged = JSON.stringify(currentFlags) !== JSON.stringify(newFlags);
    if (hasChanged) {
        res.app.locals['featureFlags'].flags = newFlags;
        logger.info(`FEATURE FLAGS UPDATED: ${JSON.stringify(newFlags)}`);
    }
};
/**
 * Populates the validation errors
 *
 * @param {Request} req
 * @param {Response} res
 */
const populateValidationErrors = (req, res) => {
    const errors = req.flash(`DPR_ERRORS`);
    if (errors && errors[0]) {
        res.locals['validationErrors'] = JSON.parse(errors[0]);
    }
};
/**
 * Derives the definition path and sets the locals
 *
 * @param {Request} req
 * @param {Response} res
 * @param {DprConfig} [config]
 */
const deriveDefinitionsPath = (req, res, config) => {
    // Check definitions path from config
    const dpdPathFromConfig = config?.dataProductDefinitionsPath;
    res.locals['dpdPathFromConfig'] = !!dpdPathFromConfig;
    // Check definitions path from request
    const dpdPathFromQuery = getDefinitionsPath(req.query) || null;
    const dpdPathFromBody = req.body?.dataProductDefinitionsPath;
    const definitionsPathFromQuery = dpdPathFromQuery || dpdPathFromBody;
    res.locals['dpdPathFromQuery'] = !!definitionsPathFromQuery;
    // Set the definitions path to locals
    // - incoming query overwrites config
    const activeDefinitionsPath = definitionsPathFromQuery || dpdPathFromConfig;
    if (activeDefinitionsPath) {
        res.locals['hasDefinitionPath'] = !!activeDefinitionsPath;
        res.locals['definitionsPath'] = activeDefinitionsPath;
        res.locals['pathSuffix'] = `?dataProductDefinitionsPath=${res.locals['definitionsPath']}`;
        logger.info(`DEFINITIONS PATH SET: ${JSON.stringify({
            definitionsPath: res.locals['definitionsPath'],
            dpdPathFromBody: res.locals['dpdPathFromBody'],
            dpdPathFromQuery: res.locals['dpdPathFromQuery'],
        })}`);
    }
};
/**
 * Populates the definitions and sets the local collection
 *
 * @param {Services} services
 * @param {Request} req
 * @param {Response} res
 * @param {DprConfig} [config]
 */
const populateDefinitions = async (services, req, res, config) => {
    // 1. set the definitions path
    deriveDefinitionsPath(req, res, config);
    // 2. set the definitions
    await setDefinitions(services, req, res, config);
    // 3. set the collections
    await setProductCollection(services, req, res);
};
/**
 * Set the current collection in the UI
 *
 * - only updates and makes API call if collection ID has changed
 *
 * @param {Services} services
 * @param {Request} req
 * @param {Response} res
 */
const setProductCollection = async (services, req, res) => {
    const { token, dprUser } = localsHelper.getValues(res);
    const selectedProductCollectionId = await services.productCollectionStoreService.getSelectedProductCollectionId(dprUser.id);
    const currentId = req.session['currentCollectionId'];
    const updateCollection = currentId !== selectedProductCollectionId;
    req.session['currentCollectionId'] = selectedProductCollectionId;
    const allDefs = req.session['allDefinitions'];
    res.locals['definitions'] = allDefs ?? [];
    if (updateCollection) {
        if (selectedProductCollectionId && allDefs) {
            const collection = await services.productCollectionService.getProductCollection(token, selectedProductCollectionId);
            if (collection) {
                req.session['currentCollection'] = collection;
                const productIds = collection.products.map((p) => p.productId);
                res.locals['definitions'] = allDefs.filter((def) => productIds.includes(def.id));
                logger.info(`COLLECTION SET: ${res.locals['definitions'].length}`);
                return;
            }
        }
        res.locals['definitions'] = allDefs ?? [];
    }
};
/**
 * Set the full catalogue of definitions to app.locals
 *
 * @param {Services} services
 * @param {Request} req
 * @param {Response} res
 * @param {DprConfig} [config]
 */
const setDefinitions = async (services, req, res, config) => {
    const { token } = localsHelper.getValues(res);
    if (shouldRunDefinitionsCheck(req.session, config)) {
        const defs = await services.reportingService.getDefinitions(token, res.locals['definitionsPath']);
        if (!defs)
            return;
        req.session['allDefinitions'] = defs;
        logger.info(`Definitions set: ${req.session['allDefinitions'].length}`);
        recordDefinitionsCheck(req.session);
    }
};
/**
 * Checks if the get definition endpoint should be run
 *
 * @export
 * @param {{ lastDefinitionsCheck?: number }} session
 * @param {DprConfig} [config]
 * @return {*}  {boolean}
 */
function shouldRunDefinitionsCheck(session, config) {
    const interval = config?.checkDefinitionsInterval;
    // No config -> always run
    if (interval === undefined) {
        return true;
    }
    // Explicit override -> always run
    if (interval === 0) {
        return true;
    }
    const lastRun = session.lastDefinitionsCheck;
    // Never run before -> run now
    if (!lastRun) {
        return true;
    }
    // Check interval
    return Date.now() - lastRun > interval;
}
/**
 * Records the last run into the session
 *
 * @export
 * @param {{ lastDefinitionsCheck?: number }} session
 */
function recordDefinitionsCheck(session) {
    // eslint-disable-next-line no-param-reassign
    session.lastDefinitionsCheck = Date.now();
}
/**
 * Sets active service config to app.locals
 *
 * @param {Services} services
 * @param {Response} res
 */
const initialiseServices = async (services, res) => {
    const { dprUser } = localsHelper.getValues(res);
    if (dprUser.id) {
        // Downloading
        if (!res.app.locals['downloadingEnabled']) {
            res.app.locals['downloadingEnabled'] = services.downloadPermissionService.enabled;
            logger.info(`Init service: downloadPermissionService: ${res.app.locals['downloadingEnabled']}`);
        }
        // Bookmarking
        if (!res.app.locals['bookmarkingEnabled']) {
            res.app.locals['bookmarkingEnabled'] = services.bookmarkService.enabled;
            logger.info(`Init service: bookmarkService: ${res.app.locals['bookmarkingEnabled']}`);
        }
        // Collections
        if (!res.app.locals['collectionsEnabled']) {
            res.app.locals['collectionsEnabled'] = services.productCollectionService.enabled;
            logger.info(`Init service: productCollectionService: ${res.app.locals['collectionsEnabled']}`);
        }
        // Missing reports
        if (!res.app.locals['requestMissingEnabled']) {
            res.app.locals['requestMissingEnabled'] = services.missingReportService.enabled;
            logger.info(`Init service: missingReportService: ${res.app.locals['requestMissingEnabled']}`);
        }
        // Save defaults
        const enabled = isBooleanFlagEnabledOrMissing('saveDefaultsEnabled', res.app)
            ? services.defaultFilterValuesService.enabled
            : false;
        const current = res.app.locals['saveDefaultsEnabled'];
        if (current !== enabled) {
            res.app.locals['saveDefaultsEnabled'] = enabled;
            logger.info(`Init service: defaultFilterValuesService: ${res.app.locals['saveDefaultsEnabled']}`);
        }
    }
};
/**
 * Populates the requested reports to locals
 *
 * @param {Services} services
 * @param {Response} res
 */
const populateRequestedReports = async (services, res) => {
    const { dprUser } = localsHelper.getValues(res);
    if (dprUser.id) {
        const { requestedReports, recentlyViewedReports } = await getAllMyReports(res, services, dprUser.id);
        res.locals['requestedReports'] = requestedReports;
        res.locals['recentlyViewedReports'] = recentlyViewedReports;
        if (res.app.locals['bookmarkingEnabled']) {
            res.locals['bookmarks'] = await getAllMyBookmarks(res, services, dprUser.id);
        }
    }
};
/**
 * Initialises the DPR paths to locals
 *
 * @param {Response} res
 */
const setUpDprPaths = (res) => {
    res.app.locals.dprPaths ??= {
        bookmarkActionEndpoint: '/dpr/my-reports/bookmarks',
        downloadActionEndpoint: '/dpr/download-report/',
        productCollectionEndpoint: '/dpr/product-collection/selected',
        bookmarkListPath: '/dpr/my-reports/bookmarks',
        requestedListPath: '/dpr/my-reports/requested-reports',
        recentlyViewedListPath: '/dpr/my-reports/recently-viewed',
        reportsCatalogue: '/dpr/report-catalogue',
        userReportsList: '/dpr/my-reports',
        requestReportPath: '/dpr/request-report',
        viewReportPath: '/dpr/view-report',
        dprHomepage: '/dpr',
    };
};

export { setupResources as default, recordDefinitionsCheck, setupResources, shouldRunDefinitionsCheck };
//# sourceMappingURL=setUpDprResources.js.map
