"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc.cjs"));
var _jsdocUtils = require("../jsdocUtils.cjs");
var _jsdoccomment = require("@es-joy/jsdoccomment");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var _default = exports.default = (0, _iterateJsdoc.default)(({
  context,
  indent,
  jsdoc,
  utils
}) => {
  const functionType = context.options[0] ?? 'property';
  const {
    enableFixer = true
  } = context.options[1] ?? {};

  /**
   * @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} tag
   */
  const checkType = tag => {
    const potentialType = tag.type;
    let parsedType;
    try {
      parsedType = (0, _jsdoccomment.parse)(/** @type {string} */potentialType, 'typescript');
    } catch {
      return;
    }
    (0, _jsdoccomment.traverse)(parsedType, (nde, parentNode) => {
      // @ts-expect-error Adding our own property for use below
      nde.parentNode = parentNode;
    });
    (0, _jsdoccomment.traverse)(parsedType, (nde, parentNode, property, idx) => {
      switch (nde.type) {
        case 'JsdocTypeFunction':
          {
            if (functionType !== 'method') {
              break;
            }
            if (parentNode?.type === 'JsdocTypeObjectField' && typeof parentNode.key === 'string') {
              utils.reportJSDoc('Found function property; prefer method signature.', tag, enableFixer ? () => {
                const objectField = parentNode;
                const obj =
                /**
                 * @type {import('jsdoc-type-pratt-parser').ObjectFieldResult & {
                 *   parentNode: import('jsdoc-type-pratt-parser').ObjectResult
                 * }}
                 */
                objectField.parentNode;
                const index = obj.elements.indexOf(parentNode);
                obj.elements[index] = {
                  /* c8 ignore next 5 -- Guard */
                  meta: nde.meta ? {
                    quote: objectField.meta.quote,
                    ...nde.meta
                  } : {
                    quote: objectField.meta.quote
                  },
                  name: (/** @type {string} */objectField.key),
                  parameters: nde.parameters,
                  returnType: (/** @type {import('jsdoc-type-pratt-parser').RootResult} */
                  nde.returnType),
                  type: 'JsdocTypeMethodSignature',
                  typeParameters: nde.typeParameters
                };
                (0, _jsdocUtils.rewireByParsedType)(jsdoc, tag, parsedType, indent);
              } : null);
              break;
            }
            if (parentNode?.type === 'JsdocTypeParenthesis' &&
            // @ts-expect-error Our own added API
            parentNode.parentNode?.type === 'JsdocTypeIntersection' &&
            // @ts-expect-error Our own added API
            parentNode.parentNode.parentNode.type === 'JsdocTypeObjectField' &&
            // @ts-expect-error Our own added API
            typeof parentNode.parentNode.parentNode.key === 'string') {
              // @ts-expect-error Our own added API
              const intersection = parentNode.parentNode;
              const objectField = intersection.parentNode;
              const object = objectField.parentNode;
              // const objFieldIndex = object.elements.indexOf(objectField);

              /**
               * @param {import('jsdoc-type-pratt-parser').FunctionResult} func
               */
              const convertToMethod = func => {
                return /** @type {import('jsdoc-type-pratt-parser').MethodSignatureResult} */{
                  /* c8 ignore next 5 -- Guard */
                  meta: func.meta ? {
                    quote: objectField.meta.quote,
                    ...func.meta
                  } : {
                    quote: objectField.meta.quote
                  },
                  name: (/** @type {string} */objectField.key),
                  parameters: func.parameters,
                  returnType: (/** @type {import('jsdoc-type-pratt-parser').RootResult} */
                  func.returnType),
                  type: 'JsdocTypeMethodSignature',
                  typeParameters: func.typeParameters
                };
              };

              /** @type {import('jsdoc-type-pratt-parser').MethodSignatureResult[]} */
              const methods = [];
              /** @type {number[]} */
              const methodIndexes = [];
              for (const [index, element] of intersection.elements.entries()) {
                if (element.type !== 'JsdocTypeParenthesis' || element.element.type !== 'JsdocTypeFunction') {
                  return;
                }
                methods.push(convertToMethod(element.element));
                methodIndexes.push(index);
              }
              utils.reportJSDoc('Found function property; prefer method signature.', tag, enableFixer ? () => {
                for (const methodIndex of methodIndexes.toReversed()) {
                  object.elements.splice(methodIndex, 1);
                }
                object.elements.splice(methodIndexes[0], 0, ...methods);
                (0, _jsdocUtils.rewireByParsedType)(jsdoc, tag, parsedType, indent);
              } : null);
            }
            break;
          }
        case 'JsdocTypeMethodSignature':
          {
            if (functionType !== 'property') {
              break;
            }

            /**
             * @param {import('jsdoc-type-pratt-parser').MethodSignatureResult} node
             */
            const convertToFunction = node => {
              return {
                arrow: true,
                constructor: false,
                meta: (/** @type {Required<import('jsdoc-type-pratt-parser').MethodSignatureResult['meta']>} */
                node.meta),
                parameters: node.parameters,
                parenthesis: true,
                returnType: node.returnType,
                type: 'JsdocTypeFunction',
                typeParameters: node.typeParameters
              };
            };
            utils.reportJSDoc('Found method signature; prefer function property.', tag, enableFixer ? () => {
              /* c8 ignore next 3 -- TS guard */
              if (!parentNode || !property || typeof idx !== 'number') {
                throw new Error('Unexpected lack of parent or property');
              }
              const object = /** @type {import('jsdoc-type-pratt-parser').ObjectResult} */
              parentNode;
              const funcs = [];
              const removals = [];
              for (const [index, element] of object.elements.entries()) {
                if (element.type === 'JsdocTypeMethodSignature' && element.name === nde.name) {
                  funcs.push(convertToFunction(element));
                  if (index !== idx) {
                    removals.push(index);
                  }
                }
              }
              if (funcs.length === 1) {
                object.elements[idx] = /** @type {import('jsdoc-type-pratt-parser').ObjectFieldResult} */{
                  key: nde.name,
                  meta: nde.meta,
                  optional: false,
                  readonly: false,
                  right: funcs[0],
                  type: 'JsdocTypeObjectField'
                };
              } else {
                for (const removal of removals.toReversed()) {
                  object.elements.splice(removal, 1);
                }
                object.elements[idx] = {
                  key: nde.name,
                  meta: nde.meta,
                  optional: false,
                  readonly: false,
                  right: {
                    elements: funcs.map(func => {
                      return /** @type {import('jsdoc-type-pratt-parser').ParenthesisResult} */{
                        element: func,
                        type: 'JsdocTypeParenthesis'
                      };
                    }),
                    type: 'JsdocTypeIntersection'
                  },
                  type: 'JsdocTypeObjectField'
                };
              }
              (0, _jsdocUtils.rewireByParsedType)(jsdoc, tag, parsedType, indent);
            } : null);
          }
          break;
      }
    });
  };
  const tags = utils.filterTags(({
    tag
  }) => {
    return Boolean(tag !== 'import' && utils.tagMightHaveTypePosition(tag));
  });
  for (const tag of tags) {
    if (tag.type) {
      checkType(tag);
    }
  }
}, {
  iterateAllJsdocs: true,
  meta: {
    docs: {
      description: 'Prefers either function properties or method signatures',
      url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/ts-method-signature-style.md#repos-sticky-header'
    },
    fixable: 'code',
    schema: [{
      enum: ['method', 'property'],
      type: 'string'
    }, {
      additionalProperties: false,
      properties: {
        enableFixer: {
          description: 'Whether to enable the fixer. Defaults to `true`.',
          type: 'boolean'
        }
      },
      type: 'object'
    }],
    type: 'suggestion'
  }
});
module.exports = exports.default;
//# sourceMappingURL=tsMethodSignatureStyle.cjs.map