import FiltersFormUtils from '../../../../components/_async/async-filters-form/utils.js';
import localsHelper from '../../../../utils/localsHelper.js';
import FiltersUtils from '../../../../components/_filters/utils.js';
import { removeDuplicates } from '../../../../utils/reportStoreHelper.js';
import { UserStoreItemBuilder } from '../../../../utils/UserStoreItemBuilder.js';
import { ReportType, RequestStatus } from '../../../../types/UserReports.js';
import PersonalisationUtils from '../../../../utils/Personalisation/personalisationUtils.js';
import { FiltersType } from '../../../../components/_filters/filtersTypeEnum.js';
import { getFields, hasInteractiveFilters } from '../../../../utils/definitionUtils.js';

/**
 * Updates the store with the request details
 *
 * @param {Request} req
 * @param {Response} res
 * @param {Services} services
 * @param {SetQueryFromFiltersResult} querySummaryData
 * @param {string} executionData
 * @return {*}  {Promise<string>}
 */
const updateStore = async ({ req, res, services, queryData, executionData, childExecutionData, }) => {
    const { search, id, type } = req.body;
    const { dprUser, definitionsPath, dpdPathFromQuery } = localsHelper.getValues(res);
    await removeDuplicates({ storeService: services.requestedReportService, userId: dprUser.id, id, search });
    await removeDuplicates({ storeService: services.recentlyViewedService, userId: dprUser.id, id, search });
    const requestFormData = req.body;
    const reportData = {
        type: requestFormData.type,
        reportId: requestFormData.reportId,
        reportName: requestFormData.reportName,
        description: requestFormData.description,
        id: requestFormData.id,
        name: requestFormData.name,
    };
    let requestedReportData;
    switch (type) {
        case ReportType.REPORT:
            requestedReportData = new UserStoreItemBuilder(reportData, requestFormData)
                .addExecutionData(executionData)
                .addChildExecutionData(childExecutionData)
                .addFilters(queryData?.filterData)
                .addSortData(queryData?.sortData)
                .addDefinitionsPath(definitionsPath, dpdPathFromQuery)
                .addRequestUrls(req)
                .addQuery(queryData)
                .addStatus(RequestStatus.SUBMITTED)
                .addTimestamp()
                .build();
            break;
        case ReportType.DASHBOARD: {
            requestedReportData = new UserStoreItemBuilder(reportData, requestFormData)
                .addExecutionData(executionData)
                .addChildExecutionData(childExecutionData)
                .addFilters(queryData?.filterData)
                .addDefinitionsPath(definitionsPath, dpdPathFromQuery)
                .addRequestUrls(req)
                .addQuery(queryData)
                .addStatus(RequestStatus.SUBMITTED)
                .addTimestamp()
                .addMetrics(JSON.parse(req.body.sections))
                .build();
            break;
        }
    }
    if (requestedReportData)
        await services.requestedReportService.addReport(dprUser.id, requestedReportData);
};
async function requestChildReports(childVariants, reportingService, token, reportId, queryData, dataProductDefinitionsPath) {
    let query;
    if (queryData) {
        query = queryData.query;
        delete query['sortColumn'];
        delete query['sortedAsc'];
    }
    return Promise.all(childVariants.map((childVariant) => reportingService
        .requestAsyncReport(token, reportId, childVariant.id, {
        ...(query && query),
        ...(dataProductDefinitionsPath && { dataProductDefinitionsPath }),
    })
        .then((response) => {
        const { executionId, tableId } = response;
        if (!executionId || !tableId) {
            throw new Error('requestChildReports: No execution of tableId in response');
        }
        return { executionId, tableId, variantId: childVariant.id };
    })));
}
const requestProduct = async ({ req, res, token, dashboardService, reportingService, }) => {
    const { definitionsPath: dataProductDefinitionsPath, dpdPathFromQuery } = localsHelper.getValues(res);
    const { reportId, id, type } = req.body;
    let fields;
    let queryData;
    let executionId;
    let tableId;
    let definition;
    let childVariants = [];
    if (type === ReportType.REPORT) {
        definition = await reportingService.getDefinition(token, reportId, id, dataProductDefinitionsPath);
        fields = definition?.variant.specification?.fields || [];
        queryData = FiltersFormUtils.setQueryFromFilters(req, fields);
        ({ executionId, tableId } = await reportingService.requestAsyncReport(token, reportId, id, {
            ...queryData.query,
            dataProductDefinitionsPath,
        }));
        childVariants = definition.variant.childVariants ?? [];
    }
    if (type === ReportType.DASHBOARD) {
        definition = await dashboardService.getDefinition(token, reportId, id, dataProductDefinitionsPath);
        fields = definition?.filterFields || [];
        queryData = FiltersFormUtils.setQueryFromFilters(req, fields);
        ({ executionId, tableId } = await dashboardService.requestAsyncDashboard(token, reportId, id, {
            ...queryData.query,
            dataProductDefinitionsPath,
        }));
    }
    const childExecutionData = await requestChildReports(childVariants, reportingService, token, reportId, queryData, dataProductDefinitionsPath);
    const executionData = {
        executionId,
        tableId,
        ...(dpdPathFromQuery && { dataProductDefinitionsPath }),
    };
    return {
        executionData,
        childExecutionData,
        queryData,
    };
};
const renderDashboardRequestData = async ({ token, reportId, definitionPath, services, definition, }) => {
    const productDefinitions = await services.reportingService.getDefinitions(token, definitionPath);
    const productDefinition = productDefinitions.find((def) => def.id === reportId);
    const reportName = productDefinition?.name;
    const { name, description, sections, filterFields: fields } = definition;
    return {
        reportName,
        name,
        description,
        sections: sections || [],
        fields: fields || [],
    };
};
const renderReportRequestData = async (definition) => {
    const fields = getFields(definition);
    return {
        definition,
        reportName: definition.name,
        name: definition.variant.name,
        description: definition.variant.description || definition.description,
        template: definition.variant.specification,
        fields,
        interactive: definition?.variant?.interactive || hasInteractiveFilters(fields),
    };
};
const getDefintionByType = async (req, res, _next, services) => {
    const { token, definitionsPath } = localsHelper.getValues(res);
    const { reportId, id, variantId, type } = req.params;
    const service = type === ReportType.REPORT ? services.reportingService : services.dashboardService;
    const definition = await service.getDefinition(token, reportId, (variantId || id), definitionsPath);
    return definition;
};
const getFilterData = async (req, res, fields, interactive, services, userId) => {
    const { reportId, id } = req.params;
    // 1. Get filters from definition with default values
    let filtersData = await FiltersFormUtils.renderFilters(fields, interactive);
    // 2. Update filter values with user context values. eg. establishmnent code
    filtersData.filters = PersonalisationUtils.setUserContextDefaults(res, filtersData.filters);
    // 3. Update filter values with saved defaults
    let defaultFilterValues;
    if (res.locals['saveDefaultsEnabled']) {
        defaultFilterValues = await services.defaultFilterValuesService.get(userId, reportId, id, FiltersType.REQUEST);
    }
    if (defaultFilterValues) {
        filtersData = PersonalisationUtils.setFilterValuesFromSavedDefaults(filtersData.filters, filtersData.sortBy || [], defaultFilterValues);
    }
    // 4. Overwrite filter values with query param values
    filtersData.filters = FiltersUtils.setFilterValuesFromRequest(filtersData.filters, req);
    return { filtersData, defaultFilterValues };
};
/**
 * Sends the request for the async report
 *
 * @param {AsyncReportUtilsParams} { req, res, services }
 * @return {*}
 */
