#!/usr/bin/env node
// (C) 2021-2023 GoodData Corporation
const fs = require('fs');

const basePath = '../../fixtures/ComplexModel';
const filename = `${basePath}/model.maql`;

function generateMainDateDimension(mainDatasetCount) {
    const result = [];
    for (let i = 1; i <= mainDatasetCount; i += 1) {
        const dataset = i.toString().padStart(2, '0');
        result.push(
            `INCLUDE TEMPLATE "urn:custom_v2:date" MODIFY (IDENTIFIER "m${dataset}date01", TITLE "m${dataset} date01");`
        );
    }
    return result.join('\n');
}

function generateSubDateDimension(mainDatasetCount, subDatasetCount) {
    const result = [];
    for (let i = 1; i <= mainDatasetCount; i += 1) {
        const mainDataset = i.toString().padStart(2, '0');
        for (let j = 1; j <= subDatasetCount; j += 1) {
            const subDataset = j.toString().padStart(2, '0');
            result.push(
                `INCLUDE TEMPLATE "urn:custom_v2:date" MODIFY (IDENTIFIER "m${mainDataset}s${subDataset}date01", TITLE "m${mainDataset} s${subDataset} date01");`
            );
        }
    }
    return result.join('\n');
}

function generateMainFolder(mainDatasetCount) {
    const dataset = mainDatasetCount.toString().padStart(2, '0');
    return `CREATE FOLDER {dim.m${dataset}} VISUAL(TITLE "m${dataset}") TYPE ATTRIBUTE;\nCREATE FOLDER {ffld.m${dataset}} VISUAL(TITLE "m${dataset}") TYPE FACT;`;
}

function generateSubFolder(mainDatasetCount, subDatasetCount) {
    const mainDataset = mainDatasetCount.toString().padStart(2, '0');
    const subDataset = subDatasetCount.toString().padStart(2, '0');
    return `CREATE FOLDER {dim.m${mainDataset}s${subDataset}} VISUAL(TITLE "m${mainDataset}s${subDataset}") TYPE ATTRIBUTE;\nCREATE FOLDER {ffld.m${mainDataset}s${subDataset}} VISUAL(TITLE "m${mainDataset}s${subDataset}") TYPE FACT;`;
}

function generateMainDataset(mainDatasetCount) {
    const dataset = mainDatasetCount.toString().padStart(2, '0');
    return `CREATE DATASET {dataset.m${dataset}};`;
}

function generateSubDataset(mainDatasetCount, subDatasetCount) {
    const mainDataset = mainDatasetCount.toString().padStart(2, '0');
    const subDataset = subDatasetCount.toString().padStart(2, '0');
    return `CREATE DATASET {dataset.m${mainDataset}s${subDataset}};`;
}

function generateMainFact(mainDatasetNumber, numberOfFact) {
    const result = [];
    const dataset = mainDatasetNumber.toString().padStart(2, '0');
    for (let i = 1; i <= numberOfFact; i += 1) {
        const fact = i.toString().padStart(2, '0');
        result.push(
            `CREATE FACT {fact.m${dataset}.m${dataset}fact${fact}} VISUAL(FOLDER {ffld.m${dataset}}) AS {f_m${dataset}.f_m${dataset}fact${fact}};`,
            `ALTER DATATYPE {f_m${dataset}.f_m${dataset}fact${fact}} DECIMAL(15,3);`,
            `ALTER DATASET {dataset.m${dataset}} ADD {fact.m${dataset}.m${dataset}fact${fact}};`
        );
    }
    return result.join('\n');
}

