import { XmlStream } from "../../../utils/xml-stream.js";
import { xmlEncode, xmlDecode } from "../../../../../utils/utils.browser.js";
import { BaseXform } from "../base-xform.js";
class PivotTableXform extends BaseXform {
    constructor() {
        super();
        // Parser state consolidated into object for easier reset
        this.state = {
            inPivotFields: false,
            inRowFields: false,
            inColFields: false,
            inDataFields: false,
            inRowItems: false,
            inColItems: false,
            inLocation: false,
            inItems: false,
            inPivotTableStyleInfo: false,
            inChartFormats: false,
            inPivotArea: false
        };
        // Current parsing context
        this.currentPivotField = null;
        this.currentRowItem = null;
        this.currentColItem = null;
        this.currentChartFormat = null;
        // Buffer for collecting pivotArea XML
        this.pivotAreaXmlBuffer = [];
        this.pivotAreaDepth = 0;
        this.map = {};
        this.model = null;
    }
    prepare(_model) {
        // No preparation needed
    }
    get tag() {
        // http://www.datypic.com/sc/ooxml/e-ssml_pivotTableDefinition.html
        return "pivotTableDefinition";
    }
    reset() {
        this.model = null;
        // Reset all parser state flags using object
        Object.keys(this.state).forEach(key => {
            this.state[key] = false;
        });
        // Reset current context
        this.currentPivotField = null;
        this.currentRowItem = null;
        this.currentColItem = null;
        this.currentChartFormat = null;
        this.pivotAreaXmlBuffer = [];
        this.pivotAreaDepth = 0;
    }
    /**
     * Render pivot table XML.
     * Supports both newly created models and loaded models.
     */
    render(xmlStream, model) {
        const isLoaded = model.isLoaded;
        if (isLoaded) {
            this.renderLoaded(xmlStream, model);
        }
        else {
            this.renderNew(xmlStream, model);
        }
    }
    /**
     * Render newly created pivot table
     */
    renderNew(xmlStream, model) {
        const { rows, columns, values, cacheFields, cacheId, applyWidthHeightFormats } = model;
        // Build rowItems - need one <i> for each unique value in row fields, plus grand total
        const rowItems = buildRowItems(rows, cacheFields);
        // Build colItems - need one <i> for each unique value in col fields, plus grand total
        const colItems = buildColItems(columns, cacheFields, values.length);
        // Calculate pivot table dimensions
        const rowFieldItemCount = rows.length > 0 ? cacheFields[rows[0]]?.sharedItems?.length || 0 : 0;
        const colFieldItemCount = columns.length > 0 ? cacheFields[columns[0]]?.sharedItems?.length || 0 : 0;
        // Location: A3 is where pivot table starts
        // - firstHeaderRow: 1 (column headers are in first row of pivot table)
        // - firstDataRow: 2 (data starts in second row)
        // - firstDataCol: 1 (data starts in second column, after row labels)
        // Calculate ref based on actual data size:
        // - Start row: 3
        // - Header rows: 2 (column label row + subheader row)
        // - Data rows: rowFieldItemCount
        // - Grand total row: 1
        // endRow = 3 + 2 + rowFieldItemCount + 1 - 1 = 5 + rowFieldItemCount
        // Or simplified: startRow (3) + 1 (column labels) + rowFieldItemCount (data) + 1 (grand total)
        const endRow = 3 + 1 + rowFieldItemCount + 1; // = 5 + rowFieldItemCount
        const endCol = 1 + colFieldItemCount + 1; // start col + data cols + grand total
        const endColLetter = String.fromCharCode(64 + endCol);
        const locationRef = `A3:${endColLetter}${endRow}`;
        xmlStream.openXml(XmlStream.StdDocAttributes);
        xmlStream.openNode(this.tag, {
            ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
            name: "PivotTable2",
            cacheId,
            applyNumberFormats: "0",
            applyBorderFormats: "0",
            applyFontFormats: "0",
            applyPatternFormats: "0",
            applyAlignmentFormats: "0",
            applyWidthHeightFormats,
            dataCaption: "Values",
            updatedVersion: "8",
            minRefreshableVersion: "3",
            useAutoFormatting: "1",
            itemPrintTitles: "1",
            createdVersion: "8",
            indent: "0",
            compact: "0",
            compactData: "0",
            multipleFieldFilters: "0"
        });
        xmlStream.writeXml(`
      <location ref="${locationRef}" firstHeaderRow="1" firstDataRow="2" firstDataCol="1" />
      <pivotFields count="${cacheFields.length}">
        ${renderPivotFields(model)}
      </pivotFields>
      <rowFields count="${rows.length}">
        ${rows.map(rowIndex => `<field x="${rowIndex}" />`).join("\n    ")}
      </rowFields>
      <rowItems count="${rowItems.count}">
        ${rowItems.xml}
      </rowItems>
      <colFields count="${columns.length === 0 ? 1 : columns.length}">
        ${columns.length === 0
            ? '<field x="-2" />'
            : columns.map(columnIndex => `<field x="${columnIndex}" />`).join("\n    ")}
      </colFields>
      <colItems count="${colItems.count}">
        ${colItems.xml}
      </colItems>
      <dataFields count="${values.length}">
        ${buildDataFields(cacheFields, values, model.metric)}
      </dataFields>
      <pivotTableStyleInfo
        name="PivotStyleLight16"
        showRowHeaders="1"
        showColHeaders="1"
        showRowStripes="0"
        showColStripes="0"
        showLastColumn="1"
      />
      <extLst>
        <ext
          uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}"
          xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
        >
          <x14:pivotTableDefinition
            hideValuesRow="1"
            xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"
          />
        </ext>
        <ext
          uri="{747A6164-185A-40DC-8AA5-F01512510D54}"
          xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout"
        >
          <xpdl:pivotTableDefinition16
            EnabledSubtotalsDefault="0"
            SubtotalsOnTopDefault="0"
          />
        </ext>
      </extLst>
    `);
        xmlStream.closeNode();
    }
    /**
     * Render loaded pivot table (preserving original structure)
     */
    renderLoaded(xmlStream, model) {
        const attrs = {
            ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
            name: model.name || "PivotTable1",
            cacheId: model.cacheId,
            applyNumberFormats: model.applyNumberFormats || "0",
            applyBorderFormats: model.applyBorderFormats || "0",
            applyFontFormats: model.applyFontFormats || "0",
            applyPatternFormats: model.applyPatternFormats || "0",
            applyAlignmentFormats: model.applyAlignmentFormats || "0",
            // Preserve original value when present; default to Excel's typical "0".
            applyWidthHeightFormats: model.applyWidthHeightFormats ?? "0",
            dataCaption: model.dataCaption || "Values",
            updatedVersion: model.updatedVersion || "8",
            minRefreshableVersion: model.minRefreshableVersion || "3",
            useAutoFormatting: model.useAutoFormatting ? "1" : "0",
            itemPrintTitles: model.itemPrintTitles ? "1" : "0",
            createdVersion: model.createdVersion || "8",
            indent: model.indent !== undefined ? String(model.indent) : "0",
            multipleFieldFilters: model.multipleFieldFilters ? "1" : "0"
        };
        // Add outline attributes if present
        if (model.outline) {
            attrs.outline = "1";
        }
        if (model.outlineData) {
            attrs.outlineData = "1";
        }
        if (model.chartFormat !== undefined) {
            attrs.chartFormat = String(model.chartFormat);
        }
        // Only add compact/compactData if they are true (some files don't have them)
        if (model.compact) {
            attrs.compact = "1";
        }
        if (model.compactData) {
            attrs.compactData = "1";
        }
        xmlStream.openXml(XmlStream.StdDocAttributes);
        xmlStream.openNode(this.tag, attrs);
        // Location
        if (model.location) {
            xmlStream.leafNode("location", {
                ref: model.location.ref,
                firstHeaderRow: model.location.firstHeaderRow,
                firstDataRow: model.location.firstDataRow,
                firstDataCol: model.location.firstDataCol
            });
        }
        // Pivot fields
        if (model.pivotFields.length > 0) {
            xmlStream.openNode("pivotFields", { count: model.pivotFields.length });
            for (const pivotField of model.pivotFields) {
                this.renderPivotFieldLoaded(xmlStream, pivotField);
            }
            xmlStream.closeNode();
        }
        // Row fields
        if (model.rowFields.length > 0) {
            xmlStream.openNode("rowFields", { count: model.rowFields.length });
            for (const fieldIndex of model.rowFields) {
                xmlStream.leafNode("field", { x: fieldIndex });
            }
            xmlStream.closeNode();
        }
        // Row items - use parsed items if available; otherwise emit a minimal grand total
        if (model.rowItems && model.rowItems.length > 0) {
            xmlStream.openNode("rowItems", { count: model.rowItems.length });
            for (const item of model.rowItems) {
                this.renderRowColItem(xmlStream, item);
            }
            xmlStream.closeNode();
        }
        else {
            xmlStream.writeXml('<rowItems count="1"><i t="grand"><x/></i></rowItems>');
        }
        // Col fields
        // Only render colFields if it was present in the original file or if there are actual column fields
        // Some pivot tables don't have colFields element at all
        if (model.hasColFields || model.colFields.length > 0) {
            const colFieldCount = model.colFields.length === 0 ? 1 : model.colFields.length;
            xmlStream.openNode("colFields", { count: colFieldCount });
            if (model.colFields.length === 0) {
                xmlStream.leafNode("field", { x: -2 });
            }
            else {
                for (const fieldIndex of model.colFields) {
                    xmlStream.leafNode("field", { x: fieldIndex });
                }
            }
            xmlStream.closeNode();
        }
        // Col items - use parsed items if available
        if (model.colItems && model.colItems.length > 0) {
            xmlStream.openNode("colItems", { count: model.colItems.length });
            for (const item of model.colItems) {
                this.renderRowColItem(xmlStream, item);
            }
            xmlStream.closeNode();
        }
        else {
            xmlStream.writeXml('<colItems count="1"><i t="grand"><x/></i></colItems>');
        }
        // Data fields
        if (model.dataFields.length > 0) {
            xmlStream.openNode("dataFields", { count: model.dataFields.length });
            for (const dataField of model.dataFields) {
                const dfAttrs = {
                    name: dataField.name,
                    fld: dataField.fld,
                    baseField: dataField.baseField ?? 0,
                    baseItem: dataField.baseItem ?? 0
                };
                if (dataField.subtotal && dataField.subtotal !== "sum") {
                    dfAttrs.subtotal = dataField.subtotal;
                }
                xmlStream.leafNode("dataField", dfAttrs);
            }
            xmlStream.closeNode();
        }
        // Chart formats (for pivot charts) - preserve original pivotArea XML
        if (model.chartFormats && model.chartFormats.length > 0) {
            xmlStream.openNode("chartFormats", { count: model.chartFormats.length });
            for (const cf of model.chartFormats) {
                xmlStream.openNode("chartFormat", {
                    chart: cf.chart,
                    format: cf.format,
                    series: cf.series ? "1" : undefined
                });
                // Use preserved pivotArea XML or fallback to default
                if (cf.pivotAreaXml) {
                    xmlStream.writeXml(cf.pivotAreaXml);
                }
                else {
                    // Fallback for newly created chart formats (shouldn't happen for loaded models)
                    xmlStream.writeXml(`<pivotArea type="data" outline="0" fieldPosition="0"><references count="1"><reference field="4294967294" count="1" selected="0"><x v="0"/></reference></references></pivotArea>`);
                }
                xmlStream.closeNode();
            }
            xmlStream.closeNode();
        }
        // Style info
        xmlStream.leafNode("pivotTableStyleInfo", {
            name: model.styleName || "PivotStyleLight16",
            showRowHeaders: "1",
            showColHeaders: "1",
            showRowStripes: "0",
            showColStripes: "0",
            showLastColumn: "1"
        });
        // Extensions
        xmlStream.writeXml(`<extLst><ext uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}" xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"><x14:pivotTableDefinition hideValuesRow="1" xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"/></ext><ext uri="{747A6164-185A-40DC-8AA5-F01512510D54}" xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout"><xpdl:pivotTableDefinition16/></ext></extLst>`);
        xmlStream.closeNode();
    }
    /**
     * Render a row or column item element
     */
    renderRowColItem(xmlStream, item) {
        const attrs = {};
        if (item.t) {
            attrs.t = item.t;
        }
        if (item.x && item.x.length > 0) {
            xmlStream.openNode("i", attrs);
            for (const x of item.x) {
                if (x.v && x.v !== 0) {
                    xmlStream.leafNode("x", { v: x.v });
                }
                else {
                    xmlStream.leafNode("x");
                }
            }
            xmlStream.closeNode();
        }
        else {
            // Empty item (like <i/> in colItems)
            xmlStream.leafNode("i", attrs);
        }
    }
    /**
     * Render a loaded pivot field
     */
    renderPivotFieldLoaded(xmlStream, field) {
        const attrs = {};
        // Only add attributes that were present in the original
        if (field.axis) {
            attrs.axis = field.axis;
        }
        if (field.dataField) {
            attrs.dataField = "1";
        }
        // showAll is typically always present
        attrs.showAll = field.showAll ? "1" : "0";
        if (field.items && field.items.length > 0) {
            xmlStream.openNode("pivotField", attrs);
            xmlStream.openNode("items", { count: field.items.length + 1 });
            for (const itemIndex of field.items) {
                xmlStream.leafNode("item", { x: itemIndex });
            }
            // Grand total item
            xmlStream.writeXml('<item t="default"/>');
            xmlStream.closeNode(); // items
            xmlStream.closeNode(); // pivotField
        }
        else {
            xmlStream.leafNode("pivotField", attrs);
        }
    }
    parseOpen(node) {
        const { name, attributes } = node;
        switch (name) {
            case this.tag:
                // pivotTableDefinition root element
                this.reset();
                this.model = {
                    name: attributes.name,
                    cacheId: parseInt(attributes.cacheId || "0", 10),
                    uid: attributes["xr:uid"],
                    pivotFields: [],
                    rowFields: [],
                    colFields: [],
                    dataFields: [],
                    applyNumberFormats: attributes.applyNumberFormats,
                    applyBorderFormats: attributes.applyBorderFormats,
                    applyFontFormats: attributes.applyFontFormats,
                    applyPatternFormats: attributes.applyPatternFormats,
                    applyAlignmentFormats: attributes.applyAlignmentFormats,
                    applyWidthHeightFormats: attributes.applyWidthHeightFormats,
                    dataCaption: attributes.dataCaption,
                    updatedVersion: attributes.updatedVersion,
                    minRefreshableVersion: attributes.minRefreshableVersion,
                    createdVersion: attributes.createdVersion,
                    useAutoFormatting: attributes.useAutoFormatting === "1",
                    itemPrintTitles: attributes.itemPrintTitles === "1",
                    indent: attributes.indent ? parseInt(attributes.indent, 10) : 0,
                    compact: attributes.compact === "1",
                    compactData: attributes.compactData === "1",
                    multipleFieldFilters: attributes.multipleFieldFilters === "1",
                    outline: attributes.outline === "1",
                    outlineData: attributes.outlineData === "1",
                    chartFormat: attributes.chartFormat ? parseInt(attributes.chartFormat, 10) : undefined,
                    rowItems: [],
                    colItems: [],
                    chartFormats: [],
                    isLoaded: true
                };
                break;
            case "location":
                if (this.model) {
                    this.model.location = {
                        ref: attributes.ref,
                        firstHeaderRow: attributes.firstHeaderRow
                            ? parseInt(attributes.firstHeaderRow, 10)
                            : undefined,
                        firstDataRow: attributes.firstDataRow
                            ? parseInt(attributes.firstDataRow, 10)
                            : undefined,
                        firstDataCol: attributes.firstDataCol
                            ? parseInt(attributes.firstDataCol, 10)
                            : undefined
                    };
                }
                break;
            case "pivotFields":
                this.state.inPivotFields = true;
                break;
            case "pivotField":
                if (this.state.inPivotFields) {
                    this.currentPivotField = {
                        axis: attributes.axis,
                        dataField: attributes.dataField === "1",
                        items: [],
                        compact: attributes.compact === "1",
                        outline: attributes.outline === "1",
                        showAll: attributes.showAll === "1",
                        defaultSubtotal: attributes.defaultSubtotal === "1"
                    };
                }
                break;
            case "items":
                if (this.currentPivotField) {
                    this.state.inItems = true;
                }
                break;
            case "item":
                if (this.state.inItems && this.currentPivotField && attributes.x !== undefined) {
                    this.currentPivotField.items.push(parseInt(attributes.x, 10));
                }
                break;
            case "rowFields":
                this.state.inRowFields = true;
                break;
            case "colFields":
                this.state.inColFields = true;
                // Track that colFields element was present in original file
                if (this.model) {
                    this.model.hasColFields = true;
                }
                break;
            case "dataFields":
                this.state.inDataFields = true;
                break;
            case "rowItems":
                this.state.inRowItems = true;
                break;
            case "colItems":
                this.state.inColItems = true;
                break;
            case "i":
                // Handle row/col item element
                if (this.state.inRowItems && this.model) {
                    this.currentRowItem = { t: attributes.t, x: [] };
                }
                else if (this.state.inColItems && this.model) {
                    this.currentColItem = { t: attributes.t, x: [] };
                }
                break;
            case "x":
                // Handle x element inside row/col items or pivotArea
                if (this.state.inPivotArea) {
                    // Collect x element for pivotArea XML
                    const xAttrs = Object.entries(attributes)
                        .map(([k, v]) => `${k}="${v}"`)
                        .join(" ");
                    this.pivotAreaXmlBuffer.push(xAttrs ? `<x ${xAttrs}/>` : "<x/>");
                }
                else if (this.currentRowItem) {
                    this.currentRowItem.x.push({ v: attributes.v ? parseInt(attributes.v, 10) : 0 });
                }
                else if (this.currentColItem) {
                    this.currentColItem.x.push({ v: attributes.v ? parseInt(attributes.v, 10) : 0 });
                }
                break;
            case "chartFormats":
                this.state.inChartFormats = true;
                break;
            case "chartFormat":
                if (this.state.inChartFormats && this.model) {
                    this.currentChartFormat = {
                        chart: attributes.chart ? parseInt(attributes.chart, 10) : 0,
                        format: attributes.format ? parseInt(attributes.format, 10) : 0,
                        series: attributes.series === "1"
                    };
                }
                break;
            case "pivotArea":
                // Start collecting pivotArea XML for chartFormat
                if (this.currentChartFormat) {
                    this.state.inPivotArea = true;
                    const attrsStr = Object.entries(attributes)
                        .map(([k, v]) => `${k}="${v}"`)
                        .join(" ");
                    this.pivotAreaXmlBuffer = [attrsStr ? `<pivotArea ${attrsStr}>` : "<pivotArea>"];
                }
                break;
            case "references":
            case "reference":
                // Collect nested elements in pivotArea
                if (this.state.inPivotArea) {
                    this.pivotAreaDepth++;
                    const attrsStr = Object.entries(attributes)
                        .map(([k, v]) => `${k}="${v}"`)
                        .join(" ");
                    this.pivotAreaXmlBuffer.push(`<${name}${attrsStr ? " " + attrsStr : ""}>`);
                }
                break;
            case "field":
                // Handle field element (used in rowFields, colFields)
                if (this.model) {
                    const fieldIndex = parseInt(attributes.x || "0", 10);
                    if (this.state.inRowFields) {
                        this.model.rowFields.push(fieldIndex);
                    }
                    else if (this.state.inColFields) {
                        this.model.colFields.push(fieldIndex);
                    }
                }
                break;
            case "dataField":
                if (this.state.inDataFields && this.model) {
                    this.model.dataFields.push({
                        name: xmlDecode(attributes.name || ""),
                        fld: parseInt(attributes.fld || "0", 10),
                        baseField: attributes.baseField ? parseInt(attributes.baseField, 10) : 0,
                        baseItem: attributes.baseItem ? parseInt(attributes.baseItem, 10) : 0,
                        subtotal: attributes.subtotal
                    });
                }
                break;
            case "pivotTableStyleInfo":
                if (this.model) {
                    this.model.styleName = attributes.name;
                }
                break;
        }
        return true;
    }
    parseText(_text) {
        // No text content in pivot table elements
    }
    parseClose(name) {
        // Handle pivotArea nested elements - close tags
        if (this.state.inPivotArea) {
            if (name === "pivotArea") {
                this.pivotAreaXmlBuffer.push("</pivotArea>");
                if (this.currentChartFormat) {
                    this.currentChartFormat.pivotAreaXml = this.pivotAreaXmlBuffer.join("");
                }
                this.state.inPivotArea = false;
                this.pivotAreaXmlBuffer = [];
                this.pivotAreaDepth = 0;
                return true;
            }
            else if (name === "references" || name === "reference") {
                this.pivotAreaXmlBuffer.push(`</${name}>`);
                this.pivotAreaDepth--;
                return true;
            }
            // x elements are self-closing, no need to handle close
            return true;
        }
        switch (name) {
            case this.tag:
                // End of pivotTableDefinition
                return false;
            case "pivotFields":
                this.state.inPivotFields = false;
                break;
            case "pivotField":
                if (this.currentPivotField && this.model) {
                    this.model.pivotFields.push(this.currentPivotField);
                    this.currentPivotField = null;
                }
                break;
            case "items":
                this.state.inItems = false;
                break;
            case "rowFields":
                this.state.inRowFields = false;
                break;
            case "colFields":
                this.state.inColFields = false;
                break;
            case "dataFields":
                this.state.inDataFields = false;
                break;
            case "rowItems":
                this.state.inRowItems = false;
                break;
            case "colItems":
                this.state.inColItems = false;
                break;
            case "i":
                // Finish row/col item
                if (this.currentRowItem && this.model) {
                    this.model.rowItems.push(this.currentRowItem);
                    this.currentRowItem = null;
                }
                else if (this.currentColItem && this.model) {
                    this.model.colItems.push(this.currentColItem);
                    this.currentColItem = null;
                }
                break;
            case "chartFormats":
                this.state.inChartFormats = false;
                break;
            case "chartFormat":
                if (this.currentChartFormat && this.model) {
                    this.model.chartFormats.push(this.currentChartFormat);
                    this.currentChartFormat = null;
                }
                break;
        }
        return true;
    }
    reconcile(_model, _options) {
        // No reconciliation needed
    }
}
PivotTableXform.PIVOT_TABLE_ATTRIBUTES = {
    xmlns: "http://schemas.openxmlformats.org/spreadsheetml/2006/main"
};
// Helpers
/**
 * Build rowItems XML - one item for each unique value in row fields, plus grand total.
 * Each <i> represents a row in the pivot table.
 * - Regular items: <i><x/></i> for index 0, <i><x v="index"/></i> for index > 0
 * - Grand total: <i t="grand"><x/></i>
 * Note: When v=0, the v attribute should be omitted (Excel convention)
 */
