import { isClosingBracketToken, isClosingParenToken, isNotClosingParenToken, isNotOpeningBraceToken, isOpeningBraceToken, isOpeningBracketToken, isOpeningParenToken, isSemicolonToken } from '@eslint-community/eslint-utils';
import { isBeginningOfLine } from './commons.js';
import { getFirstAndLastTokens } from './commons.js';
/**
 * Creates AST event handlers for svelte nodes.
 *
 * @param context The rule context.
 * @returns AST event handlers.
 */
export function defineVisitor(context) {
    const { offsets, sourceCode } = context;
    const visitor = {
        TSTypeAnnotation(node) {
            // : Type
            // => Type
            const [colonOrArrowToken, secondToken] = sourceCode.getFirstTokens(node, {
                count: 2,
                includeComments: false
            });
            const baseToken = sourceCode.getFirstToken(node.parent);
            offsets.setOffsetToken([colonOrArrowToken, secondToken], 1, baseToken);
            const before = sourceCode.getTokenBefore(colonOrArrowToken);
            if (before && before.value === '?') {
                offsets.setOffsetToken(before, 1, baseToken);
            }
        },
        TSAsExpression(node) {
            // foo as T
            // or
            // foo satisfies T
            const expressionTokens = getFirstAndLastTokens(sourceCode, node.expression);
            const asOrSatisfiesToken = sourceCode.getTokenAfter(expressionTokens.lastToken);
            offsets.setOffsetToken([asOrSatisfiesToken, getFirstAndLastTokens(sourceCode, node.typeAnnotation).firstToken], 1, expressionTokens.firstToken);
        },
        TSSatisfiesExpression(node) {
            // foo satisfies T
            visitor.TSAsExpression(node);
        },
        TSTypeReference(node) {
            // T<U>
            // or
            // const ErrorMap = Map<string, Error>
            //                  ^^^^^^^^^^^^^^^^^^
            const typeArguments = node.typeArguments ??
                // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Support old typescript-eslint
                node.typeParameters;
            if (typeArguments) {
                const firstToken = sourceCode.getFirstToken(node);
                offsets.setOffsetToken(sourceCode.getFirstToken(typeArguments), 1, firstToken);
            }
        },
        TSInstantiationExpression(node) {
            // const ErrorMap = Map<string, Error>
            //                  ^^^^^^^^^^^^^^^^^^
            visitor.TSTypeReference(node);
        },
        TSTypeParameterInstantiation(node) {
            // <T>
            offsets.setOffsetElementList(node.params, sourceCode.getFirstToken(node), sourceCode.getLastToken(node), 1);
        },
        TSTypeParameterDeclaration(node) {
            // <T>
            visitor.TSTypeParameterInstantiation(node);
        },
        TSTypeAliasDeclaration(node) {
            // type T = {}
            const typeToken = sourceCode.getFirstToken(node);
            const idToken = sourceCode.getFirstToken(node.id);
            offsets.setOffsetToken(idToken, 1, typeToken);
            let eqToken;
            if (node.typeParameters) {
                offsets.setOffsetToken(sourceCode.getFirstToken(node.typeParameters), 1, idToken);
                eqToken = sourceCode.getTokenAfter(node.typeParameters);
            }
            else {
                eqToken = sourceCode.getTokenAfter(node.id);
            }
            const initToken = sourceCode.getTokenAfter(eqToken);
            offsets.setOffsetToken([eqToken, initToken], 1, idToken);
        },
        TSFunctionType(node) {
            // ()=>void
            const firstToken = sourceCode.getFirstToken(node);
            // new or < or (
            let currToken = firstToken;
            if (node.type === 'TSConstructorType') {
                // currToken is new token
                // < or (
                currToken = sourceCode.getTokenAfter(currToken);
                offsets.setOffsetToken(currToken, 1, firstToken);
            }
            if (node.typeParameters) {
                // currToken is < token
                // (
                currToken = sourceCode.getTokenAfter(node.typeParameters);
                offsets.setOffsetToken(currToken, 1, firstToken);
            }
            const leftParenToken = currToken;
            const rightParenToken = sourceCode.getTokenAfter(node.params[node.params.length - 1] || leftParenToken, { filter: isClosingParenToken, includeComments: false });
            offsets.setOffsetElementList(node.params, leftParenToken, rightParenToken, 1);
            const arrowToken = sourceCode.getTokenAfter(rightParenToken);
            offsets.setOffsetToken(arrowToken, 1, leftParenToken);
        },
        TSConstructorType(node) {
            // new ()=>void
            visitor.TSFunctionType(node);
        },
        TSTypeLiteral(node) {
            // {foo:any}
            offsets.setOffsetElementList(node.members, sourceCode.getFirstToken(node), sourceCode.getLastToken(node), 1);
        },
        TSPropertySignature(node) {
            // { target:any }
            //   ^^^^^^^^^^
            const firstToken = sourceCode.getFirstToken(node);
            const keyTokens = getFirstAndLastTokens(sourceCode, node.key);
            let keyLast;
            if (node.computed) {
                const closeBracket = sourceCode.getTokenAfter(keyTokens.lastToken);
                offsets.setOffsetElementList([node.key], firstToken, closeBracket, 1);
                keyLast = closeBracket;
            }
            else {
                keyLast = keyTokens.lastToken;
            }
            if (node.typeAnnotation) {
                const typeAnnotationToken = sourceCode.getFirstToken(node.typeAnnotation);
                offsets.setOffsetToken([...sourceCode.getTokensBetween(keyLast, typeAnnotationToken), typeAnnotationToken], 1, firstToken);
            }
            else if (node.optional) {
                const qToken = sourceCode.getLastToken(node);
                offsets.setOffsetToken(qToken, 1, firstToken);
            }
        },
        TSIndexSignature(node) {
            // { [k: string ]: string[] };
            //   ^^^^^^^^^^^^^^^^^^^^^^
            const leftBracketToken = sourceCode.getFirstToken(node);
            const rightBracketToken = sourceCode.getTokenAfter(node.parameters[node.parameters.length - 1] || leftBracketToken, { filter: isClosingBracketToken, includeComments: false });
            offsets.setOffsetElementList(node.parameters, leftBracketToken, rightBracketToken, 1);
            const keyLast = rightBracketToken;
            if (node.typeAnnotation) {
                const typeAnnotationToken = sourceCode.getFirstToken(node.typeAnnotation);
                offsets.setOffsetToken([...sourceCode.getTokensBetween(keyLast, typeAnnotationToken), typeAnnotationToken], 1, leftBracketToken);
            }
        },
        TSArrayType(node) {
            // T[]
            const firstToken = sourceCode.getFirstToken(node);
            offsets.setOffsetToken(sourceCode.getLastTokens(node, { count: 2, includeComments: false }), 0, firstToken);
        },
        TSTupleType(node) {
            // [T, U]
            offsets.setOffsetElementList(node.elementTypes, sourceCode.getFirstToken(node), sourceCode.getLastToken(node), 1);
        },
        TSQualifiedName(node) {
            // A.B
            const objectToken = sourceCode.getFirstToken(node);
            const dotToken = sourceCode.getTokenBefore(node.right);
            const propertyToken = sourceCode.getTokenAfter(dotToken);
            offsets.setOffsetToken([dotToken, propertyToken], 1, objectToken);
        },
        TSIndexedAccessType(node) {
            // A[B]
            const objectToken = sourceCode.getFirstToken(node);
            const leftBracketToken = sourceCode.getTokenBefore(node.indexType, {
                filter: isOpeningBracketToken,
                includeComments: false
            });
            const rightBracketToken = sourceCode.getTokenAfter(node.indexType, {
                filter: isClosingBracketToken,
                includeComments: false
            });
            offsets.setOffsetToken(leftBracketToken, 1, objectToken);
            offsets.setOffsetElementList([node.indexType], leftBracketToken, rightBracketToken, 1);
        },
        TSUnionType(node) {
            // A | B
            const firstToken = sourceCode.getFirstToken(node);
            const types = [...node.types];
            if (getFirstAndLastTokens(sourceCode, types[0]).firstToken === firstToken) {
                types.shift();
            }
            offsets.setOffsetElementList(types, firstToken, null, isBeginningOfLine(sourceCode, firstToken) ? 0 : 1);
        },
        TSIntersectionType(node) {
            // A & B
            visitor.TSUnionType(node);
        },
        TSMappedType(node) {
            // {[key in foo]: bar}
            const leftBraceToken = sourceCode.getFirstToken(node);
            const leftBracketToken = sourceCode.getTokenBefore(node.key || node.typeParameter);
            const rightBracketToken = sourceCode.getTokenAfter(node.nameType || node.constraint || node.typeParameter);
            offsets.setOffsetToken([...sourceCode.getTokensBetween(leftBraceToken, leftBracketToken), leftBracketToken], 1, leftBraceToken);
            offsets.setOffsetElementList([node.key, node.constraint, node.typeParameter, node.nameType], leftBracketToken, rightBracketToken, 1);
            if (node.key && node.constraint) {
                const firstToken = sourceCode.getFirstToken(node.key);
                offsets.setOffsetToken([
                    ...sourceCode.getTokensBetween(firstToken, node.constraint),
                    sourceCode.getFirstToken(node.constraint)
                ], 1, firstToken);
            }
            const rightBraceToken = sourceCode.getLastToken(node);
            if (node.typeAnnotation) {
                const typeAnnotationToken = sourceCode.getFirstToken(node.typeAnnotation);
                offsets.setOffsetToken([
                    ...sourceCode.getTokensBetween(rightBracketToken, typeAnnotationToken),
                    typeAnnotationToken
                ], 1, leftBraceToken);
            }
            else {
                offsets.setOffsetToken([...sourceCode.getTokensBetween(rightBracketToken, rightBraceToken)], 1, leftBraceToken);
            }
            offsets.setOffsetToken(rightBraceToken, 0, leftBraceToken);
        },
        TSTypeParameter(node) {
            // {[key in foo]: bar}
            //   ^^^^^^^^^^
            // type T<U extends V = W>
            //        ^^^^^^^^^^^^^^^
            const [firstToken, ...afterTokens] = sourceCode.getTokens(node);
            for (const child of [node.constraint, node.default]) {
                if (!child) {
                    continue;
                }
                const [, ...removeTokens] = sourceCode.getTokens(child);
                for (const token of removeTokens) {
                    const i = afterTokens.indexOf(token);
                    if (i >= 0) {
                        afterTokens.splice(i, 1);
                    }
                }
            }
            const secondToken = afterTokens.shift();
            if (!secondToken) {
                return;
            }
            offsets.setOffsetToken(secondToken, 1, firstToken);
            if (secondToken.value === 'extends') {
                let prevToken = null;
                let token = afterTokens.shift();
                while (token) {
                    if (token.value === '=') {
                        break;
                    }
                    offsets.setOffsetToken(token, 1, secondToken);
                    prevToken = token;
                    token = afterTokens.shift();
                }
                while (token) {
                    offsets.setOffsetToken(token, 1, prevToken || secondToken);
                    token = afterTokens.shift();
                }
            }
            else {
                offsets.setOffsetToken(afterTokens, 1, firstToken);
            }
        },
        TSConditionalType(node) {
            // T extends Foo ? T : U
            const checkTypeToken = sourceCode.getFirstToken(node);
            const extendsToken = sourceCode.getTokenAfter(node.checkType);
            const extendsTypeToken = sourceCode.getFirstToken(node.extendsType);
            offsets.setOffsetToken(extendsToken, 1, checkTypeToken);
            offsets.setOffsetToken(extendsTypeToken, 1, extendsToken);
            const questionToken = sourceCode.getTokenAfter(node.extendsType, {
                filter: isNotClosingParenToken,
                includeComments: false
            });
            const consequentToken = sourceCode.getTokenAfter(questionToken);
            const colonToken = sourceCode.getTokenAfter(node.trueType, {
                filter: isNotClosingParenToken,
                includeComments: false
            });
            const alternateToken = sourceCode.getTokenAfter(colonToken);
            let baseNode = node;
            let parent = baseNode.parent;
            while (parent && parent.type === 'TSConditionalType' && parent.falseType === baseNode) {
                baseNode = parent;
                parent = baseNode.parent;
            }
            const baseToken = sourceCode.getFirstToken(baseNode);
            offsets.setOffsetToken([questionToken, colonToken], 1, baseToken);
            offsets.setOffsetToken(consequentToken, 1, questionToken);
            offsets.setOffsetToken(alternateToken, 1, colonToken);
        },
        TSInterfaceDeclaration(node) {
            // interface I {}
            const interfaceToken = sourceCode.getFirstToken(node);
            offsets.setOffsetToken(sourceCode.getFirstToken(node.id), 1, interfaceToken);
            if (node.typeParameters != null) {
                offsets.setOffsetToken(sourceCode.getFirstToken(node.typeParameters), 1, sourceCode.getFirstToken(node.id));
            }
            if (node.extends != null && node.extends.length) {
                const extendsToken = sourceCode.getTokenBefore(node.extends[0]);
                offsets.setOffsetToken(extendsToken, 1, interfaceToken);
                offsets.setOffsetElementList(node.extends, extendsToken, null, 1);
            }
            // It may not calculate the correct location because the visitor key is not provided.
            // if (node.implements != null && node.implements.length) {
            //   const implementsToken = sourceCode.getTokenBefore(node.implements[0])!
            //  offsets.setOffsetToken(implementsToken, 1, interfaceToken)
            //  offsets.setOffsetElementList( node.implements, implementsToken, null, 1)
            // }
            const bodyToken = sourceCode.getFirstToken(node.body);
            offsets.setOffsetToken(bodyToken, 0, interfaceToken);
        },
        TSInterfaceBody(node) {
            offsets.setOffsetElementList(node.body, sourceCode.getFirstToken(node), sourceCode.getLastToken(node), 1);
        },
        TSClassImplements(node) {
            // class C implements T {}
            //                    ^
            const typeArguments = node.typeArguments ??
                // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Support old typescript-eslint
                node.typeParameters;
            if (typeArguments) {
                offsets.setOffsetToken(sourceCode.getFirstToken(typeArguments), 1, sourceCode.getFirstToken(node));
            }
        },
        TSInterfaceHeritage(node) {
            // interface I extends E implements T {}
            //                     ^            ^
            visitor.TSClassImplements(node);
        },
        TSEnumDeclaration(node) {
            // enum E {}
            const firstToken = sourceCode.getFirstToken(node);
            const idTokens = getFirstAndLastTokens(sourceCode, node.id);
            const prefixTokens = sourceCode.getTokensBetween(firstToken, idTokens.firstToken);
            offsets.setOffsetToken(prefixTokens, 0, firstToken);
            offsets.setOffsetToken(idTokens.firstToken, 1, firstToken);
            const leftBraceToken = sourceCode.getTokenAfter(idTokens.lastToken);
            const rightBraceToken = sourceCode.getLastToken(node);
            offsets.setOffsetToken(leftBraceToken, 0, firstToken);
            if (node.members)
                offsets.setOffsetElementList(node.members, leftBraceToken, rightBraceToken, 1);
        },
        TSEnumBody(node) {
            // enum E {...}
            //        ^^^^^
            const leftBraceToken = sourceCode.getFirstToken(node);
            const rightBraceToken = sourceCode.getLastToken(node);
            offsets.setOffsetElementList(node.members, leftBraceToken, rightBraceToken, 1);
        },
        TSModuleDeclaration(node) {
            const firstToken = sourceCode.getFirstToken(node);
            const idTokens = getFirstAndLastTokens(sourceCode, node.id);
            const prefixTokens = sourceCode.getTokensBetween(firstToken, idTokens.firstToken);
            offsets.setOffsetToken(prefixTokens, 0, firstToken);
            offsets.setOffsetToken(idTokens.firstToken, 1, firstToken);
            if (node.body) {
                const bodyFirstToken = sourceCode.getFirstToken(node.body);
                offsets.setOffsetToken(bodyFirstToken, isOpeningBraceToken(bodyFirstToken) ? 0 : 1, firstToken);
            }
        },
        TSModuleBlock(node) {
            visitor.TSInterfaceBody(node);
        },
        TSMethodSignature(node) {
            // fn(arg: A): R | null;
            const firstToken = sourceCode.getFirstToken(node);
            const keyTokens = getFirstAndLastTokens(sourceCode, node.key);
            let keyLast;
            if (node.computed) {
                const closeBracket = sourceCode.getTokenAfter(keyTokens.lastToken);
                offsets.setOffsetElementList([node.key], firstToken, closeBracket, 1);
                keyLast = closeBracket;
            }
            else {
                keyLast = keyTokens.lastToken;
            }
            const leftParenToken = sourceCode.getTokenAfter(keyLast, {
                filter: isOpeningParenToken,
                includeComments: false
            });
            offsets.setOffsetToken([...sourceCode.getTokensBetween(keyLast, leftParenToken), leftParenToken], 1, firstToken);
            const rightParenToken = sourceCode.getTokenAfter(node.params[node.params.length - 1] || leftParenToken, { filter: isClosingParenToken, includeComments: false });
            offsets.setOffsetElementList(node.params, leftParenToken, rightParenToken, 1);
            if (node.returnType) {
                const typeAnnotationToken = sourceCode.getFirstToken(node.returnType);
                offsets.setOffsetToken([...sourceCode.getTokensBetween(keyLast, typeAnnotationToken), typeAnnotationToken], 1, firstToken);
            }
        },
        TSCallSignatureDeclaration(node) {
            // interface A { <T> (e: E): R }
            //               ^^^^^^^^^^^^^
            const firstToken = sourceCode.getFirstToken(node);
            // new or < or (
            let currToken = firstToken;
            if (node.type === 'TSConstructSignatureDeclaration') {
                // currToken is new token
                // < or (
                currToken = sourceCode.getTokenAfter(currToken);
                offsets.setOffsetToken(currToken, 1, firstToken);
            }
            if (node.typeParameters) {
                // currToken is < token
                // (
                currToken = sourceCode.getTokenAfter(node.typeParameters);
                offsets.setOffsetToken(currToken, 1, firstToken);
            }
            const leftParenToken = currToken;
            const rightParenToken = sourceCode.getTokenAfter(node.params[node.params.length - 1] || leftParenToken, { filter: isClosingParenToken, includeComments: false });
            offsets.setOffsetElementList(node.params, leftParenToken, rightParenToken, 1);
            if (node.returnType) {
                const typeAnnotationToken = sourceCode.getFirstToken(node.returnType);
                offsets.setOffsetToken([
                    ...sourceCode.getTokensBetween(rightParenToken, typeAnnotationToken),
                    typeAnnotationToken
                ], 1, firstToken);
            }
        },
        TSConstructSignatureDeclaration(node) {
            // interface A { new <T> (e: E): R }
            //               ^^^^^^^^^^^^^^^^^
            visitor.TSCallSignatureDeclaration(node);
        },
        TSEmptyBodyFunctionExpression(node) {
            const firstToken = sourceCode.getFirstToken(node);
            let leftParenToken, bodyBaseToken;
            if (firstToken.type === 'Punctuator') {
                // method
                leftParenToken = firstToken;
                bodyBaseToken = sourceCode.getFirstToken(node.parent);
            }
            else {
                let nextToken = sourceCode.getTokenAfter(firstToken);
                let nextTokenOffset = 0;
                while (nextToken && !isOpeningParenToken(nextToken) && nextToken.value !== '<') {
                    if (nextToken.value === '*' || (node.id && nextToken.range[0] === node.id.range[0])) {
                        nextTokenOffset = 1;
                    }
                    offsets.setOffsetToken(nextToken, nextTokenOffset, firstToken);
                    nextToken = sourceCode.getTokenAfter(nextToken);
                }
                leftParenToken = nextToken;
                bodyBaseToken = firstToken;
            }
            if (!isOpeningParenToken(leftParenToken) && node.typeParameters) {
                leftParenToken = sourceCode.getTokenAfter(node.typeParameters);
            }
            const rightParenToken = sourceCode.getTokenAfter(node.params[node.params.length - 1] || leftParenToken, { filter: isClosingParenToken, includeComments: false });
            offsets.setOffsetToken(leftParenToken, 1, bodyBaseToken);
            offsets.setOffsetElementList(node.params, leftParenToken, rightParenToken, 1);
        },
        TSDeclareFunction(node) {
            // function fn(): void;
            visitor.TSEmptyBodyFunctionExpression(node);
        },
        TSTypeOperator(node) {
            // keyof T
            const firstToken = sourceCode.getFirstToken(node);
            const nextToken = sourceCode.getTokenAfter(firstToken);
            offsets.setOffsetToken(nextToken, 1, firstToken);
        },
        TSTypeQuery(node) {
            // type T = typeof a
            visitor.TSTypeOperator(node);
        },
        TSInferType(node) {
            // infer U
            visitor.TSTypeOperator(node);
        },
        TSTypePredicate(node) {
            // v is T
            const firstToken = sourceCode.getFirstToken(node);
            const opToken = sourceCode.getTokenAfter(node.parameterName, {
                filter: isNotClosingParenToken,
                includeComments: false
            });
            const rightToken = node.typeAnnotation && getFirstAndLastTokens(sourceCode, node.typeAnnotation).firstToken;
            offsets.setOffsetToken([opToken, rightToken], 1, getFirstAndLastTokens(sourceCode, firstToken).firstToken);
        },
        TSAbstractMethodDefinition(node) {
            const { keyNode, valueNode } = node.type === 'TSEnumMember'
                ? { keyNode: node.id, valueNode: node.initializer }
                : { keyNode: node.key, valueNode: node.value };
            const firstToken = sourceCode.getFirstToken(node);
            const keyTokens = getFirstAndLastTokens(sourceCode, keyNode);
            const prefixTokens = sourceCode.getTokensBetween(firstToken, keyTokens.firstToken);
            if (node.computed) {
                prefixTokens.pop(); // pop [
            }
            offsets.setOffsetToken(prefixTokens, 0, firstToken);
            let lastKeyToken;
            if (node.computed) {
                const leftBracketToken = sourceCode.getTokenBefore(keyTokens.firstToken);
                const rightBracketToken = (lastKeyToken = sourceCode.getTokenAfter(keyTokens.lastToken));
                offsets.setOffsetToken(leftBracketToken, 0, firstToken);
                offsets.setOffsetElementList([keyNode], leftBracketToken, rightBracketToken, 1);
            }
            else {
                offsets.setOffsetToken(keyTokens.firstToken, 0, firstToken);
                lastKeyToken = keyTokens.lastToken;
            }
            if (valueNode != null) {
                const initToken = sourceCode.getFirstToken(valueNode);
                offsets.setOffsetToken([...sourceCode.getTokensBetween(lastKeyToken, initToken), initToken], 1, lastKeyToken);
            }
        },
        TSAbstractPropertyDefinition(node) {
            visitor.TSAbstractMethodDefinition(node);
        },
        TSEnumMember(node) {
            visitor.TSAbstractMethodDefinition(node);
        },
        TSAbstractAccessorProperty(node) {
            visitor.TSAbstractMethodDefinition(node);
        },
        TSOptionalType(node) {
            // [number?]
            //  ^^^^^^^
            offsets.setOffsetToken(sourceCode.getLastToken(node), 1, sourceCode.getFirstToken(node));
        },
        TSNonNullExpression(node) {
            // v!
            visitor.TSOptionalType(node);
        },
        TSJSDocNonNullableType(
        // @ts-expect-error -- Missing TSJSDocNonNullableType type
        node) {
            // T!
            visitor.TSOptionalType(node);
        },
        TSTypeAssertion(node) {
            // <const>
            const firstToken = sourceCode.getFirstToken(node);
            const expressionToken = getFirstAndLastTokens(sourceCode, node.expression).firstToken;
            offsets.setOffsetElementList([node.typeAnnotation], firstToken, sourceCode.getTokenBefore(expressionToken), 1);
            offsets.setOffsetToken(expressionToken, 1, firstToken);
        },
        TSImportType(node) {
            // import('foo').B
            const typeArguments = node.typeArguments ??
                // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Support old typescript-eslint
                node.typeParameters;
            const firstToken = sourceCode.getFirstToken(node);
            const leftParenToken = sourceCode.getTokenAfter(firstToken, {
                filter: isOpeningParenToken,
                includeComments: false
            });
            offsets.setOffsetToken(leftParenToken, 1, firstToken);
            const argument = node.argument ||
                // eslint-disable-next-line @typescript-eslint/no-explicit-any -- typescript-eslint<v6 node
                node.parameter;
            const rightParenToken = sourceCode.getTokenAfter(argument, {
                filter: isClosingParenToken,
                includeComments: false
            });
            offsets.setOffsetElementList([argument], leftParenToken, rightParenToken, 1);
            if (node.qualifier) {
                const dotToken = sourceCode.getTokenBefore(node.qualifier);
                const propertyToken = sourceCode.getTokenAfter(dotToken);
                offsets.setOffsetToken([dotToken, propertyToken], 1, firstToken);
            }
            if (typeArguments) {
                offsets.setOffsetToken(sourceCode.getFirstToken(typeArguments), 1, firstToken);
            }
        },
        TSParameterProperty(node) {
            // constructor(private a)
            const firstToken = sourceCode.getFirstToken(node);
            const parameterToken = sourceCode.getFirstToken(node.parameter);
            offsets.setOffsetToken([...sourceCode.getTokensBetween(firstToken, parameterToken), parameterToken], 1, firstToken);
        },
        TSImportEqualsDeclaration(node) {
            // import foo = require('foo')
            const importToken = sourceCode.getFirstToken(node);
            const idTokens = getFirstAndLastTokens(sourceCode, node.id);
            offsets.setOffsetToken(idTokens.firstToken, 1, importToken);
            const opToken = sourceCode.getTokenAfter(idTokens.lastToken);
            offsets.setOffsetToken([opToken, sourceCode.getFirstToken(node.moduleReference)], 1, idTokens.lastToken);
        },
        TSExternalModuleReference(node) {
            // require('foo')
            const requireToken = sourceCode.getFirstToken(node);
            const leftParenToken = sourceCode.getTokenAfter(requireToken, {
                filter: isOpeningParenToken,
                includeComments: false
            });
            const rightParenToken = sourceCode.getLastToken(node);
            offsets.setOffsetToken(leftParenToken, 1, requireToken);
            offsets.setOffsetElementList([node.expression], leftParenToken, rightParenToken, 1);
        },
        TSExportAssignment(node) {
            // export = foo
            const exportNode = sourceCode.getFirstToken(node);
            const exprTokens = getFirstAndLastTokens(sourceCode, node.expression);
            const opToken = sourceCode.getTokenBefore(exprTokens.firstToken);
            offsets.setOffsetToken([opToken, exprTokens.firstToken], 1, exportNode);
        },
        TSNamedTupleMember(node) {
            // [a: string, ...b: string[]]
            //  ^^^^^^^^^
            const labelToken = sourceCode.getFirstToken(node);
            const elementTokens = getFirstAndLastTokens(sourceCode, node.elementType);
            offsets.setOffsetToken([
                ...sourceCode.getTokensBetween(labelToken, elementTokens.firstToken),
                elementTokens.firstToken
            ], 1, labelToken);
        },
        TSRestType(node) {
            // [a: string, ...b: string[]]
            //             ^^^^^^^^^^^^^^
            const firstToken = sourceCode.getFirstToken(node);
            const nextToken = sourceCode.getTokenAfter(firstToken);
            offsets.setOffsetToken(nextToken, 1, firstToken);
        },
        TSNamespaceExportDeclaration(node) {
            const firstToken = sourceCode.getFirstToken(node);
            const idToken = sourceCode.getFirstToken(node.id);
            offsets.setOffsetToken([...sourceCode.getTokensBetween(firstToken, idToken), idToken], 1, firstToken);
        },
        TSTemplateLiteralType(node) {
            const firstToken = sourceCode.getFirstToken(node);
            const quasiTokens = node.quasis.slice(1).map((n) => sourceCode.getFirstToken(n));
            const expressionToken = node.quasis.slice(0, -1).map((n) => sourceCode.getTokenAfter(n));
            offsets.setOffsetToken(quasiTokens, 0, firstToken);
            offsets.setOffsetToken(expressionToken, 1, firstToken);
        },
        // ----------------------------------------------------------------------
        // NON-STANDARD NODES
        // ----------------------------------------------------------------------
        Decorator(node) {
            // @Decorator
            const [atToken, secondToken] = sourceCode.getFirstTokens(node, {
                count: 2,
                includeComments: false
            });
            offsets.setOffsetToken(secondToken, 0, atToken);
            const parent = node.parent;
            const { decorators } = parent;
            if (!decorators || decorators.length === 0) {
                return;
            }
            if (decorators[0] === node) {
                if (parent.range[0] === node.range[0]) {
                    const startParentToken = sourceCode.getTokenAfter(decorators[decorators?.length - 1]);
                    offsets.setOffsetToken(startParentToken, 0, atToken);
                }
                else {
                    const startParentToken = sourceCode.getFirstToken(parent.parent &&
                        (parent.parent.type === 'ExportDefaultDeclaration' ||
                            parent.parent.type === 'ExportNamedDeclaration') &&
                        node.range[0] < parent.parent.range[0]
                        ? parent.parent
                        : parent);
                    offsets.copyOffset(atToken.range[0], startParentToken.range[0]);
                }
            }
            else {
                offsets.setOffsetToken(atToken, 0, sourceCode.getFirstToken(decorators[0]));
            }
        },
        AccessorProperty(node) {
            const keyNode = node.key;
            const valueNode = node.value;
            const firstToken = sourceCode.getFirstToken(node);
            const keyTokens = getFirstAndLastTokens(sourceCode, keyNode);
            const prefixTokens = sourceCode.getTokensBetween(firstToken, keyTokens.firstToken);
            if (node.computed) {
                prefixTokens.pop(); // pop [
            }
            offsets.setOffsetToken(prefixTokens, 0, firstToken);
            let lastKeyToken;
            if (node.computed) {
                const leftBracketToken = sourceCode.getTokenBefore(keyTokens.firstToken);
                const rightBracketToken = (lastKeyToken = sourceCode.getTokenAfter(keyTokens.lastToken));
                offsets.setOffsetToken(leftBracketToken, 0, firstToken);
                offsets.setOffsetElementList([keyNode], leftBracketToken, rightBracketToken, 1);
            }
            else {
                offsets.setOffsetToken(keyTokens.firstToken, 0, firstToken);
                lastKeyToken = keyTokens.lastToken;
            }
            if (valueNode != null) {
                const initToken = sourceCode.getFirstToken(valueNode);
                offsets.setOffsetToken([...sourceCode.getTokensBetween(lastKeyToken, initToken), initToken], 1, lastKeyToken);
            }
        },
        StaticBlock(node) {
            const firstToken = sourceCode.getFirstToken(node);
            let next = sourceCode.getTokenAfter(firstToken);
            while (next && isNotOpeningBraceToken(next)) {
                offsets.setOffsetToken(next, 0, firstToken);
                next = sourceCode.getTokenAfter(next);
            }
            offsets.setOffsetToken(next, 0, firstToken);
            offsets.setOffsetElementList(node.body, next, sourceCode.getLastToken(node), 1);
        },
        ImportAttribute(node) {
            const firstToken = sourceCode.getFirstToken(node);
            const keyTokens = getFirstAndLastTokens(sourceCode, node.key);
            const prefixTokens = sourceCode.getTokensBetween(firstToken, keyTokens.firstToken);
            offsets.setOffsetToken(prefixTokens, 0, firstToken);
            offsets.setOffsetToken(keyTokens.firstToken, 0, firstToken);
            const initToken = sourceCode.getFirstToken(node.value);
            offsets.setOffsetToken([...sourceCode.getTokensBetween(keyTokens.lastToken, initToken), initToken], 1, keyTokens.lastToken);
        },
        // ----------------------------------------------------------------------
        // SINGLE TOKEN NODES
        // ----------------------------------------------------------------------
        // VALUES KEYWORD
        TSAnyKeyword() {
            // noop
        },
        TSBigIntKeyword() {
            // noop
        },
        TSBooleanKeyword() {
            // noop
        },
        TSNeverKeyword() {
            // noop
        },
        TSNullKeyword() {
            // noop
        },
        TSNumberKeyword() {
            // noop
        },
        TSObjectKeyword() {
            // noop
        },
        TSStringKeyword() {
            // noop
        },
        TSSymbolKeyword() {
            // noop
        },
        TSUndefinedKeyword() {
            // noop
        },
        TSUnknownKeyword() {
            // noop
        },
        TSVoidKeyword() {
            // noop
        },
        // MODIFIERS KEYWORD
        TSAbstractKeyword() {
            // noop
        },
        TSAsyncKeyword() {
            // noop
        },
        TSPrivateKeyword() {
            // noop
        },
        TSProtectedKeyword() {
            // noop
        },
        TSPublicKeyword() {
            // noop
        },
        TSReadonlyKeyword() {
            // noop
        },
        TSStaticKeyword() {
            // noop
        },
        // OTHERS KEYWORD
        TSDeclareKeyword() {
            // noop
        },
        TSExportKeyword() {
            // noop
        },
        TSIntrinsicKeyword() {
            // noop
        },
        // OTHERS
        TSThisType() {
            // noop
        },
        // ----------------------------------------------------------------------
        // WRAPPER NODES
        // ----------------------------------------------------------------------
        TSLiteralType() {
            // noop
        }
    };
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
    const commonsVisitor = {
        // Process semicolons.
        ['TSTypeAliasDeclaration, TSCallSignatureDeclaration, TSConstructSignatureDeclaration, TSImportEqualsDeclaration,' +
            'TSAbstractMethodDefinition, TSAbstractPropertyDefinition, AccessorProperty,  TSAbstractAccessorProperty, TSEnumMember,' +
            'TSPropertySignature, TSIndexSignature, TSMethodSignature,' +
            'TSAbstractClassProperty, ClassProperty'](node) {
            const firstToken = sourceCode.getFirstToken(node);
            const lastToken = sourceCode.getLastToken(node);
            if (isSemicolonToken(lastToken) && firstToken !== lastToken) {
                const next = sourceCode.getTokenAfter(lastToken);
                if (!next || lastToken.loc.start.line < next.loc.start.line) {
                    // End of line semicolons
                    offsets.setOffsetToken(lastToken, 0, firstToken);
                }
            }
        },
        '*[type=/^TS/]'(node) {
            if (node.type !== 'TSAnyKeyword' &&
                node.type !== 'TSArrayType' &&
                node.type !== 'TSBigIntKeyword' &&
                node.type !== 'TSBooleanKeyword' &&
                node.type !== 'TSConditionalType' &&
                node.type !== 'TSConstructorType' &&
                node.type !== 'TSFunctionType' &&
                node.type !== 'TSImportType' &&
                node.type !== 'TSIndexedAccessType' &&
                node.type !== 'TSInferType' &&
                node.type !== 'TSIntersectionType' &&
                node.type !== 'TSIntrinsicKeyword' &&
                node.type !== 'TSLiteralType' &&
                node.type !== 'TSMappedType' &&
                node.type !== 'TSNamedTupleMember' &&
                node.type !== 'TSNeverKeyword' &&
                node.type !== 'TSNullKeyword' &&
                node.type !== 'TSNumberKeyword' &&
                node.type !== 'TSObjectKeyword' &&
                node.type !== 'TSOptionalType' &&
                node.type !== 'TSRestType' &&
                node.type !== 'TSStringKeyword' &&
                node.type !== 'TSSymbolKeyword' &&
                node.type !== 'TSTemplateLiteralType' &&
                node.type !== 'TSThisType' &&
                node.type !== 'TSTupleType' &&
                node.type !== 'TSTypeLiteral' &&
                node.type !== 'TSTypeOperator' &&
                node.type !== 'TSTypePredicate' &&
                node.type !== 'TSTypeQuery' &&
                node.type !== 'TSTypeReference' &&
                node.type !== 'TSUndefinedKeyword' &&
                node.type !== 'TSUnionType' &&
                node.type !== 'TSUnknownKeyword' &&
                node.type !== 'TSVoidKeyword') {
                return;
            }
            const typeNode = node;
            // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
            if (typeNode.parent.type === 'TSParenthesizedType') {
                return;
            }
            // Process parentheses.
            let leftToken = sourceCode.getTokenBefore(typeNode);
            let rightToken = sourceCode.getTokenAfter(typeNode);
            let firstToken = sourceCode.getFirstToken(typeNode);
            while (leftToken &&
                isOpeningParenToken(leftToken) &&
                rightToken &&
                isClosingParenToken(rightToken)) {
                offsets.setOffsetToken(firstToken, 1, leftToken);
                offsets.setOffsetToken(rightToken, 0, leftToken);
                firstToken = leftToken;
                leftToken = sourceCode.getTokenBefore(leftToken);
                rightToken = sourceCode.getTokenAfter(rightToken);
            }
        }
    };
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
    const extendsESVisitor = {
        ['ClassDeclaration[implements], ClassDeclaration[typeParameters], ClassDeclaration[superTypeParameters],' +
            'ClassExpression[implements], ClassExpression[typeParameters], ClassExpression[superTypeParameters]'](node) {
            if (node.typeParameters != null) {
                offsets.setOffsetToken(sourceCode.getFirstToken(node.typeParameters), 1, sourceCode.getFirstToken(node.id || node));
            }
            const superTypeArguments = node.superTypeArguments ??
                // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Support old typescript-eslint
                node.superTypeParameters;
            if (superTypeArguments != null && node.superClass != null) {
                offsets.setOffsetToken(sourceCode.getFirstToken(superTypeArguments), 1, sourceCode.getFirstToken(node.superClass));
            }
            if (node.implements != null && node.implements.length) {
                const classToken = sourceCode.getFirstToken(node);
                const implementsToken = sourceCode.getTokenBefore(node.implements[0]);
                offsets.setOffsetToken(implementsToken, 1, classToken);
                offsets.setOffsetElementList(node.implements, implementsToken, null, 1);
            }
        }
    };
    // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
    const deprecatedVisitor = {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
        TSParenthesizedType(node) {
            // (T)
            offsets.setOffsetElementList([node.typeAnnotation], sourceCode.getFirstToken(node), sourceCode.getLastToken(node), 1);
        },
        // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
        ClassProperty(node) {
            visitor.TSAbstractMethodDefinition(node);
        },
        // eslint-disable-next-line @typescript-eslint/no-explicit-any -- ignore
        TSAbstractClassProperty(node) {
            visitor.TSAbstractMethodDefinition(node);
        }
    };
    const v = visitor;
    return {
        ...v,
        ...commonsVisitor,
        ...extendsESVisitor,
        ...deprecatedVisitor
    };
}
