/**
 * @typedef {{
 *   [key: string]: string[]
 * }} AliasedTags
 */

/**
 * @type {AliasedTags}
 */
const jsdocTagsUndocumented = {
  // Undocumented but present; see
  // https://github.com/jsdoc/jsdoc/issues/1283#issuecomment-516816802
  // https://github.com/jsdoc/jsdoc/blob/master/packages/jsdoc/lib/jsdoc/tag/dictionary/definitions.js#L594
  modifies: [],
};

/**
 * @type {AliasedTags}
 */
const jsdocTags = {
  ...jsdocTagsUndocumented,
  abstract: [
    'virtual',
  ],
  access: [],
  alias: [],
  async: [],
  augments: [
    'extends',
  ],
  author: [],
  borrows: [],
  callback: [],
  class: [
    'constructor',
  ],
  classdesc: [],
  constant: [
    'const',
  ],
  constructs: [],
  copyright: [],
  default: [
    'defaultvalue',
  ],
  deprecated: [],
  description: [
    'desc',
  ],
  enum: [],
  event: [],
  example: [],
  exports: [],
  external: [
    'host',
  ],
  file: [
    'fileoverview',
    'overview',
  ],
  fires: [
    'emits',
  ],
  function: [
    'func',
    'method',
  ],
  generator: [],
  global: [],
  hideconstructor: [],
  ignore: [],
  implements: [],
  inheritdoc: [],

  // Allowing casing distinct from jsdoc `definitions.js` (required in Closure)
  inheritDoc: [],

  inner: [],
  instance: [],
  interface: [],
  kind: [],
  lends: [],
  license: [],
  listens: [],
  member: [
    'var',
  ],
  memberof: [],
  'memberof!': [],
  mixes: [],
  mixin: [],

  module: [],
  name: [],
  namespace: [],
  override: [],
  package: [],
  param: [
    'arg',
    'argument',
  ],
  private: [],
  property: [
    'prop',
  ],
  protected: [],
  public: [],
  readonly: [],
  requires: [],
  returns: [
    'return',
  ],
  see: [],
  since: [],
  static: [],
  summary: [],

  this: [],
  throws: [
    'exception',
  ],
  todo: [],
  tutorial: [],
  type: [],
  typedef: [],
  variation: [],
  version: [],
  yields: [
    'yield',
  ],
};

/**
 * @see {@link https://tsdoc.org/}
 * @type {AliasedTags}
 */
const tsdocTags = {
  alpha: [],
  beta: [],
  decorator: [],
  eventProperty: [],
  experimental: [],
  packageDocumentation: [],
  privateRemarks: [],
  remarks: [],
  sealed: [],
};

/**
 * @see {@link https://typedoc.org/documents/Tags.html}
 * @type {AliasedTags}
 */
const typedocTags = {
  category: [],
  // https://typedoc.org/documents/Tags._category.html#category-descriptions
  categoryDescription: [],
  // https://typedoc.org/documents/Tags._group.html#disablegroups
  disableGroups: [],
  document: [],
  expand: [],
  // https://typedoc.org/documents/Tags._expand.html#expandtype
  expandType: [],
  group: [],
  // https://typedoc.org/documents/Tags._group.html#groupdescription
  groupDescription: [],
  hidden: [],
  // https://typedoc.org/documents/Tags._category.html#navigation-customization
  hideCategories: [],
  // https://typedoc.org/documents/Tags._group.html#navigation-customization
  hideGroups: [],
  inline: [],
  // https://typedoc.org/documents/Tags._inline.html#inlinetype
  inlineType: [],
  mergeModuleWith: [],
  // https://typedoc.org/documents/Tags._expand.html#preventexpand
  preventExpand: [],
  // https://typedoc.org/documents/Tags._inline.html#preventinline
  preventInline: [],
  primaryExport: [],
  // https://typedoc.org/documents/Tags._category.html#navigation-customization
  showCategories: [],
  // https://typedoc.org/documents/Tags._group.html#navigation-customization
  showGroups: [],
  sortStrategy: [],
  useDeclaredType: [],
};

/**
 * @type {AliasedTags}
 */
const typeScriptTags = {
  ...jsdocTags,
  ...tsdocTags,
  ...typedocTags,

  // https://github.com/microsoft/TypeScript/issues/22160
  // https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#the-jsdoc-import-tag
  import: [],

  // https://www.typescriptlang.org/tsconfig/#stripInternal
  internal: [],

  // https://typedoc.org/documents/Tags.TypeScript_Tags.html
  jsx: [],

  // https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#overload-support-in-jsdoc
  overload: [],

  // https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#satisfies-support-in-jsdoc
  satisfies: [],

  // `@template` is also in TypeScript per:
  //      https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html#template
  template: [
    // Alias as per https://typedoc.org/documents/Tags._typeParam.html
    'typeParam',
  ],
};

/**
 * @type {AliasedTags}
 */
const undocumentedClosureTags = {
  // These are in Closure source but not in jsdoc source nor in the Closure
  //  docs: https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/parsing/Annotation.java
  closurePrimitive: [],
  customElement: [],
  expose: [],
  hidden: [],
  idGenerator: [],
  meaning: [],
  mixinClass: [],
  mixinFunction: [],
  ngInject: [],
  owner: [],
  typeSummary: [],
  wizaction: [],
};

const {
  /* eslint-disable no-unused-vars */
  inheritdoc,
  internal,
  overload,
  // Will be inverted to prefer `return`
  returns,

  satisfies,
  /* eslint-enable no-unused-vars */
  ...typeScriptTagsInClosure
} = typeScriptTags;

/**
 * @type {AliasedTags}
 */
const closureTags = {
  ...typeScriptTagsInClosure,
  ...undocumentedClosureTags,

  // From https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler
  // These are all recognized in https://github.com/jsdoc/jsdoc/blob/master/packages/jsdoc/lib/jsdoc/tag/dictionary/definitions.js
  //   except for the experimental `noinline` and the casing differences noted below

  // Defined as a synonym of `const` in jsdoc `definitions.js`
  define: [],

  dict: [],
  export: [],
  externs: [],
  final: [],

  // With casing distinct from jsdoc `definitions.js`
  implicitCast: [],

  noalias: [],
  nocollapse: [],
  nocompile: [],
  noinline: [],
  nosideeffects: [],
  polymer: [],
  polymerBehavior: [],
  preserve: [],

  // Defined as a synonym of `interface` in jsdoc `definitions.js`
  record: [],

  return: [
    'returns',
  ],

  struct: [],
  suppress: [],

  unrestricted: [],
};

export {
  closureTags,
  jsdocTags,
  typeScriptTags,
};