function generateSubFact(mainDatasetNumber, subDatasetNumber, numberOfFact) {
    const result = [];
    const mainDataset = mainDatasetNumber.toString().padStart(2, '0');
    const subDataset = subDatasetNumber.toString().padStart(2, '0');
    for (let i = 1; i <= numberOfFact; i += 1) {
        const fact = i.toString().padStart(2, '0');
        result.push(
            `CREATE FACT {fact.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}fact${fact}} VISUAL(FOLDER {ffld.m${mainDataset}s${subDataset}}) AS {f_m${mainDataset}s${subDataset}.f_m${mainDataset}s${subDataset}fact${fact}};`,
            `ALTER DATATYPE {f_m${mainDataset}s${subDataset}.f_m${mainDataset}s${subDataset}fact${fact}} DECIMAL(15,3);`,
            `ALTER DATASET {dataset.m${mainDataset}s${subDataset}} ADD {fact.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}fact${fact}};`
        );
    }
    return result.join('\n');
}

function generateMainAttribute(
    mainDatasetNumber,
    subDatasetCount,
    numberOfAttr,
    isLastDataset
) {
    const result = [];
    const dataset = mainDatasetNumber.toString().padStart(2, '0');

    for (let i = 1; i <= numberOfAttr; i += 1) {
        const attr = i.toString().padStart(2, '0');
        if (i !== 1) {
            if (!isLastDataset && i <= subDatasetCount + 2) {
                // skip connections of all sub dataset + 1 is a connection of main datasetconti
                // eslint-disable-next-line no-continue
                continue;
            }

            if (isLastDataset && i <= subDatasetCount + 1) {
                // skip connections of all sub dataset
                // eslint-disable-next-line no-continue
                continue;
            }

            result.push(
                `CREATE ATTRIBUTE {attr.m${dataset}.m${dataset}attr${attr}} VISUAL(FOLDER {dim.m${dataset}}) AS KEYS {d_m${dataset}_m${dataset}attr${attr}.id} FULLSET, {f_m${dataset}.m${dataset}attr${attr}_id};`,
                `ALTER DATASET {dataset.m${dataset}} ADD {attr.m${dataset}.m${dataset}attr${attr}};`,
                `ALTER ATTRIBUTE {attr.m${dataset}.m${dataset}attr${attr}} ADD LABELS {label.m${dataset}.m${dataset}attr${attr}} AS {d_m${dataset}_m${dataset}attr${attr}.nm_m${dataset}attr${attr}};`,
                `ALTER DATATYPE {d_m${dataset}_m${dataset}attr${attr}.nm_m${dataset}attr${attr}} VARCHAR(255);`
            );
        } else {
            result.push(
                `CREATE ATTRIBUTE {attr.m${dataset}.m${dataset}attr${attr}} VISUAL(FOLDER {dim.m${dataset}}) AS KEYS {f_m${dataset}.id} FULLSET;`,
                `ALTER DATASET {dataset.m${dataset}} ADD {attr.m${dataset}.m${dataset}attr${attr}};`,
                `ALTER ATTRIBUTE {attr.m${dataset}.m${dataset}attr${attr}} ADD LABELS {label.m${dataset}.m${dataset}attr${attr}} AS {f_m${dataset}.nm_m${dataset}attr${attr}};`,
                `ALTER ATTRIBUTE {attr.m${dataset}.m${dataset}attr${attr}} DEFAULT LABEL {label.m${dataset}.m${dataset}attr${attr}};`
            );
        }
    }
    return result.join('\n');
}

