import parseUrl from 'parseurl';
import { setUpBookmark } from '../bookmark/utils.js';
import { setUpDownload } from '../../routes/journeys/download-report/utils.js';
import ReportActionsUtils from './report-heading/report-actions/utils.js';
import { getFields, hasInteractiveFilters, getTemplate } from '../../utils/definitionUtils.js';
import PaginationUtils from './report-page/report-template/report-pagination/utils.js';
import TotalsUtils from './report-page/report-template/report-totals/utils.js';
import ReportTemplateUtils from './report-page/report-template/utils.js';
import FiltersUtils from '../_filters/utils.js';
import ColumnUtils from './report-heading/report-columns/report-columns-form/utils.js';
import { qsToQueryObject } from '../../utils/queryMappers.js';
import { LoadType, ReportType } from '../../types/UserReports.js';
import { ReportQuery } from '../../types/ReportQuery.js';
import { getActiveJourneyValue } from '../../utils/sessionHelper.js';
import localsHelper from '../../utils/localsHelper.js';
import { buildAppliedFilters } from '../_filters/filters-applied/utils.js';
import { apiTimestampToUiDateTime } from '../../utils/dateHelper.js';
import { FiltersType } from '../_filters/filtersTypeEnum.js';
import { ErrorHandler } from '../../utils/ErrorHandler/ErrorHandler.js';

