import { createAggregateError } from './errors';
import { parse, ParseError, ParseErrorCode } from 'jsonc-parser';
import { ErrorLike, asError } from '@workbench-stack/extension-api';


/**
 * Parses the JSON input using an error-tolerant "JSONC" parser. If an error occurs, it is returned as a value
 * instead of being thrown. This is useful when input is parased in the background (not in response to any specific
 * user action), because it makes it easier to save the error and only show it to the user when it matters (for
 * some interactive user action).
 */
export function parseJSONOrError<T>(input: string): T | ErrorLike {
    try {
        return parseJSON(input) as T;
    } catch (err) {
        return asError(err);
    }
}

/**
 * Parses the JSON input using an error-tolerant "JSONC" parser.
 */
function parseJSON(input: string): any {
    const errors: ParseError[] = [];
    const o = parse(input, errors, { allowTrailingComma: true, disallowComments: false });
    if (errors.length > 0) {
        throw createAggregateError(
            errors.map(v => ({
                ...v,
                code: printParseErrorCode[v.error],
                message: `parse error (code: ${v.error}, offset: ${v.offset}, length: ${v.length})`,
            })),
        );
    }
}


// until new version of jsonc-parser release
// https://github.com/Microsoft/node-jsonc-parser/issues/14

export function printParseErrorCode(code: ParseErrorCode) {
    switch (code) {
        case ParseErrorCode.InvalidSymbol: return 'InvalidSymbol';
        case ParseErrorCode.InvalidNumberFormat: return 'InvalidNumberFormat';
        case ParseErrorCode.PropertyNameExpected: return 'PropertyNameExpected';
        case ParseErrorCode.ValueExpected: return 'ValueExpected';
        case ParseErrorCode.ColonExpected: return 'ColonExpected';
        case ParseErrorCode.CommaExpected: return 'CommaExpected';
        case ParseErrorCode.CloseBraceExpected: return 'CloseBraceExpected';
        case ParseErrorCode.CloseBracketExpected: return 'CloseBracketExpected';
        case ParseErrorCode.EndOfFileExpected: return 'EndOfFileExpected';
        case ParseErrorCode.InvalidCommentToken: return 'InvalidCommentToken';
        case ParseErrorCode.UnexpectedEndOfComment: return 'UnexpectedEndOfComment';
        case ParseErrorCode.UnexpectedEndOfString: return 'UnexpectedEndOfString';
        case ParseErrorCode.UnexpectedEndOfNumber: return 'UnexpectedEndOfNumber';
        case ParseErrorCode.InvalidUnicode: return 'InvalidUnicode';
        case ParseErrorCode.InvalidEscapeCharacter: return 'InvalidEscapeCharacter';
        case ParseErrorCode.InvalidCharacter: return 'InvalidCharacter';
    }
    return '<unknown ParseErrorCode>';
}
