import { css } from '@emotion/react';
import { spacing, theme } from '@expo/styleguide';
import GithubSlugger from 'github-slugger';
import Link from 'next/link';
import React, { Children, FC, createContext, isValidElement, ReactNode, useContext } from 'react';

import { TextComponentProps } from './types';

import { durations } from '~/ui/foundations/durations';

export const AnchorContext = createContext<GithubSlugger | null>(null);

/**
 * Render the component with anchor elements and properties.
 * This adds the following elements:
 *   - hidden link position
 *   - children of the component
 *   - anchor hover icon
 */
export function withAnchor(Component: FC<TextComponentProps>) {
  function AnchorComponent({ id, children, ...rest }: TextComponentProps) {
    const slug = useSlug(id, children);
    return (
      <Component css={headingStyle} data-id={slug} {...rest}>
        <span css={anchorStyle} id={slug} />
        <Link href={`#${slug}`} passHref>
          <a css={linkStyle}>{children}</a>
        </Link>
      </Component>
    );
  }
  AnchorComponent.displayName = `Anchor(${Component.displayName})`;
  return AnchorComponent;
}

const headingStyle = css({
  position: 'relative',
});

const anchorStyle = css({
  position: 'relative',
  top: -100,
  visibility: 'hidden',
});

const linkStyle = css({
  position: 'relative',
  color: 'inherit',
  textDecoration: 'inherit',

  '::before': {
    content: '"#"',
    position: 'absolute',
    transform: 'translatex(-100%)',
    transition: `opacity ${durations.hover}`,
    opacity: 0,
    color: theme.icon.secondary,
    padding: `0.25em ${spacing[2]}px`,
    fontSize: '0.75em',
  },

  '&:hover': {
    '::before': {
      opacity: 1,
    },
  },
});

function useSlug(id: string | undefined, children: ReactNode) {
  const slugger = useContext(AnchorContext)!;
  let slugText = id;

  if (!slugText) {
    slugText = getTextFromChildren(children);
    maybeWarnMissingID(slugText);
  }

  return slugger.slug(slugText);
}

export function getTextFromChildren(children: ReactNode): string {
  return Children.toArray(children)
    .map(child => {
      if (typeof child === 'string') {
        return child;
      }
      if (isValidElement(child)) {
        return getTextFromChildren(child.props.children);
      }
      return '';
    })
    .join(' ')
    .trim();
}

/** Eventually, we want to get rid of the auto-generating ID. For now, we need to do this */
function maybeWarnMissingID(identifier: ReactNode) {
  // commenting this out, it's not useful to log this warning, only when actually fixing this issue.
  // console.warn(
  //   `Anchor element "${identifier}" is missing ID, please add it manually. Auto-generating anchor IDs will be deprecated.`
  // );
}
