function makeRows(n) {
    const dropdowns = ["Option 1", "Option 2", "Option 3"]
    const hardselects = ["Alpha", "Bravo", "Charlie"]
    // SOME_NUM is a HARDSELECT_HOOK column — values must come from the dynamic
    // validation service (editors/getdynamiccolvals.js -> `other`). Use a subset
    // of those valid values so the cells pass validation.
    const someNumValues = [
        0.00105564761956, 0.00521895988156, 0.0058409725343, 0.00613050395908,
        0.01305025071513, 0.01442142483518, 0.02422838845487, 0.0256445333481,
        0.02624525922641, 0.02891322459509, 0.02972866503043, 0.03103561933666,
        0.03150132113671, 0.03522355064527, 0.03529406247441, 0.03975785711768,
        0.04319973664507, 0.04521169189606, 0.04787342951068, 0.04948144222119
    ]
    const rows = []
    for (let i = 0; i < n; i++) {
        rows.push({
            _____DELETE__THIS__RECORD_____: "No",
            PRIMARY_KEY_FIELD: i,
            SOME_CHAR: "dummy data row " + i,
            SOME_DROPDOWN: dropdowns[i % dropdowns.length],
            SOME_HARDSELECT: hardselects[i % hardselects.length],
            SOME_NUM: someNumValues[i % someNumValues.length],
            SOME_DATE: "1960-02-12",
            SOME_DATETIME: "1960-01-01 00:00:42",
            SOME_TIME: "00:00:42",
            SOME_SHORTNUM: (i % 99) + 1,
            SOME_BESTNUM: (i % 90) + 10,
            // demo columns for the newer DQ rule types
            READONLY_COL: "Readonly default",
            HIDDEN_COL: "Hidden default",
            ROUND_COL: Number((i + 1 + i / 7).toFixed(5)), // rounds on edit
            NUMFMT_COL: 1000 + i * 12.5                     // shown as EUR
        })
    }
    return rows
}

