import * as util from 'util';
import { Lang, NamingVariableConventions, VariableType } from './constants.js';
import StringUtility from './string-utility.js';
const langToVarNameConvention = {
    [Lang.NODEJS]: NamingVariableConventions.CAMEL_CASE,
    [Lang.JAVASCRIPT]: NamingVariableConventions.CAMEL_CASE,
    [Lang.PYTHON]: NamingVariableConventions.UNDER_SCORES,
    [Lang.JAVA8]: NamingVariableConventions.CAMEL_CASE,
    [Lang.JAVA]: NamingVariableConventions.CAMEL_CASE,
    [Lang.CSHARP]: NamingVariableConventions.PASCAL_CASE,
    [Lang.CSHARPNEWTONSOFT]: NamingVariableConventions.PASCAL_CASE,
    [Lang.CURL]: NamingVariableConventions.CAMEL_CASE,
    [Lang.GO]: NamingVariableConventions.CAMEL_CASE,
    [Lang.PHP]: NamingVariableConventions.CAMEL_CASE,
    [Lang.RUBY]: NamingVariableConventions.UNDER_SCORES,
    [Lang.R]: NamingVariableConventions.UNDER_SCORES,
};
const langOptions = {
    default: {
        joinSymbol: '',
        variableWrapperFn: (v) => v,
        stringLiteral: (t) => t,
        variableDeclaration: (n) => n,
        urlFieldName: 'url',
    },
    [Lang.CSHARP]: {
        joinSymbol: ' + ',
        stringLiteral: (t) => `"${t}"`,
        variableDeclaration: (n) => `var ${n} = %s;`,
        urlFieldName: 'fullUrl',
    },
    [Lang.CSHARPNEWTONSOFT]: {
        joinSymbol: ' + ',
        stringLiteral: (t) => `"${t}"`,
        variableDeclaration: (n) => `var ${n} = %s;`,
        urlFieldName: 'fullUrl',
    },
    [Lang.GO]: {
        joinSymbol: ' + ',
        stringLiteral: (t) => `"${t}"`,
        variableDeclaration: (n) => `${n} := %s;`,
    },
    [Lang.JAVA]: {
        joinSymbol: ' + ',
        stringLiteral: (t) => `"${t}"`,
        variableDeclaration: (n) => `var ${n} = %s;`,
        urlFieldName: 'pathname',
    },
    [Lang.JAVA8]: {
        joinSymbol: ' + ',
        stringLiteral: (t) => `"${t}"`,
        variableDeclaration: (n) => `String ${n} = %s;`,
        urlFieldName: 'fullUrl',
    },
    [Lang.JAVASCRIPT]: {
        variableWrapperFn: (v) => '${' + v + '}',
        variableDeclaration: (n, options) => `const ${n} = ${options?.hasVariableInterpolation ? '`%s`' : "'%s'"};`,
    },
    [Lang.NODEJS]: {
        variableWrapperFn: (v) => '${' + v + '}',
        variableDeclaration: (n, options) => `const ${n} = ${options?.hasVariableInterpolation ? '`%s`' : "'%s'"};`,
    },
    [Lang.PHP]: {
        joinSymbol: ' . ',
        stringLiteral: (t) => `"${t}"`,
        variableDeclaration: (n) => `const ${n} = %s;`,
    },
    [Lang.PYTHON]: {
        stringLiteral: (t) => `"${t}"`,
        joinSymbol: ' + ',
        variableDeclaration: (n) => `${n} = %s`,
    },
    [Lang.RUBY]: {
        joinSymbol: ' + ',
        stringLiteral: (t) => `'${t}'`,
        variableDeclaration: (n) => `${n} = %s`,
    },
    [Lang.CURL]: {
        variableWrapperFn: (name, value) => value || `{${name}}`,
        urlFieldName: 'fullUrl',
        rawName: true,
    },
    [Lang.R]: {
        joinSymbol: ' %&% ',
        variableDeclaration: (n) => `${n} = %s`,
        urlFieldName: 'fullUrl',
        stringLiteral: (t) => `"${t}"`,
        variableWrapperFn: (name) => `${name}`,
    },
};
const reservedVariableNames = [
    'response',
    'request',
    'requests',
    'data',
    'url',
    'payload',
    'main',
    'accessToken',
    'resp',
    'Program',
    'client',
    'postData',
    'App',
    'httpClient',
    'params',
    'pathname',
    'host',
    'body',
    'fetch',
    'query',
];
/**
 * Helper object to format and aggragate lines of code.
 * Lines are aggregated in a `code` array, and need to be joined to obtain a proper code snippet.
 *
 * @param {string} indentation Desired indentation character for aggregated lines of code
 * @param {string} lineJoin Desired character to join each line of code
 */
