import * as yaml from 'js-yaml';
import moment from 'moment';
import * as fs from 'fs';
import path from 'path';
const FIELDS_WITH_VALUES_TO_FORMAT = [
    "actionType",
    "fieldType",
    "inputsOnNextNavToAssocScrn",
    "processType",
    "recordTriggerType",
    "triggerType",
    "regionContainerType",
    "runInMode",
    "type"
];
const FIELDS_WITH_VALUES_TO_FORMAT_ENUM = {
    "status": {
        "Draft": "⚠️ Draft",
        "Inactive": "⚠️ Inactive",
        "InvalidDraft": "⚠️ Invalid Draft"
    }
};
const FIELDS_PREFERRED_ORDER_START = [
    "type",
    "object",
    "processType",
    "triggerType",
    "recordTriggerType",
    "label",
    "status",
    "actionType",
    "actionName",
    "dataType",
    "objectType"
];
const FIELDS_PREFERRED_ORDER_END = [
    "connector",
    "nextNode",
    "noMoreValuesConnector",
    "conditionLogic",
    "filterLogic",
];
const FIELDS_WITH_COLUMN_CENTERED = [
    "dataType",
    "objectType",
    "operator",
    "isCollection",
    "isInput",
    "isOutput",
    "rightValue",
    "startDate",
    "startTime",
    "value"
];
export function simplifyNode(flowNode) {
    const nodeCopy = Object.assign({}, flowNode);
    for (const nodeKey of Object.keys(nodeCopy)) {
        if (["locationX", "locationY"].includes(nodeKey)) {
            delete nodeCopy[nodeKey];
        }
        else if (nodeCopy[nodeKey] === null || nodeCopy[nodeKey] === undefined) {
            delete nodeCopy[nodeKey];
        }
    }
    return nodeCopy;
}
export function flowNodeToMarkdown(flowNodeIn, allProperties) {
    const flowNode = Object.assign({}, flowNodeIn);
    delete flowNode["name"];
    const additionalTables = [];
    // Properties that can be found on multiple flow node types
    handleprocessMetadataValues(flowNode, allProperties);
    handleInputParameters(flowNode, allProperties);
    const conditionsTable = handleConditions(flowNode, allProperties);
    additionalTables.push(conditionsTable);
    const filterItemsTable = handleFilterItems(flowNode, allProperties);
    additionalTables.push(filterItemsTable);
    const inputAssignmentsTable = handleInputAssignments(flowNode, allProperties);
    additionalTables.push(inputAssignmentsTable);
    const assignmentItemsTable = handleAssignmentItems(flowNode, allProperties);
    additionalTables.push(assignmentItemsTable);
    const scheduledPathsTable = handleScheduledPaths(flowNode, allProperties);
    additionalTables.push(scheduledPathsTable);
    // Special case of decisions
    if (flowNode.type === "decisions") {
        const rules = getElementAsArray(flowNode, "rules");
        delete flowNode.rules;
        delete flowNode.rules2;
        for (const rule of rules) {
            const ruleNode = Object.assign({}, rule);
            delete ruleNode.name;
            delete ruleNode.label;
            const ruleConditionsTable = handleConditions(ruleNode, allProperties);
            const ruleTable = buildGenericMarkdownTable(ruleNode, ["allFields"], `#### Rule ${rule.name} (${rule.label})`, allProperties);
            additionalTables.push(...[ruleTable, mdEndSection(ruleConditionsTable)]);
        }
    }
    // Special case of transforms
    else if (flowNode.type === "transforms") {
        const transformValues = flowNode.transformValues || {};
        const transformValueActions = getElementAsArray(transformValues, "transformValueActions");
        delete flowNode.transformValues;
        const transformsTable = buildCustomMarkdownTable(transformValueActions, ["transformType", "value", "outputFieldApiName"], "#### Transform actions", allProperties);
        additionalTables.push(transformsTable);
    }
    else if (flowNode.type === "screens") {
        handleFields(flowNode, allProperties, "", additionalTables);
    }
    else if (flowNode.type === "start") {
        delete flowNode.type;
    }
    // Build final markdown for Node
    let table = buildGenericMarkdownTable(flowNode, ["allFields"], "", allProperties);
    for (const additionalTable of additionalTables) {
        if (additionalTable !== "") {
            table += additionalTable + "\n\n";
        }
    }
    return mdEndSection(table);
}
function handleFields(flowNode, allProperties, parentField = "", additionalTables) {
    const fields = getElementAsArray(flowNode, "fields");
    delete flowNode.fields;
    for (const field of fields) {
        const fieldNode = Object.assign({}, field);
        const fieldName = "" + (field.name || field.objectFieldReference);
        delete fieldNode.name;
        if (parentField) {
            fieldNode.parentField = parentField;
            allProperties.push(parentField);
        }
        handleInputParameters(fieldNode, allProperties);
        const fieldsBefore = getElementAsArray(fieldNode, "fields");
        delete fieldNode.fields;
        const fieldTable = buildGenericMarkdownTable(fieldNode, ["allFields"], `#### ${fieldName}`, allProperties);
        // Handle recursive loop
        if (fieldsBefore) {
            fieldNode.name = fieldName;
            fieldNode.fields = fieldsBefore;
            handleFields(fieldNode, allProperties, fieldName, additionalTables);
            additionalTables.push(...[mdEndSection(fieldTable)]);
        }
    }
}
function handleConditions(ruleNode, allProperties) {
    const conditions = getElementAsArray(ruleNode, "conditions");
    if (conditions.length === 0) {
        return "";
    }
    let id = 0;
    const conditionsValues = conditions.map((item) => {
        id++;
        return {
            conditionId: id,
            leftValueReference: item.leftValueReference,
            operator: stringifyOperator(item.operator),
            rightValue: (item.operator === "IsNull" && item.rightValue === "false") ? "<!-- -->" : stringifyValue(item.rightValue, "", allProperties)
        };
    });
    delete ruleNode.conditions;
    /* let descriptiveLine = "";
    if (ruleNode.conditionLogic) {
      descriptiveLine += "\n\nConditions logic: **" + ruleNode.conditionLogic + "**\n\n";
      delete ruleNode.conditionLogic;
    } */
    return buildCustomMarkdownTable(conditionsValues, ["conditionId", "leftValueReference", "operator", "rightValue"], "", allProperties);
}
function handleInputAssignments(flowNode, allProperties) {
    const inputAssignmentsItems = getElementAsArray(flowNode, "inputAssignments");
    if (inputAssignmentsItems.length === 0) {
        return "";
    }
    const inputAssignmentsItemsValues = inputAssignmentsItems.map((item) => {
        return {
            field: item.field,
            value: stringifyValue(item.value, item.field, allProperties)
        };
    });
    delete flowNode.inputAssignments;
    return buildCustomMarkdownTable(inputAssignmentsItemsValues, ["field", "value"], "#### Input Assignments", allProperties);
}
export function handleSchedule(flowNode, allProperties) {
    const scheduleItems = getElementAsArray(flowNode, "schedule");
    if (scheduleItems.length === 0) {
        return "";
    }
    const scheduleItemsValues = scheduleItems.map((item) => {
        const startDateFormatted = moment(item.startDate).format("ll");
        const startTimeFormatted = item?.startTime?.endsWith("Z") ? item.startTime.slice(0, 5) : item.startTime;
        return {
            frequency: item.frequency,
            startDate: !startDateFormatted.includes("Invalid") ? startDateFormatted : item.startDate,
            startTime: !startTimeFormatted.includes("Invalid") ? startTimeFormatted : item.startTime,
        };
    });
    delete flowNode.schedule;
    return buildCustomMarkdownTable(scheduleItemsValues, ["frequency", "startDate", "startTime"], "#### Schedules", allProperties);
}
export function handleFilterItems(flowNode, allProperties) {
    const filterItems = getElementAsArray(flowNode, "filters");
    if (filterItems.length === 0) {
        return "";
    }
    let id = 0;
    const filterItemsValues = filterItems.map((item) => {
        id++;
        return {
            filterId: id,
            field: item.field,
            operator: stringifyOperator(item.operator),
            value: item.operator === "IsNull" ? "<!-- -->" : stringifyValue(item.value, item.field, allProperties)
        };
    });
    delete flowNode.filters;
    let descriptiveLine = "";
    if (flowNode.filterLogic) {
        descriptiveLine += " (logic: **" + flowNode.filterLogic + "**)";
        delete flowNode.filterLogic;
    }
    return buildCustomMarkdownTable(filterItemsValues, ["filterId", "field", "operator", "value"], "#### Filters" + descriptiveLine, allProperties);
}
function handleAssignmentItems(flowNode, allProperties) {
    const assignmentItems = getElementAsArray(flowNode, "assignmentItems");
    if (assignmentItems.length === 0) {
        return "";
    }
    const assignmentItemsValues = assignmentItems.map((item) => {
        const value = item?.value?.elementReference || stringifyValue(item.value, "", allProperties);
        return {
            assignToReference: item.assignToReference,
            operator: stringifyOperator(item.operator),
            value: stringifyValue(value, "", allProperties)
        };
    });
    delete flowNode.assignmentItems;
    return buildCustomMarkdownTable(assignmentItemsValues, ["assignToReference", "operator", "value"], "#### Assignments", allProperties);
}
export function handleScheduledPaths(flowNode, allProperties) {
    const scheduledPaths = getElementAsArray(flowNode, "scheduledPaths");
    delete flowNode.scheduledPaths;
    if (scheduledPaths.length === 0) {
        return "";
    }
    return buildCustomMarkdownTable(scheduledPaths, ["label", "name", "offsetNumber", "offsetUnit", "recordField", "timeSource", "connector"], "#### Scheduled Paths", allProperties);
}
export function handleInputParameters(flowNode, allProperties) {
    const inputParameters = getElementAsArray(flowNode, "inputParameters");
    for (const inputParam of inputParameters) {
        const inputParamName = `${inputParam.name} (input)`;
        flowNode[inputParamName] = stringifyValue(inputParam.value, inputParam.name, allProperties);
    }
    delete flowNode.inputParameters;
}
export function handleprocessMetadataValues(flowNode, allProperties) {
    const metadataValues = {};
    const processMetadataValues = getElementAsArray(flowNode, "processMetadataValues");
    for (const processMetadataValue of processMetadataValues) {
        const inputParamName = `${processMetadataValue.name} (PM)`;
        flowNode[inputParamName] = stringifyValue(processMetadataValue.value, processMetadataValue.name, allProperties);
        metadataValues[processMetadataValue.name] = flowNode[inputParamName];
    }
    delete flowNode.processMetadataValues;
    return metadataValues;
}
export function buildGenericMarkdownTable(item, fields, title = "", allProperties) {
    if (fields[0] === "allFields") {
        fields = Object.keys(item);
        // Reorder fields according to preferences
        const fieldOrderFromStart = FIELDS_PREFERRED_ORDER_START.slice().reverse();
        for (const field of fieldOrderFromStart) {
            if (fields.includes(field)) {
                fields.splice(fields.indexOf(field), 1);
                fields.unshift(field);
            }
        }
        for (const field of FIELDS_PREFERRED_ORDER_END) {
            if (fields.includes(field)) {
                fields.splice(fields.indexOf(field), 1);
                fields.push(field);
            }
        }
    }
    // Add link to Apex class doc if existing
    if (item?.actionType === "apex" && item.actionName && fs.existsSync(path.join("docs", "apex", `${item.actionName}.md`))) {
        item.actionName = `[${item.actionName}](../apex/${item.actionName}.md)`;
    }
    // Add link to SObject doc if existing
    if (item?.sobjectType && fs.existsSync(path.join("docs", "objects", `${item.sobjectType}.md`))) {
        item.sobjectType = `[${item.sobjectType}](../objects/${item.sobjectType}.md)`;
    }
    let table = title ? `${title}\n\n` : '';
    table += `|<!-- -->|<!-- -->|\n|:---|:---|\n`;
    for (const field of fields) {
        if (item[field] !== undefined && item[field] !== null) {
            table += `|${prettifyFieldName(field)}|${stringifyValue(item[field], field, allProperties)}|\n`;
        }
    }
    return table + "\n\n";
}
export function buildCustomMarkdownTable(items, fields, title = "", allProperties) {
    let table = title ? `${title}\n\n` : '';
    table += "|" + fields.map(field => prettifyFieldName(field)).join("|") + "|\n";
    table += "|" + fields.map(field => FIELDS_WITH_COLUMN_CENTERED.includes(field) ? ":--:" : ":-- ").join("|") + " |\n";
    for (const item of items) {
        const fieldValues = fields.map(field => stringifyValue(item[field], field, allProperties));
        table += "|" + fieldValues.join("|") + "|\n";
    }
    return table + "\n\n";
}
export function stringifyOperator(operatorIn) {
    return prettifyFieldName(operatorIn);
}
export function stringifyValue(valueIn, field, allProperties) {
    const valueType = typeof valueIn;
    // String
    let valueStringified = valueType === "string" ?
        valueIn.split("\n").join("<br/>") :
        // String value
        (valueType === "object" && valueIn.stringValue && Object.keys(valueIn).length === 1) ?
            valueIn.stringValue :
            // Boolean value
            (valueType === "object" && (valueIn.booleanValue !== undefined) && Object.keys(valueIn).length === 1) ?
                valueIn.booleanValue :
                // Number value
                (valueType === "object" && valueIn.numberValue && Object.keys(valueIn).length === 1) ?
                    valueIn.numberValue :
                    // Target reference
                    (valueType === "object" && valueIn.targetReference && Object.keys(valueIn).length === 1) ?
                        valueIn.targetReference :
                        // Element reference
                        (valueType === "object" && valueIn.elementReference && Object.keys(valueIn).length === 1) ?
                            valueIn.elementReference :
                            // Element reference
                            (valueType === "object" && field === 'template' && valueIn?.name) ?
                                valueIn.name :
                                // Undefined or empty array or empty object
                                (valueType === "undefined" || (Array.isArray(valueIn) && valueIn.length === 0) || (valueType === "object" && Object.keys(valueIn).length === 0)) ?
                                    '<!-- -->' :
                                    // Default YAML for array & object
                                    (Array.isArray(valueIn) || valueType === "object") ?
                                        yaml.dump(valueIn).replace(/"/gm, "").replace(/^(\s+)/gm, match => '&nbsp;'.repeat(match.length)).split("\n").join("<br/>") :
                                        // Default
                                        String(valueIn).split("\n").join("<br/>");
    // Final updates if necessary
    if (allProperties.includes(valueStringified)) {
        valueStringified = `[${valueStringified}](#${valueStringified.toLowerCase()})`;
    }
    else if (FIELDS_WITH_VALUES_TO_FORMAT_ENUM[field] && FIELDS_WITH_VALUES_TO_FORMAT_ENUM[field][valueStringified]) {
        valueStringified = FIELDS_WITH_VALUES_TO_FORMAT_ENUM[field][valueStringified];
    }
    else if (FIELDS_WITH_VALUES_TO_FORMAT.includes(field)) {
        valueStringified = prettifyFieldName(valueStringified);
        if (field === "type" && valueStringified.endsWith("s")) {
            valueStringified = valueStringified.slice(0, -1);
        }
    }
    else {
        valueStringified = valueStringified === "true" ? "✅" : valueStringified === "false" ? "⬜" : valueStringified;
    }
    return valueStringified;
}
export function prettifyFieldName(field) {
    return field.replace(/([A-Z])/g, " $1").replace(/^./, str => str.toUpperCase()).replace("( P M)", "(PM)").replace("S Object", "SObject");
}
export function mdEndSection(sectionString) {
    if (!sectionString)
        return sectionString + "\n\n___\n\n";
    return sectionString;
}
export function getElementAsArray(node, key) {
    return Array.isArray(node[key]) ? node[key] : typeof node[key] === "object" ? [node[key]] : [];
}
//# sourceMappingURL=nodeFormatUtils.js.map