const data = {
    SYSDATE: "26SEP22",
    SYSTIME: "08:30",
    approvers: [
        { PERSONNAME: "sasdemo", EMAIL: "sasdemo", USERID: "sasdemo" }
    ],
    cols: [
        {
            NAME: "PRIMARY_KEY_FIELD",
            VARNUM: 1,
            LABEL: "PRIMARY_KEY_FIELD",
            FMTNAME: "",
            DDTYPE: "NUMERIC",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "SOME_BESTNUM",
            VARNUM: 9,
            LABEL: "SOME_BESTNUM",
            FMTNAME: "BEST",
            DDTYPE: "NUMERIC",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "SOME_CHAR",
            VARNUM: 2,
            LABEL: "SOME_CHAR",
            FMTNAME: "",
            DDTYPE: "CHARACTER",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "SOME_DATE",
            VARNUM: 5,
            LABEL: "SOME_DATE",
            FMTNAME: "DATE",
            DDTYPE: "DATE",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "SOME_DATETIME",
            VARNUM: 6,
            LABEL: "SOME_DATETIME",
            FMTNAME: "DATETIME",
            DDTYPE: "DATETIME",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "SOME_DROPDOWN",
            VARNUM: 3,
            LABEL: "SOME_DROPDOWN",
            FMTNAME: "",
            DDTYPE: "CHARACTER",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "SOME_HARDSELECT",
            VARNUM: 10,
            LABEL: "SOME_HARDSELECT",
            FMTNAME: "",
            DDTYPE: "CHARACTER",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "SOME_NUM",
            VARNUM: 4,
            LABEL: "SOME_NUM",
            FMTNAME: "",
            DDTYPE: "NUMERIC",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "SOME_SHORTNUM",
            VARNUM: 8,
            LABEL: "SOME_SHORTNUM",
            FMTNAME: "",
            DDTYPE: "NUMERIC",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "SOME_TIME",
            VARNUM: 7,
            LABEL: "SOME_TIME",
            FMTNAME: "TIME",
            DDTYPE: "TIME",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "",
            LONGDESC: ""
        },
        {
            NAME: "READONLY_COL",
            VARNUM: 11,
            LABEL: "READONLY_COL",
            FMTNAME: "",
            DDTYPE: "CHARACTER",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "Read-only: default value inserted on add-row, not editable",
            LONGDESC: ""
        },
        {
            NAME: "HIDDEN_COL",
            VARNUM: 12,
            LABEL: "HIDDEN_COL",
            FMTNAME: "",
            DDTYPE: "CHARACTER",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "Hidden: invisible in grid but submitted; default on add-row",
            LONGDESC: ""
        },
        {
            NAME: "ROUND_COL",
            VARNUM: 13,
            LABEL: "ROUND_COL",
            FMTNAME: "",
            DDTYPE: "NUMERIC",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "Round: edited values rounded Excel-style to 2 decimals",
            LONGDESC: ""
        },
        {
            NAME: "NUMFMT_COL",
            VARNUM: 14,
            LABEL: "NUMFMT_COL",
            FMTNAME: "",
            DDTYPE: "NUMERIC",
            CLS_RULE: "READ",
            MEMLABEL: "",
            DESC: "Number format: displayed as EUR currency (value unchanged)",
            LONGDESC: ""
        }
    ],
    dqdata: [
        { BASE_COL: "SOME_DROPDOWN", RULE_VALUE: "SOME_DROPDOWN", RULE_DATA: "Option 1", SELECTBOX_ORDER: 1 },
        { BASE_COL: "SOME_DROPDOWN", RULE_VALUE: "SOME_DROPDOWN", RULE_DATA: "Option 2", SELECTBOX_ORDER: 2 },
        { BASE_COL: "SOME_DROPDOWN", RULE_VALUE: "SOME_DROPDOWN", RULE_DATA: "Option 3", SELECTBOX_ORDER: 2 },
        {
            BASE_COL: "SOME_DROPDOWN",
            RULE_VALUE: "SOME_DROPDOWN",
            RULE_DATA: "This is a long option. This option is very long. It is optional, though.",
            SELECTBOX_ORDER: 3
        },
        { BASE_COL: "SOME_HARDSELECT", RULE_VALUE: "SOME_HARDSELECT", RULE_DATA: "Alpha", SELECTBOX_ORDER: 1 },
        { BASE_COL: "SOME_HARDSELECT", RULE_VALUE: "SOME_HARDSELECT", RULE_DATA: "Bravo", SELECTBOX_ORDER: 2 },
        { BASE_COL: "SOME_HARDSELECT", RULE_VALUE: "SOME_HARDSELECT", RULE_DATA: "Charlie", SELECTBOX_ORDER: 3 }
    ],
    dqrules: [
        { BASE_COL: "PRIMARY_KEY_FIELD", RULE_TYPE: "NOTNULL", RULE_VALUE: "", X: 0 },
        { BASE_COL: "SOME_NUM", RULE_TYPE: "HARDSELECT_HOOK", RULE_VALUE: "services/validations/mpe_x_test.some_num", X: 0 },
        { BASE_COL: "SOME_HARDSELECT", RULE_TYPE: "HARDSELECT", RULE_VALUE: "", X: 0 },
        { BASE_COL: "READONLY_COL", RULE_TYPE: "READONLY", RULE_VALUE: "Readonly default", X: 0 },
        { BASE_COL: "HIDDEN_COL", RULE_TYPE: "HIDDEN", RULE_VALUE: "Hidden default", X: 0 },
        { BASE_COL: "ROUND_COL", RULE_TYPE: "ROUND", RULE_VALUE: "2", X: 0 },
        { BASE_COL: "NUMFMT_COL", RULE_TYPE: "NUMBER_FORMAT", RULE_VALUE: '{"style":"currency","currency":"EUR"}', X: 0 }
    ],
    dsmeta: [
        { ODS_TABLE: "ATTRIBUTES", NAME: "Data Set Name", VALUE: "DC996664.MPE_X_TEST" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Observations", VALUE: "496" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Member Type", VALUE: "DATA" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Variables", VALUE: "9" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Engine", VALUE: "V9" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Indexes", VALUE: "1" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Created", VALUE: "09/26/2022 08:24:39" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Integrity Constraints", VALUE: "1" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Last Modified", VALUE: "09/26/2022 08:24:45" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Observation Length", VALUE: "32947" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Protection", VALUE: " ." },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Deleted Observations", VALUE: "0" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Data Set Type", VALUE: " ." },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Compressed", VALUE: "CHAR" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Label", VALUE: " ." },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Reuse Space", VALUE: "NO" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Data Representation", VALUE: "WINDOWS_64" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Point to Observations", VALUE: "YES" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Encoding", VALUE: "wlatin1 Western (Windows)" },
        { ODS_TABLE: "ATTRIBUTES", NAME: "Sorted", VALUE: "NO" },
        { ODS_TABLE: "ENGINEHOST", NAME: "Data Set Page Size", VALUE: "262144" },
        { ODS_TABLE: "ENGINEHOST", NAME: "Number of Data Set Pages", VALUE: "3" },
        { ODS_TABLE: "ENGINEHOST", NAME: "Index File Page Size", VALUE: "4096" },
        { ODS_TABLE: "ENGINEHOST", NAME: "Number of Index File Pages", VALUE: "4" },
        { ODS_TABLE: "ENGINEHOST", NAME: "Number of Data Set Repairs", VALUE: "0" },
        { ODS_TABLE: "ENGINEHOST", NAME: "ExtendObsCounter", VALUE: "YES" },
        { ODS_TABLE: "ENGINEHOST", NAME: "Filename", VALUE: "C:DataControllerDC996664mpe_x_test.sas7bdat" },
        { ODS_TABLE: "ENGINEHOST", NAME: "Release Created", VALUE: "9.0401M7" },
        { ODS_TABLE: "ENGINEHOST", NAME: "Host Created", VALUE: "X64_DSRV16" },
        { ODS_TABLE: "ENGINEHOST", NAME: "Owner Name", VALUE: "BUILTINAdministrators" },
        { ODS_TABLE: "ENGINEHOST", NAME: "File Size", VALUE: " 1MB" },
        { ODS_TABLE: "ENGINEHOST", NAME: "File Size (bytes)", VALUE: "1048576" }
    ],
    maxvarlengths: [
        { NAME: "_____DELETE__THIS__RECORD_____", MAXLEN: 3 },
        { NAME: "PRIMARY_KEY_FIELD", MAXLEN: 4 },
        { NAME: "some_char", MAXLEN: 591 },
        { NAME: "some_dropdown", MAXLEN: 8 },
        { NAME: "some_hardselect", MAXLEN: 7 },
        { NAME: "some_num", MAXLEN: 8 },
        { NAME: "some_date", MAXLEN: 10 },
        { NAME: "some_datetime", MAXLEN: 19 },
        { NAME: "some_time", MAXLEN: 8 },
        { NAME: "some_shortnum", MAXLEN: 3 },
        { NAME: "some_bestnum", MAXLEN: 3 },
        { NAME: "readonly_col", MAXLEN: 16 },
        { NAME: "hidden_col", MAXLEN: 14 },
        { NAME: "round_col", MAXLEN: 8 },
        { NAME: "numfmt_col", MAXLEN: 8 }
    ],
    query: [],
    sasdata: makeRows(100),
    $sasdata: {
        vars: {
            _____DELETE__THIS__RECORD_____: { format: "$3.", label: "_____DELETE__THIS__RECORD_____", length: "3", type: "char" },
            PRIMARY_KEY_FIELD: { format: "best.", label: "PRIMARY_KEY_FIELD", length: "8", type: "num" },
            SOME_CHAR: { format: "$32767.", label: "SOME_CHAR", length: "32767", type: "char" },
            SOME_DROPDOWN: { format: "$128.", label: "SOME_DROPDOWN", length: "128", type: "char" },
            SOME_HARDSELECT: { format: "$128.", label: "SOME_HARDSELECT", length: "128", type: "char" },
            SOME_NUM: { format: "best.", label: "SOME_NUM", length: "8", type: "num" },
            SOME_DATE: { format: "$200.", label: "SOME_DATE", length: "200", type: "char" },
            SOME_DATETIME: { format: "$200.", label: "SOME_DATETIME", length: "200", type: "char" },
            SOME_TIME: { format: "$200.", label: "SOME_TIME", length: "200", type: "char" },
            SOME_SHORTNUM: { format: "best.", label: "SOME_SHORTNUM", length: "4", type: "num" },
            SOME_BESTNUM: { format: "BEST.", label: "SOME_BESTNUM", length: "8", type: "num" },
            READONLY_COL: { format: "$200.", label: "READONLY_COL", length: "200", type: "char" },
            HIDDEN_COL: { format: "$200.", label: "HIDDEN_COL", length: "200", type: "char" },
            ROUND_COL: { format: "best.", label: "ROUND_COL", length: "8", type: "num" },
            NUMFMT_COL: { format: "best.", label: "NUMFMT_COL", length: "8", type: "num" }
        }
    },
    sasparams: [
        {
            COLHEADERS: "_____DELETE__THIS__RECORD_____,PRIMARY_KEY_FIELD,SOME_CHAR,SOME_DROPDOWN,SOME_HARDSELECT,SOME_NUM,SOME_DATE,SOME_DATETIME,SOME_TIME,SOME_SHORTNUM,SOME_BESTNUM,READONLY_COL,HIDDEN_COL,ROUND_COL,NUMFMT_COL",
            FILTER_TEXT: "",
            PKCNT: 1,
            PK: "PRIMARY_KEY_FIELD",
            DTVARS: " SOME_DATE",
            DTTMVARS: " SOME_DATETIME",
            TMVARS: " SOME_TIME",
            COLTYPE: "{\"data\":\"_____DELETE__THIS__RECORD_____\",\"type\":\"dropdown\",\"source\":[\"No\",\"Yes\"]},{\"data\":\"PRIMARY_KEY_FIELD\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"SOME_CHAR\"},{\"data\":\"SOME_DROPDOWN\"},{\"data\":\"SOME_HARDSELECT\"},{\"data\":\"SOME_NUM\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"SOME_DATE\",\"type\":\"date\"},{\"data\":\"SOME_DATETIME\",\"type\":\"datetime\"},{\"data\":\"SOME_TIME\",\"type\":\"time\"},{\"data\":\"SOME_SHORTNUM\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"SOME_BESTNUM\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"READONLY_COL\"},{\"data\":\"HIDDEN_COL\"},{\"data\":\"ROUND_COL\",\"type\":\"numeric\",\"format\":\"0\"},{\"data\":\"NUMFMT_COL\",\"type\":\"numeric\",\"format\":\"0\"}",
            LOADTYPE: "UPDATE",
            RK_FLAG: 0,
            CLS_FLAG: 0
        }
    ],
    xl_rules: [],
    _DEBUG: "",
    _METAUSER: "sasdemo@SAS",
    _METAPERSON: "sasdemo",
    _PROGRAM: "/Projects/app/dc/services/editors/getdata",
    AUTOEXEC: "D%3A%5Copt%5Csasinside%5CConfig%5CLev1%5CSASApp%5CStoredProcessServer%5Cautoexec.sas",
    MF_GETUSER: "sasdemo",
    SYSCC: "0",
    SYSENCODING: "wlatin1",
    SYSERRORTEXT: "",
    SYSHOSTNAME: "SAS",
    SYSPROCESSID: "41DD8056A491DB23409E940000000000",
    SYSPROCESSMODE: "SAS Stored Process Server",
    SYSPROCESSNAME: "",
    SYSJOBID: "27448",
    SYSSCPL: "X64_DSRV16",
    SYSSITE: "123",
    SYSUSERID: "sassrv",
    SYSVLONG: "9.04.01M7P080520",
    SYSWARNINGTEXT: "ENCODING option ignored for files opened with RECFM=N.",
    END_DTTM: "2022-09-26T08:30:13.853000",
    MEMSIZE: "46GB"
}