export class CodeBuilder {
    code;
    indentation;
    variablesPrefix;
    lineJoin;
    capitalize;
    lang;
    langOptions;
    constructor({ indentation, lineJoin = '\n', variablesPrefix = '', capitalize = false, lang = Lang.CURL, }) {
        this.code = [];
        this.indentation = indentation;
        this.variablesPrefix = variablesPrefix;
        this.lineJoin = lineJoin || '\n';
        this.capitalize = capitalize;
        this.lang = lang;
        this.langOptions = {
            ...langOptions.default,
            ...langOptions[lang],
        };
    }
    /**
     * Add given indentation level to given string and format the string (variadic)
     * @param {number} [indentationLevel=0] - Desired level of indentation for this line
     * @param {string} line - Line of code. Can contain formatting placeholders
     * @param rest
     * @param {...object} - Parameter to bind to `line`'s formatting placeholders
     * @return {string}
     *
     * @example
     *   const builder = CodeBuilder('\t')
     *
     *   builder.buildLine('console.log("hello world")')
     *   // returns: 'console.log("hello world")'
     *
     *   builder.buildLine(2, 'console.log("hello world")')
     *   // returns: 'console.log("\t\thello world")'
     *
     *   builder.buildLine(2, 'console.log("%s %s")', 'hello', 'world')
     *   // returns: 'console.log("\t\thello world")'
     */
    buildLine(indentationLevel, line) {
        let lineIndentation = '';
        let slice = 2;
        if (typeof indentationLevel === 'string') {
            slice = 1;
            line = indentationLevel;
            indentationLevel = 0;
        }
        else if (indentationLevel === null) {
            return null;
        }
        while (indentationLevel) {
            lineIndentation += this.indentation;
            indentationLevel--;
        }
        // eslint-disable-next-line prefer-rest-params
        const format = Array.prototype.slice.call(arguments, slice, arguments.length);
        format.unshift(lineIndentation + line);
        return util.format.apply(this, format);
    }
    /**
     * Invoke buildLine() and add the line at the top of current lines
     * @return {this}
     * @param args
     */
    unshift(...args) {
        // @ts-ignore
        // eslint-disable-next-line prefer-spread
        this.code.unshift(this.buildLine.apply(this, args));
        return this;
    }
    /**
     * Invoke buildLine() and add the line at the bottom of current lines
     * @return {this}
     * @param args
     */
    push(...args) {
        // @ts-ignore
        // eslint-disable-next-line prefer-spread
        this.code.push(this.buildLine.apply(this, args));
        return this;
    }
    /**
     * Add an empty line at the end of current lines
     * @return {this}
     */
    blank() {
        this.code.push(null);
        return this;
    }
    /**
     * Concatenate all current lines using the given lineJoin
     * @return {string}
     */
    join() {
        return this.code.join(this.lineJoin);
    }
    clean() {
        this.code.splice(1);
    }
    /**
     * Get variable name, use variablesPrefix if specified
     * @param {string} name
     * @param {VariableType} type
     * @return {string}
     */
    var(name, type = VariableType.CUSTOM) {
        if (type === VariableType.PATH) {
            let value = this.variablesPrefix + name;
            if (reservedVariableNames.includes(value)) {
                value = `${value}_path`;
            }
            switch (langToVarNameConvention[this.lang]) {
                case NamingVariableConventions.UNDER_SCORES:
                    value = StringUtility.toSnakeCase(value);
                    break;
                case NamingVariableConventions.PASCAL_CASE:
                    value = StringUtility.toPascalCase(value);
                    break;
                case NamingVariableConventions.CAMEL_CASE:
                default:
                    value = StringUtility.toCamelCase(value);
                    break;
            }
            return value;
        }
        // TODO remove, once we refactor all variables in code-samples
        return this.variablesPrefix
            ? this.variablesPrefix + (this.capitalize ? StringUtility.toPascalCase(name) : name)
            : name;
    }
}
//# sourceMappingURL=code-builder.js.map