import { splitSpace, isSpace } from '../../util.js';
const isQuoted = (s) => s && s.startsWith('"') && s.endsWith('"');
/**
 * Splits remaining `spec.lines[].tokens.description` into `name` and `descriptions` tokens,
 * and populates the `spec.name`
 */
export default function nameTokenizer() {
    const typeEnd = (num, { tokens }, i) => tokens.type === '' ? num : i;
    return (spec) => {
        // look for the name starting in the line where {type} ends
        let finalTypeLine = spec.source.reduce(typeEnd, 0);
        let tokens;
        if (spec.type) {
            do {
                ({ tokens } = spec.source[finalTypeLine]);
                if (tokens.description.trim()) {
                    break;
                }
                finalTypeLine++;
            } while (spec.source[finalTypeLine]);
        }
        else {
            ({ tokens } = spec.source[finalTypeLine]);
        }
        const source = tokens.description.trimStart();
        const quotedGroups = source.split('"');
        // if it starts with quoted group, assume it is a literal
        if (quotedGroups.length > 1 &&
            quotedGroups[0] === '' &&
            quotedGroups.length % 2 === 1) {
            spec.name = quotedGroups[1];
            tokens.name = `"${quotedGroups[1]}"`;
            [tokens.postName, tokens.description] = splitSpace(source.slice(tokens.name.length));
            return spec;
        }
        let brackets = 0;
        let name = '';
        let optional = false;
        let defaultValue;
        // assume name is non-space string or anything wrapped into brackets
        for (const ch of source) {
            if (brackets === 0 && isSpace(ch))
                break;
            if (ch === '[')
                brackets++;
            if (ch === ']')
                brackets--;
            name += ch;
        }
        if (brackets !== 0) {
            spec.problems.push({
                code: 'spec:name:unpaired-brackets',
                message: 'unpaired brackets',
                line: spec.source[0].number,
                critical: true,
            });
            return spec;
        }
        const nameToken = name;
        if (name[0] === '[' && name[name.length - 1] === ']') {
            optional = true;
            name = name.slice(1, -1);
            const parts = name.split('=');
            name = parts[0].trim();
            if (parts[1] !== undefined)
                defaultValue = parts.slice(1).join('=').trim();
            if (name === '') {
                spec.problems.push({
                    code: 'spec:name:empty-name',
                    message: 'empty name',
                    line: spec.source[0].number,
                    critical: true,
                });
                return spec;
            }
            if (defaultValue === '') {
                spec.problems.push({
                    code: 'spec:name:empty-default',
                    message: 'empty default value',
                    line: spec.source[0].number,
                    critical: true,
                });
                return spec;
            }
            // has "=" and is not a string, except for "=>"
            if (!isQuoted(defaultValue) && /=(?!>)/.test(defaultValue)) {
                spec.problems.push({
                    code: 'spec:name:invalid-default',
                    message: 'invalid default value syntax',
                    line: spec.source[0].number,
                    critical: true,
                });
                return spec;
            }
        }
        if (!optional) {
            const eqIndex = name.search(/=(?!>)/);
            if (eqIndex !== -1) {
                defaultValue = name.slice(eqIndex + 1).trim();
                name = name.slice(0, eqIndex).trim();
                if (name === '') {
                    spec.problems.push({
                        code: 'spec:name:empty-name',
                        message: 'empty name',
                        line: spec.source[0].number,
                        critical: true,
                    });
                    return spec;
                }
                if (defaultValue === '') {
                    spec.problems.push({
                        code: 'spec:name:empty-default',
                        message: 'empty default value',
                        line: spec.source[0].number,
                        critical: true,
                    });
                    return spec;
                }
                if (!isQuoted(defaultValue) && /=(?!>)/.test(defaultValue)) {
                    spec.problems.push({
                        code: 'spec:name:invalid-default',
                        message: 'invalid default value syntax',
                        line: spec.source[0].number,
                        critical: true,
                    });
                    return spec;
                }
            }
        }
        spec.optional = optional;
        spec.name = name;
        tokens.name = nameToken;
        if (defaultValue !== undefined)
            spec.default = defaultValue;
        [tokens.postName, tokens.description] = splitSpace(source.slice(tokens.name.length));
        return spec;
    };
}