function generateSubAttribute(
    mainDatasetNumber,
    subDatasetNumber,
    numberOfAttr
) {
    const result = [];
    const mainDataset = mainDatasetNumber.toString().padStart(2, '0');
    const subDataset = subDatasetNumber.toString().padStart(2, '0');

    for (let i = 1; i <= numberOfAttr; i += 1) {
        const attr = i.toString().padStart(2, '0');
        if (i !== 1) {
            result.push(
                `CREATE ATTRIBUTE {attr.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}} VISUAL(FOLDER {dim.m${mainDataset}s${subDataset}}) AS KEYS {d_m${mainDataset}s${subDataset}_m${mainDataset}s${subDataset}attr${attr}.id} FULLSET, {f_m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}_id};`,
                `ALTER DATASET {dataset.m${mainDataset}s${subDataset}} ADD {attr.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}};`,
                `ALTER ATTRIBUTE {attr.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}} ADD LABELS {label.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}} AS {d_m${mainDataset}s${subDataset}_m${mainDataset}s${subDataset}attr${attr}.nm_m${mainDataset}s${subDataset}attr${attr}};`,
                `ALTER DATATYPE {d_m${mainDataset}s${subDataset}_m${mainDataset}s${subDataset}attr${attr}.nm_m${mainDataset}s${subDataset}attr${attr}} VARCHAR(255);`
            );
        } else {
            result.push(
                `CREATE ATTRIBUTE {attr.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}} VISUAL(FOLDER {dim.m${mainDataset}s${subDataset}}) AS KEYS {f_m${mainDataset}s${subDataset}.id} FULLSET;`,
                `ALTER DATASET {dataset.m${mainDataset}s${subDataset}} ADD {attr.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}};`,
                `ALTER ATTRIBUTE {attr.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}} ADD LABELS {label.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}} AS {f_m${mainDataset}s${subDataset}.nm_m${mainDataset}s${subDataset}attr${attr}};`,
                `ALTER ATTRIBUTE {attr.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}} DEFAULT LABEL {label.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr${attr}};`
            );
        }
    }
    return result.join('\n');
}

function generateMainDatasetConnections(mainDatasetCount) {
    const result = [];
    for (let i = 1; i <= mainDatasetCount; i += 1) {
        const dataset = i.toString().padStart(2, '0');
        if (i !== 1) {
            result.push(
                `ALTER ATTRIBUTE {attr.m${dataset}.m${dataset}attr01} ADD KEYS {f_m${(
                    parseInt(dataset, 10) - 1
                )
                    .toString()
                    .padStart(2, '0')}.m${dataset}_id};`
            );
        }
    }
    return result.join('\n');
}

function generateSubDatasetConnections(mainDatasetCount, subDatasetCount) {
    const result = [];
    for (let i = 1; i <= mainDatasetCount; i += 1) {
        const mainDataset = i.toString().padStart(2, '0');
        for (let j = 1; j <= subDatasetCount; j += 1) {
            const subDataset = j.toString().padStart(2, '0');
            result.push(
                `ALTER ATTRIBUTE {attr.m${mainDataset}s${subDataset}.m${mainDataset}s${subDataset}attr01} ADD KEYS {f_m${mainDataset}.m${mainDataset}s${subDataset}_id};`
            );
        }
    }
    return result.join('\n');
}

function generateMainDateConnections(mainDatasetCount) {
    const result = [];
    for (let i = 1; i <= mainDatasetCount; i += 1) {
        const dataset = i.toString().padStart(2, '0');
        result.push(
            `ALTER ATTRIBUTE {m${dataset}date01.date} ADD KEYS {f_m${dataset}.dt_m${dataset}date01_id};`
        );
    }
    return result.join('\n');
}

function generateSubDateConnections(mainDatasetCount, subDatasetCount) {
    const result = [];
    for (let i = 1; i <= mainDatasetCount; i += 1) {
        const mainDataset = i.toString().padStart(2, '0');
        for (let j = 1; j <= subDatasetCount; j += 1) {
            const subDataset = j.toString().padStart(2, '0');
            result.push(
                `ALTER ATTRIBUTE {m${mainDataset}s${subDataset}date01.date} ADD KEYS {f_m${mainDataset}s${subDataset}.dt_m${mainDataset}s${subDataset}date01_id};`
            );
        }
    }
    return result.join('\n');
}

function generateMainDatasets(
    mainDatasetCount,
    subDatasetCount,
    numberOfAttr,
    numberOfFact
) {
    const result = [];
    for (let i = 1; i <= mainDatasetCount; i += 1) {
        result.push(generateMainDataset(i));
        result.push(generateMainFolder(i));
        result.push(generateMainFact(i, numberOfFact));
        if (i === mainDatasetCount) {
            result.push(
                generateMainAttribute(i, subDatasetCount, numberOfAttr, true)
            );
        } else {
            result.push(
                generateMainAttribute(i, subDatasetCount, numberOfAttr, false)
            );
        }
    }
    return result.join('\n');
}

