/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import _ from 'lodash';
import {createDocsByIdIndex} from './docs';
import type {VersionTag, VersionTags} from './types';
import type {
  SidebarItemDoc,
  SidebarItem,
  SidebarItemCategory,
  SidebarItemCategoryLink,
} from './sidebars/types';
import type {
  PropSidebars,
  PropVersionMetadata,
  PropSidebarItem,
  PropSidebarItemCategory,
  PropTagDocList,
  PropTagDocListDoc,
  PropTagsListPage,
  PropSidebarItemLink,
  PropVersionDocs,
  DocMetadata,
  LoadedVersion,
} from '@docusaurus/plugin-content-docs';

export function toSidebarDocItemLinkProp({
  item,
  doc,
}: {
  item: SidebarItemDoc;
  doc: Pick<
    DocMetadata,
    'id' | 'title' | 'permalink' | 'unlisted' | 'frontMatter'
  >;
}): PropSidebarItemLink {
  const {
    id,
    title,
    permalink,
    frontMatter: {
      sidebar_label: sidebarLabel,
      sidebar_custom_props: customProps,
    },
    unlisted,
  } = doc;
  return {
    type: 'link',
    label: sidebarLabel ?? item.label ?? title,
    href: permalink,
    className: item.className,
    customProps: item.customProps ?? customProps,
    docId: id,
    unlisted,
  };
}

export function toSidebarsProp(loadedVersion: LoadedVersion): PropSidebars {
  const docsById = createDocsByIdIndex(loadedVersion.docs);

  function getDocById(docId: string): DocMetadata {
    const docMetadata = docsById[docId];
    if (!docMetadata) {
      throw new Error(
        `Invalid sidebars file. The document with id "${docId}" was used in the sidebar, but no document with this id could be found.
Available document ids are:
- ${Object.keys(docsById).sort().join('\n- ')}`,
      );
    }
    return docMetadata;
  }

  const convertDocLink = (item: SidebarItemDoc): PropSidebarItemLink => {
    const doc = getDocById(item.id);
    return toSidebarDocItemLinkProp({item, doc});
  };

  function getCategoryLinkHref(
    link: SidebarItemCategoryLink | undefined,
  ): string | undefined {
    switch (link?.type) {
      case 'doc':
        return getDocById(link.id).permalink;
      case 'generated-index':
        return link.permalink;
      default:
        return undefined;
    }
  }

  function getCategoryLinkUnlisted(
    link: SidebarItemCategoryLink | undefined,
  ): boolean {
    if (link?.type === 'doc') {
      return getDocById(link.id).unlisted;
    }
    return false;
  }

  function getCategoryLinkCustomProps(
    link: SidebarItemCategoryLink | undefined,
  ) {
    switch (link?.type) {
      case 'doc':
        return getDocById(link.id).frontMatter.sidebar_custom_props;
      default:
        return undefined;
    }
  }

  function convertCategory(item: SidebarItemCategory): PropSidebarItemCategory {
    const {link, ...rest} = item;
    const href = getCategoryLinkHref(link);
    const linkUnlisted = getCategoryLinkUnlisted(link);
    const customProps = item.customProps ?? getCategoryLinkCustomProps(link);

    return {
      ...rest,
      items: item.items.map(normalizeItem),
      ...(href && {href}),
      ...(linkUnlisted && {linkUnlisted}),
      ...(customProps && {customProps}),
    };
  }

  function normalizeItem(item: SidebarItem): PropSidebarItem {
    switch (item.type) {
      case 'category':
        return convertCategory(item);
      case 'ref':
      case 'doc':
        return convertDocLink(item);
      case 'link':
      default:
        return item;
    }
  }

  // Transform the sidebar so that all sidebar item will be in the
  // form of 'link' or 'category' only.
  // This is what will be passed as props to the UI component.
  return _.mapValues(loadedVersion.sidebars, (items) =>
    items.map(normalizeItem),
  );
}

function toVersionDocsProp(loadedVersion: LoadedVersion): PropVersionDocs {
  return Object.fromEntries(
    loadedVersion.docs.map((doc) => [
      doc.id,
      {
        id: doc.id,
        title: doc.title,
        description: doc.description,
        sidebar: doc.sidebar,
      },
    ]),
  );
}

export function toVersionMetadataProp(
  pluginId: string,
  loadedVersion: LoadedVersion,
): PropVersionMetadata {
  return {
    pluginId,
    version: loadedVersion.versionName,
    label: loadedVersion.label,
    banner: loadedVersion.banner,
    badge: loadedVersion.badge,
    noIndex: loadedVersion.noIndex,
    className: loadedVersion.className,
    isLast: loadedVersion.isLast,
    docsSidebars: toSidebarsProp(loadedVersion),
    docs: toVersionDocsProp(loadedVersion),
  };
}

export function toTagDocListProp({
  allTagsPath,
  tag,
  docs,
}: {
  allTagsPath: string;
  tag: VersionTag;
  docs: DocMetadata[];
}): PropTagDocList {
  function toDocListProp(): PropTagDocListDoc[] {
    const list = _.compact(
      tag.docIds.map((id) => docs.find((doc) => doc.id === id)),
    );
    // Sort docs by title
    list.sort((doc1, doc2) => doc1.title.localeCompare(doc2.title));
    return list.map((doc) => ({
      id: doc.id,
      title: doc.title,
      description: doc.description,
      permalink: doc.permalink,
    }));
  }

  return {
    label: tag.label,
    permalink: tag.permalink,
    allTagsPath,
    count: tag.docIds.length,
    items: toDocListProp(),
    unlisted: tag.unlisted,
  };
}

export function toTagsListTagsProp(
  versionTags: VersionTags,
): PropTagsListPage['tags'] {
  return Object.values(versionTags)
    .filter((tagValue) => !tagValue.unlisted)
    .map((tagValue) => ({
      label: tagValue.label,
      permalink: tagValue.permalink,
      count: tagValue.docIds.length,
    }));
}
