import CypherLexer from './generated-parser/CypherCmdLexer';

export enum CypherTokenType {
  comment = 'comment',
  keyword = 'keyword',
  label = 'label',
  predicateFunction = 'predicateFunction',
  function = 'function',
  procedure = 'procedure',
  variable = 'variable',
  paramDollar = 'paramDollar',
  paramValue = 'paramValue',
  symbolicName = 'symbolicName',
  operator = 'operator',
  stringLiteral = 'stringLiteral',
  numberLiteral = 'numberLiteral',
  booleanLiteral = 'booleanLiteral',
  keywordLiteral = 'keywordLiteral',
  property = 'property',
  namespace = 'namespace',
  bracket = 'bracket',
  separator = 'separator',
  punctuation = 'punctuation',
  none = 'none',
  consoleCommand = 'consoleCommand',
}

export const lexerOperators = [
  CypherLexer.AMPERSAND,
  CypherLexer.BAR,
  CypherLexer.COLON,
  CypherLexer.COLONCOLON,
  CypherLexer.DIVIDE,
  CypherLexer.DOLLAR,
  CypherLexer.DOT,
  CypherLexer.DOTDOT,
  CypherLexer.DOUBLEBAR,
  CypherLexer.EQ,
  CypherLexer.EXCLAMATION_MARK,
  CypherLexer.GE,
  CypherLexer.GT,
  CypherLexer.LE,
  CypherLexer.LT,
  CypherLexer.MINUS,
  CypherLexer.NEQ,
  CypherLexer.INVALID_NEQ,
  CypherLexer.PERCENT,
  CypherLexer.PLUS,
  CypherLexer.PLUSEQUAL,
  CypherLexer.POW,
  CypherLexer.QUESTION,
  CypherLexer.REGEQ,
  CypherLexer.TIMES,
];

export const lexerBrackets = [
  CypherLexer.LBRACKET,
  CypherLexer.LCURLY,
  CypherLexer.LPAREN,
  CypherLexer.RBRACKET,
  CypherLexer.RCURLY,
  CypherLexer.RPAREN,
];
export const lexerPunctuation = [CypherLexer.SEMICOLON];
export const lexerSeparators = [
  CypherLexer.COMMA,
  CypherLexer.ARROW_LINE,
  CypherLexer.ARROW_LEFT_HEAD,
  CypherLexer.ARROW_RIGHT_HEAD,
];
export const lexerNumberLiterals = [
  CypherLexer.DECIMAL_DOUBLE,
  CypherLexer.UNSIGNED_DECIMAL_INTEGER,
  CypherLexer.UNSIGNED_HEX_INTEGER,
  CypherLexer.UNSIGNED_OCTAL_INTEGER,
];

export const lexerStringLiteral = [
  CypherLexer.STRING_LITERAL1,
  CypherLexer.STRING_LITERAL2,
];

export const lexerGarbage = [
  CypherLexer.ErrorChar,
  CypherLexer.EOF,
  CypherLexer.SPACE,
];

export const identifier = [
  CypherLexer.IDENTIFIER,
  CypherLexer.ESCAPED_SYMBOLIC_NAME,
];

export const lexerComment = [
  CypherLexer.MULTI_LINE_COMMENT,
  CypherLexer.SINGLE_LINE_COMMENT,
];