function generateSubDatasets(
    mainDatasetCount,
    subDatasetCount,
    numberOfAttr,
    numberOfFact
) {
    const result = [];
    for (let i = 1; i <= mainDatasetCount; i += 1) {
        for (let j = 1; j <= subDatasetCount; j += 1) {
            result.push(generateSubDataset(i, j));
            result.push(generateSubFolder(i, j));
            result.push(generateSubFact(i, j, numberOfFact));
            result.push(generateSubAttribute(i, j, numberOfAttr));
        }
    }
    return result.join('\n');
}

function generateSynchronized(mainDatasetCount, subDatasetCount) {
    let listOfDataset = '';
    for (let i = 1; i <= mainDatasetCount; i += 1) {
        listOfDataset += `{dataset.m${i.toString().padStart(2, '0')}}, `;
        for (let j = 1; j <= subDatasetCount; j += 1) {
            listOfDataset += `{dataset.m${i.toString().padStart(2, '0')}s${j
                .toString()
                .padStart(2, '0')}}, `;
        }
    }
    return `SYNCHRONIZE ${listOfDataset.slice(0, -2)};`; // slice here to remove the last comma
}

async function createModelMaqlFile(
    mainDatasetCount,
    subDatasetCount,
    mainAttributeCount,
    subAttributeCount,
    mainFactCount,
    subFactCount
) {
    let fileContent = '';
    fileContent += generateMainDateDimension(mainDatasetCount);
    fileContent += generateSubDateDimension(mainDatasetCount, subDatasetCount);
    fileContent += generateMainDatasets(
        mainDatasetCount,
        subDatasetCount,
        mainAttributeCount,
        mainFactCount
    );
    fileContent += generateSubDatasets(
        mainDatasetCount,
        subDatasetCount,
        subAttributeCount,
        subFactCount
    );
    fileContent += generateMainDatasetConnections(mainDatasetCount);
    fileContent += generateSubDatasetConnections(
        mainDatasetCount,
        subDatasetCount
    );
    fileContent += generateMainDateConnections(mainDatasetCount);
    fileContent += generateSubDateConnections(
        mainDatasetCount,
        subDatasetCount
    );
    fileContent += generateSynchronized(mainDatasetCount, subDatasetCount);

    try {
        await fs.promises.appendFile(filename, fileContent, 'utf8');
        // eslint-disable-next-line no-console
        console.log('Generated model MAQL successfully...');
    } catch (err) {
        // eslint-disable-next-line no-console
        console.error(err);
    }
}

function findLineBreakBefore(text, index) {
    for (let i = index; i >= 0; i -= 1) {
        if (text[i] === '\n') {
            return i;
        }
    }
    return -1;
}

async function splitFileIntoSmallerFiles(filePath) {
    const maxFileSize = 200000;

    fs.readFile(filePath, 'utf8', async (err, text) => {
        if (err) {
            return;
        }

        const numberOfFiles = Math.ceil(text.length / maxFileSize);
        if (numberOfFiles === 1) {
            return;
        }

        let lastIndex = 0;
        for (let i = 1; i <= numberOfFiles; i += 1) {
            const startIndex = lastIndex;
            const endIndex = startIndex + maxFileSize;

            const lineBreakIndex = findLineBreakBefore(text, endIndex);
            if (lineBreakIndex < endIndex && i !== numberOfFiles) {
                lastIndex = lineBreakIndex + 1;
            } else {
                lastIndex = endIndex;
            }

            const smallerFileContent = text.slice(startIndex, lastIndex);

            const fileName = `${basePath}/model.maql_${i}`;
            fs.writeFileSync(fileName, smallerFileContent);
            // eslint-disable-next-line no-console
            console.log(`File ${fileName} written.`);
        }
    });
}

async function splitModelMaqlFile() {
    await splitFileIntoSmallerFiles(filename);
}

module.exports = {
    createModelMaqlFile,
    splitModelMaqlFile
};
