/* global IntersectionObserver */

import React, { useRef, useState, useEffect } from 'react'
import { toRaw, transition, colors, toPx } from 'theme'
import { useLocation } from '@gatsbyjs/reach-router'
import FeatherIcon from 'components/icons/Feather'
import styled, { css } from 'styled-components'
import { Link as GatsbyLink } from 'gatsby'
import { ArrowUpRight } from 'react-feather'

import Flex from '../../components/elements/Flex'

const addActiveClass = isActive => (isActive ? { className: 'active' } : null)

const isInternalLink = (to = '/') => /^\/(?!\/)/.test(to)

const getHash = href => href.replace('/#', '#')

const mergeRel = (...values) => {
  const tokens = values
    .flatMap(value => (value ? value.split(/\s+/) : []))
    .filter(Boolean)

  return tokens.length > 0 ? [...new Set(tokens)].join(' ') : undefined
}

const linkStyle = css`
  text-decoration: inherit;
  color: inherit;
`

const LinkBase = styled('a')`
  ${linkStyle}
`

const PrefetchLink = styled(GatsbyLink)`
  ${linkStyle}
`

const linkIconWrapper = css`
  display: inline-flex;
  align-items: center;

  svg {
    transition: stroke ${transition.medium};
  }

  &:hover {
    svg {
      stroke: ${colors.hoverLink};
    }
  }
`

const Icon = ({ children }) => {
  const [size, setSize] = useState(12)
  const ref = useRef(null)

  useEffect(() => {
    if (ref.current && typeof window !== 'undefined') {
      const computedStyle = window.getComputedStyle(ref.current)
      const size = toRaw(computedStyle['font-size']) * 0.8
      setSize(size)
    }
  }, [])

  return (
    <Flex as='span' ref={ref} css={linkIconWrapper}>
      {children}
      <FeatherIcon icon={ArrowUpRight} size={toPx(size)} />
    </Flex>
  )
}

const onView = (node, fn, opts) => {
  if (!window.IntersectionObserver || !node) return

  const observer = new IntersectionObserver(
    entries => entries.forEach(entry => fn(entry.isIntersecting)),
    {
      root: null,
      rootMargin: '0px',
      threshold: 0.6,
      ...opts
    }
  )
  observer.observe(node)
}

const withBaseLink = Component => {
  const BaseLinkWrapper = ({
    children,
    href,
    externalIcon = true,
    isInternal = isInternalLink(href),
    title,
    onClick,
    rel: relProp,
    ...props
  }) => {
    // In-page hash anchors (#section) are same-page navigation: never open in a
    // new tab or add a noopener rel.
    const isHash = typeof href === 'string' && href.startsWith('#')
    const isSamePage = isInternal || isHash
    const rel = isSamePage ? relProp : mergeRel(relProp, 'noopener noreferrer')
    const target = isSamePage ? undefined : '_blank'

    return (
      <Component {...props}>
        <LinkBase
          title={title}
          href={href}
          rel={rel}
          target={target}
          onClick={onClick}
        >
          {externalIcon ? <Icon>{children}</Icon> : children}
        </LinkBase>
      </Component>
    )
  }

  return BaseLinkWrapper
}

export const withLink = Component => {
  const BaseLink = withBaseLink(Component)

  const LinkWrapper = ({
    actively,
    children,
    href = '/',
    title,
    prefetch = true,
    externalIcon,
    icon,
    onClick,
    rel,
    ...props
  }) => {
    const [isIntersecting, setIsIntersecting] = useState(false)
    const location = useLocation()
    const isInternal = !href || isInternalLink(href)
    const partiallyActive = actively === 'partial'

    useEffect(() => {
      if (isInternal && actively === 'observer') {
        let node
        try {
          node = document.querySelector(getHash(href))
        } catch {
          return
        }
        onView(node, isBeingIntersecting =>
          setIsIntersecting(isBeingIntersecting)
        )
      }
    }, [isInternal, actively, href])

    const getProps = ({ isPartiallyCurrent, isCurrent, location }) =>
      typeof actively === 'function'
        ? addActiveClass(actively({ location }))
        : addActiveClass(
          isIntersecting || partiallyActive ? isPartiallyCurrent : isCurrent
        )

    if (prefetch && isInternal) {
      return (
        <Component {...props}>
          <PrefetchLink
            to={href || location.pathname}
            getProps={getProps}
            title={title}
            onClick={onClick}
            rel={rel}
          >
            {children}
          </PrefetchLink>
        </Component>
      )
    }

    return (
      <BaseLink
        href={href}
        externalIcon={externalIcon}
        isInternal={isInternal}
        title={title}
        onClick={onClick}
        rel={rel}
        {...props}
      >
        {children}
      </BaseLink>
    )
  }

  return LinkWrapper
}

withLink.isInternalLink = isInternalLink
withLink.External = withBaseLink
