import iterateJsdoc from '../iterateJsdoc.js';

export default iterateJsdoc(({
  context,
  jsdoc,
  utils,
}) => {
  const [
    mainCircumstance,
    {
      tags = null,
    } = {},
  ] = context.options;

  const tgs = /**
               * @type {null|"any"|{[key: string]: "always"|"never"}}
               */ (tags);

  /**
   * @param {import('@es-joy/jsdoccomment').JsdocTagWithInline} jsdocTag
   * @param {string} targetTagName
   * @param {"always"|"never"} [circumstance]
   * @returns {void}
   */
  const checkHyphens = (jsdocTag, targetTagName, circumstance = mainCircumstance) => {
    const always = !circumstance || circumstance === 'always';
    const desc = /** @type {string} */ (utils.getTagDescription(jsdocTag));
    if (!desc.trim()) {
      return;
    }

    const startsWithHyphen = (/^\s*-/u).test(desc);
    let lines = 0;
    for (const {
      tokens,
    } of jsdocTag.source) {
      if (tokens.description) {
        break;
      }

      lines++;
    }

    if (always) {
      if (!startsWithHyphen) {
        utils.reportJSDoc(
          `There must be a hyphen before @${targetTagName} description.`,
          {
            line: jsdocTag.source[0].number + lines,
          },
          () => {
            for (const {
              tokens,
            } of jsdocTag.source) {
              if (tokens.description) {
                tokens.description = tokens.description.replace(
                  /^(\s*)/u, '$1- ',
                );
                break;
              }
            }
          },
        );
      }
    } else if (startsWithHyphen) {
      utils.reportJSDoc(
        `There must be no hyphen before @${targetTagName} description.`,
        {
          line: jsdocTag.source[0].number + lines,
        },
        () => {
          for (const {
            tokens,
          } of jsdocTag.source) {
            if (tokens.description) {
              tokens.description = tokens.description.replace(
                /^\s*-\s*/u, '',
              );
              break;
            }
          }
        },
        true,
      );
    }
  };

  utils.forEachPreferredTag('param', checkHyphens);
  if (tgs) {
    const tagEntries = Object.entries(tgs);
    for (const [
      tagName,
      circumstance,
    ] of tagEntries) {
      if (tagName === '*') {
        const preferredParamTag = utils.getPreferredTagName({
          tagName: 'param',
        });
        for (const {
          tag,
        } of jsdoc.tags) {
          if (tag === preferredParamTag || tagEntries.some(([
            tagNme,
          ]) => {
            return tagNme !== '*' && tagNme === tag;
          })) {
            continue;
          }

          utils.forEachPreferredTag(tag, (jsdocTag, targetTagName) => {
            checkHyphens(
              jsdocTag,
              targetTagName,
              /** @type {"always"|"never"} */ (circumstance),
            );
          });
        }

        continue;
      }

      utils.forEachPreferredTag(tagName, (jsdocTag, targetTagName) => {
        checkHyphens(
          jsdocTag,
          targetTagName,
          /** @type {"always"|"never"} */ (circumstance),
        );
      });
    }
  }
}, {
  iterateAllJsdocs: true,
  meta: {
    docs: {
      description: 'Requires a hyphen before the `@param` description.',
      url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-hyphen-before-param-description.md#repos-sticky-header',
    },
    fixable: 'code',
    schema: [
      {
        enum: [
          'always', 'never',
        ],
        type: 'string',
      },
      {
        additionalProperties: false,
        properties: {
          tags: {
            anyOf: [
              {
                patternProperties: {
                  '.*': {
                    enum: [
                      'always', 'never',
                    ],
                    type: 'string',
                  },
                },
                type: 'object',
              },
              {
                enum: [
                  'any',
                ],
                type: 'string',
              },
            ],
          },
        },
        type: 'object',
      },
    ],
    type: 'layout',
  },
});