function buildRowItems(rows, cacheFields) {
    if (rows.length === 0) {
        // No row fields - just grand total
        return { count: 1, xml: '<i t="grand"><x /></i>' };
    }
    // Get unique values count from the first row field
    const rowFieldIndex = rows[0];
    const sharedItems = cacheFields[rowFieldIndex]?.sharedItems || [];
    const itemCount = sharedItems.length;
    // Build items: one for each unique value + grand total
    const items = [];
    // Regular items - reference each unique value by index
    // Note: v="0" should be omitted (Excel uses <x/> instead of <x v="0"/>)
    for (let i = 0; i < itemCount; i++) {
        if (i === 0) {
            items.push("<i><x /></i>");
        }
        else {
            items.push(`<i><x v="${i}" /></i>`);
        }
    }
    // Grand total row
    items.push('<i t="grand"><x /></i>');
    return {
        count: items.length,
        xml: items.join("\n        ")
    };
}
/**
 * Build colItems XML - one item for each unique value in column fields, plus grand total.
 * When there are multiple data fields (values), each column value may have sub-columns.
 * Note: When v=0, the v attribute should be omitted (Excel convention)
 */
function buildColItems(columns, cacheFields, valueCount) {
    if (columns.length === 0) {
        // No column fields - columns are based on data fields (values)
        if (valueCount > 1) {
            // Multiple values: one column per value + grand total
            const items = [];
            for (let i = 0; i < valueCount; i++) {
                if (i === 0) {
                    items.push("<i><x /></i>");
                }
                else {
                    items.push(`<i><x v="${i}" /></i>`);
                }
            }
            items.push('<i t="grand"><x /></i>');
            return { count: items.length, xml: items.join("\n        ") };
        }
        // Single value: just grand total
        return { count: 1, xml: '<i t="grand"><x /></i>' };
    }
    // Get unique values count from the first column field
    const colFieldIndex = columns[0];
    const sharedItems = cacheFields[colFieldIndex]?.sharedItems || [];
    const itemCount = sharedItems.length;
    // Build items: one for each unique value + grand total
    const items = [];
    // Regular items - reference each unique value by index
    // Note: v="0" should be omitted (Excel uses <x/> instead of <x v="0"/>)
    for (let i = 0; i < itemCount; i++) {
        if (i === 0) {
            items.push("<i><x /></i>");
        }
        else {
            items.push(`<i><x v="${i}" /></i>`);
        }
    }
    // Grand total column
    items.push('<i t="grand"><x /></i>');
    return {
        count: items.length,
        xml: items.join("\n        ")
    };
}
/**
 * Build dataField XML elements for all values in the pivot table.
 * Supports multiple values when columns is empty.
 */
