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

export default iterateJsdoc(({
  context,
  indent,
  jsdoc,
  utils,
}) => {
  const [
    defaultRequireValue = 'always',
    {
      tags: tagMap = {},
    } = {},
  ] = context.options;

  const {
    source,
  } = jsdoc;

  const always = defaultRequireValue === 'always';
  const never = defaultRequireValue === 'never';

  /** @type {string} */
  let currentTag;
  source.some(({
    number,
    tokens,
  }) => {
    const {
      delimiter,
      description,
      end,
      tag,
    } = tokens;

    /**
     * @returns {void}
     */
    const neverFix = () => {
      tokens.delimiter = '';
      tokens.postDelimiter = '';
    };

    /**
     * @param {string} checkValue
     * @returns {boolean}
     */
    const checkNever = (checkValue) => {
      if (delimiter && delimiter !== '/**' && (
        never && !tagMap.always?.includes(checkValue) ||
        tagMap.never?.includes(checkValue)
      )) {
        utils.reportJSDoc('Expected JSDoc line to have no prefix.', {
          column: 0,
          line: number,
        }, neverFix);

        return true;
      }

      return false;
    };

    /**
     * @returns {void}
     */
    const alwaysFix = () => {
      if (!tokens.start) {
        tokens.start = indent + ' ';
      }

      tokens.delimiter = '*';
      tokens.postDelimiter = tag || description ? ' ' : '';
    };

    /**
     * @param {string} checkValue
     * @returns {boolean}
     */
    const checkAlways = (checkValue) => {
      if (
        !delimiter && (
          always && !tagMap.never?.includes(checkValue) ||
          tagMap.always?.includes(checkValue)
        )
      ) {
        utils.reportJSDoc('Expected JSDoc line to have the prefix.', {
          column: 0,
          line: number,
        }, alwaysFix);

        return true;
      }

      return false;
    };

    if (tag) {
      // Remove at sign
      currentTag = tag.slice(1);
    }

    if (
      // If this is the end but has a tag, the delimiter will also be
      //  populated and will be safely ignored later
      end && !tag
    ) {
      return false;
    }

    if (!currentTag) {
      if (tagMap.any?.includes('*description')) {
        return false;
      }

      if (checkNever('*description')) {
        return true;
      }

      if (checkAlways('*description')) {
        return true;
      }

      return false;
    }

    if (tagMap.any?.includes(currentTag)) {
      return false;
    }

    if (checkNever(currentTag)) {
      return true;
    }

    if (checkAlways(currentTag)) {
      return true;
    }

    return false;
  });
}, {
  iterateAllJsdocs: true,
  meta: {
    docs: {
      description:
        'Requires that each JSDoc line starts with an `*`.',
      url: 'https://github.com/gajus/eslint-plugin-jsdoc/blob/main/docs/rules/require-asterisk-prefix.md#repos-sticky-header',
    },
    fixable: 'code',
    schema: [
      {
        description: `If it is \`"always"\` then a problem is raised when there is no asterisk
prefix on a given JSDoc line. If it is \`"never"\` then a problem is raised
when there is an asterisk present.

The default value is \`"always"\`. You may also set the default to \`"any"\`
and use the \`tags\` option to apply to specific tags only.`,
        enum: [
          'always', 'never', 'any',
        ],
        type: 'string',
      },
      {
        additionalProperties: false,
        properties: {
          tags: {
            additionalProperties: false,
            description: `If you want different values to apply to specific tags, you may use
the \`tags\` option object. The keys are \`always\`, \`never\`, or \`any\` and
the values are arrays of tag names or the special value \`*description\`
which applies to the main JSDoc block description.

\`\`\`js
{
  'jsdoc/require-asterisk-prefix': ['error', 'always', {
    tags: {
      always: ['*description'],
      any: ['example', 'license'],
      never: ['copyright']
    }
  }]
}
\`\`\``,
            properties: {
              always: {
                description: `If it is \`"always"\` then a problem is raised when there is no asterisk
prefix on a given JSDoc line.`,
                items: {
                  type: 'string',
                },
                type: 'array',
              },
              any: {
                description: 'No problem is raised regardless of asterisk presence or non-presence.',
                items: {
                  type: 'string',
                },
                type: 'array',
              },
              never: {
                description: `If it is \`"never"\` then a problem is raised
when there is an asterisk present.`,
                items: {
                  type: 'string',
                },
                type: 'array',
              },
            },
            type: 'object',
          },
        },
        type: 'object',
      },
    ],
    type: 'layout',
  },
});