const request = async ({ req, res, services }) => {
    const { token } = localsHelper.getValues(res);
    const requestArgs = { req, res, token };
    const { executionData, queryData, childExecutionData } = await requestProduct({
        ...requestArgs,
        dashboardService: services.dashboardService,
        reportingService: services.reportingService,
    });
    if (executionData.executionId && executionData.tableId) {
        await updateStore({
            req,
            res,
            services,
            queryData,
            executionData,
            childExecutionData,
        });
    }
    else {
        throw new Error('No execution data returned');
    }
    return executionData;
};
const cancelRequest = async ({ req, res, services }) => {
    const { token, dprUser, definitionsPath } = localsHelper.getValues(res);
    const { reportId, id, executionId, type } = req.params;
    let service = services.reportingService;
    if (type === ReportType.REPORT)
        service = services.reportingService;
    if (type === ReportType.DASHBOARD)
        service = services.dashboardService;
    const response = await service.cancelAsyncRequest(token, reportId, id, executionId, definitionsPath);
    if (response && response['cancellationSucceeded']) {
        await services.requestedReportService.updateStatus(executionId, dprUser.id, RequestStatus.ABORTED);
    }
};
/**
 * Returns the data required for rendering the async filters component
 *
 * @param {AsyncReportUtilsParams} { req, res, dataSources }
 * @return {*}
 */
const renderRequest = async ({ req, res, services, next, }) => {
    try {
        const { token, csrfToken, definitionsPath: definitionPath, dprUser } = localsHelper.getValues(res);
        const { reportId, type, id } = req.params;
        const { definition } = res.locals;
        const defaultsSaved = req.query['defaultsSaved'];
        const definitionApiArgs = { token, reportId: reportId, definitionPath, services };
        let name;
        let reportName;
        let description;
        let fields = [];
        let sections;
        let interactive;
        let filtersData;
        let defaultFilterValues = [];
        if (type === ReportType.REPORT) {
            ;
            ({ name, reportName, description, fields, interactive } = await renderReportRequestData(definition));
        }
        if (type === ReportType.DASHBOARD) {
            ;
            ({ name, reportName, description, sections, fields } = await renderDashboardRequestData({
                ...definitionApiArgs,
                definition: definition,
            }));
        }
        if (fields) {
            const filterData = await getFilterData(req, res, fields, interactive || false, services, dprUser.id);
            defaultFilterValues = filterData.defaultFilterValues || defaultFilterValues;
            filtersData = filterData.filtersData;
        }
        const reportData = {
            reportName: reportName,
            name: name,
            description: description,
            reportId: reportId,
            id: id,
            definitionPath: definitionPath,
            csrfToken,
            sections: sections,
            hasDefaults: defaultFilterValues?.length > 0,
            defaultsSaved,
            type: type,
            saveDefaultsEnabled: res.locals['saveDefaultsEnabled'],
        };
        return {
            title: `Request ${type}`,
            filtersDescription: `Customise your ${type} using the filters below and submit your request.`,
            filtersData,
            reportData,
        };
    }
    catch (error) {
        next(error);
        return false;
    }
};
const getFiltersFromReqBody = (req) => {
    return Object.keys(req.body)
        .filter((attr) => attr.includes('filters.'))
        .filter((attr) => !!req.body[attr])
        .map((attr) => {
        return { name: attr, value: req.body[attr] };
    });
};
var AysncRequestUtils = {
    request,
    cancelRequest,
    renderRequest,
    getFiltersFromReqBody,
    getDefintionByType,
};

export { cancelRequest, AysncRequestUtils as default, getDefintionByType, getFiltersFromReqBody, renderRequest, request, updateStore };
//# sourceMappingURL=utils.js.map