export const lexerKeywords = [
  CypherLexer.ACCESS,
  CypherLexer.ACTIVE,
  CypherLexer.ADMIN,
  CypherLexer.ADMINISTRATOR,
  CypherLexer.ALIAS,
  CypherLexer.ALIASES,
  CypherLexer.ALL,
  CypherLexer.ALL_SHORTEST_PATHS,
  CypherLexer.ALTER,
  CypherLexer.AND,
  CypherLexer.ANY,
  CypherLexer.ARRAY,
  CypherLexer.AS,
  CypherLexer.ASC,
  CypherLexer.ASCENDING,
  CypherLexer.ASSERT,
  CypherLexer.ASSIGN,
  CypherLexer.AT,
  CypherLexer.BINDINGS,
  CypherLexer.BOOL,
  CypherLexer.BOOLEAN,
  CypherLexer.BOOSTED,
  CypherLexer.BOTH,
  CypherLexer.BREAK,
  CypherLexer.BRIEF,
  CypherLexer.BTREE,
  CypherLexer.BUILT,
  CypherLexer.BY,
  CypherLexer.CALL,
  CypherLexer.CASE,
  CypherLexer.CIDR,
  CypherLexer.CHANGE,
  CypherLexer.COLLECT,
  CypherLexer.COMMAND,
  CypherLexer.COMMANDS,
  CypherLexer.COMMIT,
  CypherLexer.COMPOSITE,
  CypherLexer.CONSTRAINT,
  CypherLexer.CONSTRAINTS,
  CypherLexer.CONTAINS,
  CypherLexer.CONTINUE,
  CypherLexer.COPY,
  CypherLexer.COUNT,
  CypherLexer.CREATE,
  CypherLexer.CSV,
  CypherLexer.CONCURRENT,
  CypherLexer.CURRENT,
  CypherLexer.DATA,
  CypherLexer.DATABASE,
  CypherLexer.DATABASES,
  CypherLexer.DATE,
  CypherLexer.DATETIME,
  CypherLexer.DBMS,
  CypherLexer.DEALLOCATE,
  CypherLexer.DEFAULT,
  CypherLexer.DEFINED,
  CypherLexer.DELETE,
  CypherLexer.DENY,
  CypherLexer.DESC,
  CypherLexer.DESCENDING,
  CypherLexer.DESTROY,
  CypherLexer.DETACH,
  CypherLexer.DIFFERENT,
  CypherLexer.DISTINCT,
  CypherLexer.DRIVER,
  CypherLexer.DROP,
  CypherLexer.DRYRUN,
  CypherLexer.DUMP,
  CypherLexer.DURATION,
  CypherLexer.EACH,
  CypherLexer.EDGE,
  CypherLexer.ELEMENT,
  CypherLexer.ELEMENTS,
  CypherLexer.ELSE,
  CypherLexer.ENABLE,
  CypherLexer.ENCRYPTED,
  CypherLexer.END,
  CypherLexer.ENDS,
  CypherLexer.ERROR,
  CypherLexer.EXECUTABLE,
  CypherLexer.EXECUTE,
  CypherLexer.EXIST,
  CypherLexer.EXISTENCE,
  CypherLexer.EXISTS,
  CypherLexer.FAIL,
  CypherLexer.FALSE,
  CypherLexer.FIELDTERMINATOR,
  CypherLexer.FINISH,
  CypherLexer.FLOAT,
  CypherLexer.FOR,
  CypherLexer.FOREACH,
  CypherLexer.FROM,
  CypherLexer.FULLTEXT,
  CypherLexer.FUNCTION,
  CypherLexer.FUNCTIONS,
  CypherLexer.GRANT,
  CypherLexer.GRAPH,
  CypherLexer.GRAPHS,
  CypherLexer.GROUP,
  CypherLexer.GROUPS,
  CypherLexer.HEADERS,
  CypherLexer.HOME,
  CypherLexer.IF,
  CypherLexer.IMMUTABLE,
  CypherLexer.IMPERSONATE,
  CypherLexer.IN,
  CypherLexer.INDEX,
  CypherLexer.INDEXES,
  CypherLexer.INF,
  CypherLexer.INFINITY,
  CypherLexer.INSERT,
  CypherLexer.INT,
  CypherLexer.INTEGER,
  CypherLexer.IS,
  CypherLexer.JOIN,
  CypherLexer.KEY,
  CypherLexer.LABEL,
  CypherLexer.LABELS,
  CypherLexer.LEADING,
  CypherLexer.LIMITROWS,
  CypherLexer.LIST,
  CypherLexer.LOAD,
  CypherLexer.LOCAL,
  CypherLexer.LOOKUP,
  CypherLexer.MANAGEMENT,
  CypherLexer.MAP,
  CypherLexer.MATCH,
  CypherLexer.MERGE,
  CypherLexer.NAME,
  CypherLexer.NAMES,
  CypherLexer.NAN,
  CypherLexer.NEW,
  CypherLexer.NFC,
  CypherLexer.NFD,
  CypherLexer.NFKC,
  CypherLexer.NFKD,
  CypherLexer.NODE,
  CypherLexer.NODETACH,
  CypherLexer.NODES,
  CypherLexer.NONE,
  CypherLexer.NORMALIZE,
  CypherLexer.NORMALIZED,
  CypherLexer.NOT,
  CypherLexer.NOTHING,
  CypherLexer.NOWAIT,
  CypherLexer.NULL,
  CypherLexer.OF,
  CypherLexer.ON,
  CypherLexer.ONLY,
  CypherLexer.OPTION,
  CypherLexer.OPTIONAL,
  CypherLexer.OPTIONS,
  CypherLexer.OR,
  CypherLexer.ORDER,
  CypherLexer.OUTPUT,
  CypherLexer.PASSWORD,
  CypherLexer.PASSWORDS,
  CypherLexer.PATH,
  CypherLexer.PATHS,
  CypherLexer.PERIODIC,
  CypherLexer.PLAINTEXT,
  CypherLexer.POINT,
  CypherLexer.POPULATED,
  CypherLexer.PRIMARY,
  CypherLexer.PRIMARIES,
  CypherLexer.PRIVILEGE,
  CypherLexer.PRIVILEGES,
  CypherLexer.PROCEDURE,
  CypherLexer.PROCEDURES,
  CypherLexer.PROPERTIES,
  CypherLexer.PROPERTY,
  CypherLexer.RANGE,
  CypherLexer.READ,
  CypherLexer.REALLOCATE,
  CypherLexer.REDUCE,
  CypherLexer.REL,
  CypherLexer.RELATIONSHIP,
  CypherLexer.RELATIONSHIPS,
  CypherLexer.REMOVE,
  CypherLexer.RENAME,
  CypherLexer.REPEATABLE,
  CypherLexer.REPLACE,
  CypherLexer.REPORT,
  CypherLexer.REQUIRE,
  CypherLexer.REQUIRED,
  CypherLexer.RETURN,
  CypherLexer.REVOKE,
  CypherLexer.ROLE,
  CypherLexer.ROLES,
  CypherLexer.ROW,
  CypherLexer.ROWS,
  CypherLexer.SCAN,
  CypherLexer.SECONDARY,
  CypherLexer.SECONDARIES,
  CypherLexer.SEC,
  CypherLexer.SECOND,
  CypherLexer.SECONDS,
  CypherLexer.SEEK,
  CypherLexer.SERVER,
  CypherLexer.SERVERS,
  CypherLexer.SET,
  CypherLexer.SETTING,
  CypherLexer.SETTINGS,
  CypherLexer.SHORTEST,
  CypherLexer.SHORTEST_PATH,
  CypherLexer.SHOW,
  CypherLexer.SIGNED,
  CypherLexer.SINGLE,
  CypherLexer.SKIPROWS,
  CypherLexer.START,
  CypherLexer.STARTS,
  CypherLexer.STATUS,
  CypherLexer.STOP,
  CypherLexer.VARCHAR,
  CypherLexer.STRING,
  CypherLexer.SUPPORTED,
  CypherLexer.SUSPENDED,
  CypherLexer.TARGET,
  CypherLexer.TERMINATE,
  CypherLexer.TEXT,
  CypherLexer.THEN,
  CypherLexer.TIME,
  CypherLexer.TIMESTAMP,
  CypherLexer.TIMEZONE,
  CypherLexer.TO,
  CypherLexer.TOPOLOGY,
  CypherLexer.TRAILING,
  CypherLexer.TRANSACTION,
  CypherLexer.TRANSACTIONS,
  CypherLexer.TRAVERSE,
  CypherLexer.TRIM,
  CypherLexer.TRUE,
  CypherLexer.TYPE,
  CypherLexer.TYPED,
  CypherLexer.TYPES,
  CypherLexer.UNION,
  CypherLexer.UNIQUE,
  CypherLexer.UNIQUENESS,
  CypherLexer.UNWIND,
  CypherLexer.URL,
  CypherLexer.USE,
  CypherLexer.USER,
  CypherLexer.USERS,
  CypherLexer.USING,
  CypherLexer.VALUE,
  CypherLexer.VECTOR,
  CypherLexer.VERBOSE,
  CypherLexer.VERTEX,
  CypherLexer.WAIT,
  CypherLexer.WHEN,
  CypherLexer.WHERE,
  CypherLexer.WITH,
  CypherLexer.WITHOUT,
  CypherLexer.WRITE,
  CypherLexer.XOR,
  CypherLexer.YIELD,
  CypherLexer.ZONED,
  // Preparser tokens
  CypherLexer.EXPLAIN,
  CypherLexer.PROFILE,
];

