import { getFieldDisplayName, getFieldsByName } from '../../definitionUtils.js';
import { DateMapper } from '../../DateMapper/DateMapper.js';

class SectionedDataHelper {
    sections = [];
    data = [];
    fields = [];
    summariesData = [];
    sectionedDataArray = [];
    sectionedData;
    reportQuery;
    createSectionKey(row) {
        // Build structured entries with column names + values
        const keyObj = this.sections.map((col) => ({
            name: col,
            value: row[col] ?? '',
        }));
        const keyValues = keyObj.map((v) => v.value).join('');
        const key = keyValues !== '' ? JSON.stringify(keyObj) : 'mainSection';
        return {
            keyObj: key === 'mainSection' ? [] : keyObj,
            key,
        };
    }
    createSectionTitle(keyObj, fields) {
        return keyObj
            .map((column) => {
            const fieldName = getFieldDisplayName(fields, column.name);
            return `${fieldName}: ${column.value}`;
        })
            .join(', ');
    }
    /**
     * Groups the data into sections
     *
     * @return {*}  {SectionedData}
     * @memberof SectionedDataBuilder
     */
    groupBySections(data, fields, summary) {
        const sectionMap = data.reduce((acc, row) => {
            const { key, keyObj } = this.createSectionKey(row);
            const title = this.createSectionTitle(keyObj, fields);
            // Create the section if needed
            if (!acc[key]) {
                acc[key] = {
                    title,
                    count: 0,
                    key,
                    keyObj,
                    summaries: [],
                    data: [],
                };
            }
            const section = acc[key];
            if (summary) {
                const { id, template } = summary;
                const existingSummary = section.summaries.find((s) => s.template === template);
                if (existingSummary) {
                    existingSummary.data.push(row);
                }
                else {
                    section.summaries.push({
                        id,
                        template,
                        fields,
                        data: [row],
                    });
                }
            }
            else {
                section.data.push(row);
                section.count += 1;
            }
            return acc;
        }, {});
        this.sectionedDataArray.push({
            sections: Object.values(sectionMap),
        });
    }
    createSummarySections() {
        this.summariesData.forEach((summaryData) => {
            const { fields, id, template, data } = summaryData;
            this.groupBySections(data, fields, { id, template });
        });
    }
    mergeSections() {
        return {
            sections: this.sectionedDataArray
                .flatMap((input) => input.sections)
                .reduce((acc, section) => {
                const { key, keyObj } = section;
                const existing = acc.find((s) => s.key === key);
                // If section doesn't exist yet → add fresh copy
                if (!existing) {
                    return acc.concat({
                        key: section.key || '',
                        keyObj: keyObj || [],
                        title: section.title || '',
                        count: section.count,
                        summaries: section.summaries ? [...section.summaries] : [],
                        data: section.data ? [...section.data] : [],
                    });
                }
                // If exists → update it by concatenating data & summaries
                const updated = acc.map((s) => s.key === key
                    ? {
                        ...s,
                        count: s.count + section.count,
                        summaries: (s.summaries ?? []).concat(section.summaries ?? []),
                        data: (s.data ?? []).concat(section.data ?? []),
                    }
                    : s);
                return updated;
            }, []),
        };
    }
    getSortField() {
        const { sortColumn } = this.reportQuery;
        return sortColumn && this.sections.includes(sortColumn) ? [sortColumn] : [];
    }
    getSortDirection() {
        const { sortedAsc } = this.reportQuery;
        return sortedAsc ? 1 : -1;
    }
    /**
     * Sorts sections based on keyObj values using an optional nameOrder override.
     * - nameOrder can be partial; unspecified names fall back to default order
     * - Default order is derived from the first section's keyObj sequence
     */
    sortSections(sectionedData) {
        const dataMapper = new DateMapper();
        const nameOrder = this.getSortField();
        const direction = this.getSortDirection();
        const { sections } = sectionedData;
        if (sections.length === 0)
            return sectionedData;
        // Default order is the order of the keyObj.name fields in the first section
        const defaultOrder = sections[0].keyObj.map((k) => k.name);
        // Merge partial nameOrder with default order
        const finalOrder = nameOrder
            // 1. start with user-specified names
            .filter((name) => defaultOrder.includes(name))
            // 2. append missing names in default order
            .concat(defaultOrder.filter((name) => !nameOrder.includes(name)));
        const buildMap = (keyObj) => keyObj.reduce((acc, ko) => ({ ...acc, [ko.name]: ko.value }), {});
        const sortedSections = [...sections].sort((a, b) => {
            const aMap = buildMap(a.keyObj);
            const bMap = buildMap(b.keyObj);
            // Compare using final order
            const comparisons = finalOrder.map((name) => {
                const aVal = aMap[name] ?? '';
                const bVal = bMap[name] ?? '';
                const aDate = dataMapper.parseIfDate(aVal);
                const bDate = dataMapper.parseIfDate(bVal);
                // date sorting
                if (aDate && bDate) {
                    const diff = aDate.valueOf() - bDate.valueOf();
                    if (diff !== 0)
                        return diff * direction;
                    return 0;
                }
                // fallback to string sorting
                return aVal.localeCompare(bVal, undefined, { numeric: true }) * direction;
            });
            // Return first non-zero comparison
            return comparisons.find((x) => x !== 0) ?? 0;
        });
        return {
            sections: sortedSections,
        };
    }
    withSections(sections) {
        this.sections = sections || [];
        return this;
    }
    withFields(fields) {
        this.fields = getFieldsByName(this.sections, fields);
        return this;
    }
    withData(data) {
        this.data = data;
        return this;
    }
    withSummaries(summaryData) {
        this.summariesData = summaryData || [];
        return this;
    }
    withReportQuery(reportQuery) {
        this.reportQuery = reportQuery;
        return this;
    }
    build() {
        let sections;
        if (this.sections.length) {
            this.groupBySections(this.data, this.fields);
            this.createSummarySections();
            sections = this.mergeSections();
            sections = this.sortSections(sections);
        }
        else {
            const singleSection = {
                key: '',
                keyObj: [],
                data: this.data,
                count: this.data.length,
                summaries: this.summariesData,
            };
            sections = { sections: [singleSection] };
        }
        return sections;
    }
}

export { SectionedDataHelper };
//# sourceMappingURL=SectionedDataHelper.js.map