// The DQ-rule demo columns (HARDSELECT / READONLY / HIDDEN / ROUND /
// NUMBER_FORMAT) live on their own mock table, MPE_X_NEW, so that the shared
// MPE_X_TEST keeps matching the excel upload fixtures (which predate these
// columns). Mirror of the SASJS mock (editors/getdata.js). The demo tables are
// built lazily (below) so we don't clone the large MPE_X_TEST payload unless
// MPE_X_NEW is actually requested.
const RULE_DEMO_COLS = ['SOME_HARDSELECT', 'READONLY_COL', 'HIDDEN_COL', 'ROUND_COL', 'NUMFMT_COL']

function stripRuleCols(t) {
    const uc = new Set(RULE_DEMO_COLS)
    const lc = new Set(RULE_DEMO_COLS.map((c) => c.toLowerCase()))

    t.cols = t.cols.filter((c) => !uc.has(c.NAME))
    t.dqdata = t.dqdata.filter((d) => !uc.has(d.BASE_COL))
    t.dqrules = t.dqrules.filter((r) => !uc.has(r.BASE_COL))
    t.maxvarlengths = t.maxvarlengths.filter((m) => !lc.has((m.NAME || '').toLowerCase()))
    RULE_DEMO_COLS.forEach((c) => delete t.$sasdata.vars[c])
    t.sasdata = t.sasdata.map((row) => {
        const r = { ...row }
        RULE_DEMO_COLS.forEach((c) => delete r[c])
        return r
    })
    t.sasparams = t.sasparams.map((p) => {
        const headers = p.COLHEADERS.split(',').filter((h) => !uc.has(h))
        const coltype = JSON.parse('[' + p.COLTYPE + ']')
            .filter((o) => !uc.has(o.data))
            .map((o) => JSON.stringify(o))
            .join(',')
        return { ...p, COLHEADERS: headers.join(','), COLTYPE: coltype }
    })
    return t
}

const _reqFile =
    typeof _WEBIN_FILEREF1 !== 'undefined' && _WEBIN_FILEREF1 ? _WEBIN_FILEREF1.toString() : ''

let _out
if (_reqFile.includes('MPE_X_NEW')) {
    // MPE_X_NEW: full clone of MPE_X_TEST that retains the rule columns.
    _out = JSON.parse(JSON.stringify(data))
    _out.dsmeta = _out.dsmeta.map((m) =>
        m.NAME === 'Data Set Name' ? { ...m, VALUE: m.VALUE.replace('MPE_X_TEST', 'MPE_X_NEW') } : m
    )
} else {
    // MPE_X_TEST: strip the rule columns so it matches the excel upload fixtures.
    _out = stripRuleCols(data)
}

_webout = JSON.stringify(_out)