class Report {
    services;
    res;
    req;
    definition;
    loadType;
    requestData;
    id;
    reportId;
    tableId;
    token;
    userId;
    variant;
    specification;
    fields;
    reportData;
    childData = [];
    summariesData = [];
    reportDetails;
    actions;
    columns;
    count;
    pagination;
    totals;
    dataTable;
    filterData;
    savedDefaultsConfig;
    reportQuery;
    appliedFilters;
    extractedRequestData;
    expired = false;
    constructor(services, res, req, definition, loadType, requestData) {
        this.services = services;
        this.res = res;
        this.req = req;
        this.definition = definition;
        this.loadType = loadType;
        this.requestData = requestData;
        // From locals
        this.token = res.locals['dprUser'].token;
        this.userId = res.locals['dprUser'].id;
        // From params
        this.id = this.req.params['id'];
        this.reportId = this.req.params['reportId'];
        this.tableId = this.req.params['tableId'];
        // From definition
        this.variant = this.definition.variant;
        const { specification } = this.variant;
        if (!specification) {
            throw new Error('No specification in definition');
        }
        this.specification = specification;
        this.fields = this.specification.fields;
    }
    build = async () => {
        // General report data
        const reportMeta = this.buildReportMeta();
        this.buildReportDetails();
        // Columns and filters
        this.buildColumns();
        await this.buildFilters();
        await this.buildSavedDefaultsConfig();
        this.buildAppliedFilters();
        // Get the data
        this.buildReportQuery();
        await this.getData();
        await this.getCount();
        if (this.expired) {
            return {
                expired: this.expired,
            };
        }
        // Template & page furniture
        this.buildTable();
        this.buildActions();
        this.buildPagination();
        return {
            renderData: {
                columns: this.columns,
                filterData: this.filterData,
                appliedFilters: this.appliedFilters,
                ...this.savedDefaultsConfig,
                count: this.count,
                ...reportMeta,
                ...this.actions,
                ...this.reportDetails,
                ...(this.pagination && { pagination: this.pagination }),
                ...(this.extractedRequestData && this.extractedRequestData),
                totals: this.totals,
                dataTable: this.dataTable,
                fields: this.fields,
            },
        };
    };
    /**
     * Gets all the report data
     *
     */
    getData = async () => {
        try {
            await this.getReportData();
            if (this.loadType === LoadType.ASYNC) {
                await this.getSummariesData();
                await this.setChildData();
            }
        }
        catch (error) {
            const dprError = new ErrorHandler(error).formatError();
            if (dprError.status === 404) {
                this.expired = true;
            }
            else {
                throw error;
            }
        }
    };
    /**
     * Gets the report data
     */
    getReportData = async () => {
        this.reportData = this.loadType === LoadType.SYNC ? await this.getSyncData() : await this.getAysncData();
    };
    getAysncData = async () => {
        const reportQueryRecord = this.reportQuery.toRecordWithFilterPrefix(true);
        return this.services.reportingService.getAsyncReport(this.token, this.reportId, this.id, this.tableId, reportQueryRecord);
    };
    getSyncData = async () => {
        const { resourceName } = this.variant;
        const listWithWarnings = await this.services.reportingService.getListWithWarnings(resourceName, this.token, this.reportQuery);
        return listWithWarnings.data;
    };
    /**
     * Gets the summaries data for summary tables
     * NOTE: Only available for Async
     *
     * @return {*}
     */
    getSummariesData = async () => {
        const { definitionsPath: dataProductDefinitionsPath } = localsHelper.getValues(this.res);
        this.summariesData = !this.variant.summaries
            ? []
            : await Promise.all(this.variant.summaries.map(async (summary) => {
                const summaryReport = await this.services.reportingService.getAsyncSummaryReport(this.token, this.reportId, this.id, this.tableId, summary.id, {
                    dataProductDefinitionsPath,
                });
                return {
                    ...summary,
                    data: summaryReport,
                };
            }));
    };
    /**
     * Gets the child date for a parent child report
     * NOTE: Only available for Async
     *
     */
    setChildData = async () => {
        // Get the child data, if applicable
        const { childVariants } = this.variant;
        this.childData = !childVariants
            ? []
            : await this.getChildData(childVariants, this.services, this.token, this.req, this.res, this.requestData);
    };
    getChildData = async (childVariants, services, token, req, res, requestData) => {
        const { definitionsPath: dataProductDefinitionsPath } = localsHelper.getValues(res);
        const { reportId } = req.params;
        const childExecutionData = requestData?.childExecutionData;
        if (!childExecutionData) {
            throw new Error('getChildData: No execution data found for child variants');
        }
        return Promise.all(childVariants.map(async (childVariant) => {
            const { specification } = childVariant;
            if (!specification) {
                throw new Error('getChildData: No specification found in child variant definition');
            }
            const query = new ReportQuery({
                fields: specification?.fields || [],
                template: 'parent-child',
                queryParams: req.query,
                definitionsPath: dataProductDefinitionsPath,
            }).toRecordWithFilterPrefix(true);
            const childData = childExecutionData.find((e) => e.variantId === childVariant.id);
            if (!childData || !childData.tableId) {
                throw new Error('getChildData: No matching child execution data found');
            }
            const { tableId: childTableId } = childData;
            const childReport = await services.reportingService.getAsyncReport(token, reportId, childVariant.id, childTableId, query);
            return {
                id: childVariant.id,
                data: childReport,
            };
        }));
    };
    /**
     * Builds the report table
     *
     */
    buildTable = () => {
        this.dataTable = ReportTemplateUtils.createReportTemplateData(this.definition, this.columns, this.reportData, this.childData, this.summariesData, this.reportQuery);
    };
    /**
     * Gets the relevant report details from the report definition
     *
     */
    buildReportDetails = () => {
        const { name: reportName, description: reportDescription } = this.definition;
        const { classification, printable, name, description } = this.variant;
        const { template, fields } = this.specification;
        this.reportDetails = {
            reportName,
            name,
            description: description || reportDescription || '',
            classification: classification || '',
            printable: Boolean(printable),
            template,
            specification: this.specification, // TODO: check if needed ???
            fields, // TODO: check if needed???
        };
    };
    /**
     * Gets the reports meta data required to load a report
     *
     * @return {*}
     */
    buildReportMeta = () => {
        const { csrfToken } = localsHelper.getValues(this.res);
        return {
            id: this.id,
            reportId: this.reportId,
            ...(this.tableId && { tableId: this.tableId }),
            loadType: this.loadType,
            csrfToken,
            type: ReportType.REPORT,
        };
    };
    /**
     * Extracts the relevant data from the requested report data
     *
     * @param {RequestedReport} requestData
     * @return {*}  {ExtractedRequestData}
     */
    extractDataFromRequest = (requestData) => {
        const { query, url, timestamp } = requestData;
        return {
            executionId: requestData.executionId,
            requestedTimestamp: apiTimestampToUiDateTime(timestamp.requested),
            querySummary: query?.summary || [],
            queryData: query?.data,
            requestUrl: url?.request,
            defaultQuery: url?.report?.default,
            dataProductDefinitionsPath: requestData.dataProductDefinitionsPath,
        };
    };
    /**
     * Builds the report actions:
     * - download config
     * - bookmark config
     * - general actions
     */
    buildActions = () => {
        const { tableId, reportId, id } = this.req.params;
        this.extractedRequestData = this.requestData ? this.extractDataFromRequest(this.requestData) : undefined;
        // Setup bookmark
        const bookmarkConfig = setUpBookmark(this.res, this.req, this.services.bookmarkService);
        // Setup download
        const downloadConfig = setUpDownload(this.res, this.req);
        // Setup other actions
        const actions = ReportActionsUtils.setActions(this.reportDetails, downloadConfig, this.extractedRequestData);
        // Get the feedback submission path
        const sessionKey = this.loadType === LoadType.SYNC ? { id, reportId } : { id, reportId, tableId };
        const feedbackSubmissionFormPath = getActiveJourneyValue(this.req, sessionKey, 'feedbackSubmissionFormPath');
        this.actions = {
            actions,
            downloadConfig,
            bookmarkConfig,
            feedbackFormHref: feedbackSubmissionFormPath,
        };
    };
    /**
     * Build thee filters
     *
     */
    buildFilters = async () => {
        this.filterData = await FiltersUtils.getInteractiveFilters({
            fields: getFields(this.definition),
            req: this.req,
        });
    };
    buildSavedDefaultsConfig = async () => {
        this.savedDefaultsConfig = {
            hasDefaults: await this.services.defaultFilterValuesService.hasDefaults(this.userId, this.reportId, this.id, FiltersType.INTERACTIVE),
            saveDefaultsEnabled: this.res.locals['saveDefaultsEnabled'],
        };
    };
    /**
     * Builds the applied filters buttons
     *
     */
    buildAppliedFilters = () => {
        const fields = getFields(this.definition);
        const sessionKey = { id: this.id, reportId: this.reportId, tableId: this.tableId };
        this.appliedFilters = buildAppliedFilters(this.req, sessionKey, fields);
    };
    /**
     * Build columns
     *
     */
    buildColumns = () => {
        this.columns = ColumnUtils.getColumns(this.specification, this.req);
    };
    /**
     * Builds the pagination config for a report
     */
    buildPagination = () => {
        const url = parseUrl(this.req);
        if (!url) {
            throw new Error('Unable to set url data from request');
        }
        let pagination;
        let totals;
        if (this.reportDetails.template === 'list') {
            pagination = PaginationUtils.getPaginationData(url, this.count, this.req);
            const { pageSize, currentPage, totalRows } = pagination;
            totals = TotalsUtils.getTotals(pageSize, currentPage, totalRows, this.dataTable.rowCount);
        }
        if (pagination) {
            this.pagination = pagination;
        }
        if (totals) {
            this.totals = totals;
        }
    };
    /**
     * Gets the row count
     *
     * @return {*}
     */
    getCount = async () => {
        if (this.loadType === LoadType.SYNC) {
            this.count = await this.services.reportingService.getCount(this.variant.resourceName, this.token, this.reportQuery);
        }
        else {
            this.count =
                !this.variant.interactive || !hasInteractiveFilters(getFields(this.definition))
                    ? await this.services.reportingService.getAsyncCount(this.token, this.tableId)
                    : await this.services.reportingService.getAsyncInteractiveCount(this.token, this.tableId, this.reportId, this.id, this.reportQuery);
        }
    };
    /**
     * Builds the report query
     *
     */
    buildReportQuery = () => {
        const { definitionsPath } = localsHelper.getValues(this.res);
        const fields = getFields(this.definition);
        const template = getTemplate(this.definition);
        // Sort
        const sortColumn = this.req.query?.['sortColumn'] || this.requestData?.sortBy?.data?.['sortColumn'];
        const sortedAsc = this.req.query?.['sortedAsc'] || this.requestData?.sortBy?.data?.['sortedAsc'];
        // Pagination
        const selectedPage = this.req.query?.['selectedPage'];
        const pageSize = this.req.query?.['pageSize'];
        // Filters from query string
        // 1. Initialise the filters query to the defaults from the DPD
        const interactiveDefaultSearch = getActiveJourneyValue(this.req, { id: this.id, reportId: this.reportId }, 'interactiveDefaultFiltersSearch');
        let filtersQuery = interactiveDefaultSearch ? qsToQueryObject(interactiveDefaultSearch, 'filters.') : {};
        // 2. Get the search params from the current report and use those if they are present
        const currentSearch = getActiveJourneyValue(this.req, { id: this.id, reportId: this.reportId, tableId: this.tableId }, 'currentReportFiltersSearch');
        if (currentSearch) {
            filtersQuery = qsToQueryObject(currentSearch, 'filters.');
        }
        const queryParams = {
            ...(sortColumn && { sortColumn }),
            ...(sortedAsc && { sortedAsc }),
            ...filtersQuery,
            ...(pageSize && { pageSize }),
            ...(selectedPage && { selectedPage }),
            ...(this.columns && { columns: this.columns.value }),
        };
        this.reportQuery = new ReportQuery({
            fields,
            template,
            queryParams,
            definitionsPath,
        });
    };
}

export { Report as default };
//# sourceMappingURL=Report.js.map