function buildDataFields(cacheFields, values, metric) {
    const metricName = metric === "count" ? "Count" : "Sum";
    // For 'count' metric, Excel requires subtotal="count" attribute
    const subtotalAttr = metric === "count" ? ' subtotal="count"' : "";
    return values
        .map(valueIndex => `<dataField
          name="${metricName} of ${xmlEncode(cacheFields[valueIndex].name)}"
          fld="${valueIndex}"
          baseField="0"
          baseItem="0"${subtotalAttr}
        />`)
        .join("");
}
function renderPivotFields(pivotTable) {
    // Pre-compute field type lookup for O(1) access
    const rowSet = new Set(pivotTable.rows);
    const colSet = new Set(pivotTable.columns);
    const valueSet = new Set(pivotTable.values);
    return pivotTable.cacheFields
        .map((cacheField, fieldIndex) => {
        const isRow = rowSet.has(fieldIndex);
        const isCol = colSet.has(fieldIndex);
        const isValue = valueSet.has(fieldIndex);
        return renderPivotField(isRow, isCol, isValue, cacheField.sharedItems);
    })
        .join("");
}
function renderPivotField(isRow, isCol, isValue, sharedItems) {
    // A field can be both a row/column field AND a value field
    // In this case, it needs both axis attribute AND dataField="1"
    if (isRow || isCol) {
        const axis = isRow ? "axisRow" : "axisCol";
        // Row and column fields should NOT have defaultSubtotal="0"
        let axisAttributes = 'compact="0" outline="0" showAll="0"';
        // If also a value field, add dataField="1"
        if (isValue) {
            axisAttributes = `dataField="1" ${axisAttributes}`;
        }
        // items = one for each shared item + one default item
        const itemsXml = [
            ...sharedItems.map((_item, index) => `<item x="${index}" />`),
            '<item t="default" />' // Required default item for subtotals/grand totals
        ].join("\n              ");
        return `
      <pivotField axis="${axis}" ${axisAttributes}>
        <items count="${sharedItems.length + 1}">
          ${itemsXml}
        </items>
      </pivotField>
    `;
    }
    // Value fields and non-axis fields should have defaultSubtotal="0"
    const defaultAttributes = 'compact="0" outline="0" showAll="0" defaultSubtotal="0"';
    return `
    <pivotField
      ${isValue ? 'dataField="1"' : ""}
      ${defaultAttributes}
    />
  `;
}
export { PivotTableXform };