export const lexerConsoleCmds = [
  CypherLexer.HISTORY,
  CypherLexer.PARAM,
  CypherLexer.CLEAR,
];

function toTokentypeObject(arr: number[], tokenType: CypherTokenType) {
  return arr.reduce<Record<number, CypherTokenType>>(
    (acc, curr) => ({ ...acc, [curr]: tokenType }),
    {},
  );
}

export const lexerSymbols: Record<number, CypherTokenType> = {
  ...toTokentypeObject(lexerOperators, CypherTokenType.operator),
  ...toTokentypeObject(lexerKeywords, CypherTokenType.keyword),
  ...toTokentypeObject(lexerBrackets, CypherTokenType.bracket),
  ...toTokentypeObject(lexerComment, CypherTokenType.comment),
  ...toTokentypeObject(lexerGarbage, CypherTokenType.none),
  ...toTokentypeObject(lexerNumberLiterals, CypherTokenType.numberLiteral),
  ...toTokentypeObject(lexerPunctuation, CypherTokenType.punctuation),
  ...toTokentypeObject(lexerSeparators, CypherTokenType.separator),
  ...toTokentypeObject(lexerStringLiteral, CypherTokenType.stringLiteral),
  ...toTokentypeObject(identifier, CypherTokenType.variable),
  ...toTokentypeObject(lexerConsoleCmds, CypherTokenType.consoleCommand),
};

export const hasIncorrectSymbolicName: Record<number, string> = {
  [CypherLexer.ALL_SHORTEST_PATHS]: 'allShortestPaths',
  [CypherLexer.SHORTEST_PATH]: 'shortestPath',
  [CypherLexer.LIMITROWS]: 'LIMIT',
  [CypherLexer.SKIPROWS]: 'SKIP',
};

const { literalNames, symbolicNames } = CypherLexer;

const length = Math.max(literalNames.length, symbolicNames.length);

export const tokenNames: string[] = Array.from({ length }).map(
  (_, i) =>
    hasIncorrectSymbolicName[i] ??
    literalNames[i] ??
    symbolicNames[i] ??
    '<INVALID',
);

export const keywordNames = new Set(lexerKeywords.map((i) => tokenNames[i]));
