"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateSeedExcelTemplate = generateSeedExcelTemplate;
const Excel = __importStar(require("exceljs"));
const utils_1 = require("@postxl/utils");
/**
 * Creates an Excel template file that can be used to manage Seed data.
 * The template file contains a sheet for each model, with the column for each field.
 */
async function generateSeedExcelTemplate({ context }) {
    const wb = initializeWorkbook();
    for (const model of [...context.models.values()].sort((a, b) => a.name.localeCompare(b.name))) {
        addModel({ model, wb });
    }
    const buffer = Buffer.from(await wb.xlsx.writeBuffer());
    return buffer;
}
function initializeWorkbook() {
    const wb = new Excel.Workbook();
    wb.creator = 'PostXL';
    wb.lastModifiedBy = 'PostXL';
    wb.created = new Date();
    wb.modified = new Date();
    return wb;
}
function addModel({ model, wb }) {
    // Sheets
    const ws = wb.addWorksheet(model.seedData.excel.sheetName);
    ws.views = [{ state: 'frozen', xSplit: 1, ySplit: 6, activeCell: 'B7' }];
    //Header row
    const headerCell = ws.getCell(1, 1);
    headerCell.value = model.seedData.excel.tableName;
    headerCell.font = { bold: true, size: 14 };
    // Description
    ws.getCell(3, 1).value = model.description;
    const { tableColumns, columnConfigs, blankRow } = getModelColumns({ model });
    const rows = getModelRows({ model });
    adjustColumnWidths({ columnConfigs, rows });
    // Labels
    for (const [index, columnConfig] of columnConfigs.entries()) {
        const columnIndex = index + 1;
        ws.getCell(1, columnIndex).border = { bottom: { style: 'thick' } };
        const cell = ws.getCell(5, columnIndex);
        cell.value = columnConfig.label;
        cell.font = { bold: true };
        const column = ws.getColumn(columnIndex);
        if (columnConfig.isHidden) {
            column.hidden = true;
        }
        column.width = columnConfig.width;
    }
    // Table
    const table = ws.addTable({
        name: model.seedData.excel.tableName,
        ref: 'A6',
        headerRow: true,
        totalsRow: false,
        style: {
            theme: 'TableStyleMedium2',
            showRowStripes: true,
        },
        columns: tableColumns,
        rows: rows.length > 0 ? rows : [blankRow],
    });
    table.commit();
}
function getModelColumns({ model }) {
    const tableColumns = [];
    const columnConfigs = [];
    for (const field of model.fields.values()) {
        if (field.kind === 'discriminatedUnion') {
            addDiscriminatedUnionColumns({ field, tableColumns, columnConfigs });
        }
        else {
            tableColumns.push({ name: (0, utils_1.toExcelColumnName)(field.excelName, field.name) });
            columnConfigs.push(getColumnConfig(field));
        }
    }
    const blankRow = columnConfigs.map(() => null);
    return { tableColumns, columnConfigs, blankRow };
}
function getColumnConfig(field) {
    const isHidden = field.kind === 'scalar' && (field.isCreatedAt || field.isUpdatedAt);
    const label = field.label;
    switch (field.kind) {
        case 'id':
            return { label, isHidden, width: 20, type: 'id' };
        case 'scalar':
            switch (field.type) {
                case 'string':
                    return { label, isHidden, width: 80, type: field.type };
                case 'number':
                    return { label, isHidden, width: 20, type: field.type };
                case 'boolean':
                    return { label, isHidden, width: 10, type: field.type };
                case 'Date':
                    return { label, isHidden, width: 20, type: field.type };
                default:
                    throw new utils_1.ExhaustiveSwitchCheck(field);
            }
        case 'relation':
            return { label, isHidden, width: 20, type: 'id' };
        case 'enum':
            return { label, isHidden, width: 40, type: 'string' };
        case 'discriminatedUnion':
            return { label, isHidden, width: 100, type: 'string' };
        default:
            throw new utils_1.ExhaustiveSwitchCheck(field);
    }
}
function addDiscriminatedUnionColumns({ field, tableColumns, columnConfigs, }) {
    tableColumns.push({ name: field.databaseName });
    columnConfigs.push(getColumnConfig(field));
    for (const f of field.commonFields.values()) {
        tableColumns.push({ name: f.databaseName });
        columnConfigs.push(getColumnConfig(f));
    }
    for (const member of field.members.values()) {
        for (const f of member.fields.values()) {
            tableColumns.push({ name: f.databaseName });
            columnConfigs.push(getColumnConfig(f));
        }
    }
}
function getModelRows({ model }) {
    if (model.seedData.data.length === 0) {
        return [];
    }
    return model.seedData.data.map((row) => getModelRow({ model, row }));
}
function getModelRow({ model, row }) {
    const result = [];
    for (const field of model.fields.values()) {
        if (field.kind === 'discriminatedUnion') {
            addDiscriminatedUnionRowValue({ field, value: row[field.name], result });
        }
        else {
            result.push(row[field.name] ?? null);
        }
    }
    return result;
}
function addDiscriminatedUnionRowValue({ field, value, result, }) {
    result.push(value[field.discriminatorFieldName]);
    for (const f of field.commonFields.values()) {
        result.push(value[f.name] ?? null);
    }
    for (const member of field.members.values()) {
        for (const f of member.fields.values()) {
            result.push(value[f.name] ?? null);
        }
    }
}
const maxWidth = 100;
function adjustColumnWidths({ columnConfigs, rows }) {
    for (let index = 0; index < columnConfigs.length; index++) {
        adjustColumnWidth({ columnConfig: columnConfigs[index], rows, index });
    }
}
function adjustColumnWidth({ columnConfig, rows, index, }) {
    switch (columnConfig.type) {
        case 'string':
        case 'number':
            adjustNumberOrStringColumnWidth({ columnConfig, rows, index });
            break;
        case 'boolean':
        case 'Date':
        case 'id':
            // No need to do anything
            break;
        default:
            throw new utils_1.ExhaustiveSwitchCheck(columnConfig.type);
    }
}
function adjustNumberOrStringColumnWidth({ columnConfig, rows, index, }) {
    const width = Math.max(length(columnConfig.label), ...rows.map((row) => length(row[index]))) * 1.5;
    columnConfig.width = Math.min(width, maxWidth);
}
function length(value) {
    if (typeof value === 'number') {
        return (Math.round(value * 100) / 100).toString().length;
    }
    if (typeof value === 'string') {
        return value.length;
    }
    return 0;
}
//# sourceMappingURL=excel-template.generator.js.map