import styles from './text.module.css'
import ReactMarkdown from 'react-markdown'
import YouTube from 'react-youtube'
import gfm from 'remark-gfm'
import { LightAsync as SyntaxHighlighter } from 'react-syntax-highlighter'
import atomDark from 'react-syntax-highlighter/dist/cjs/styles/prism/atom-dark'
import mention from '../lib/remark-mention'
import sub from '../lib/remark-sub'
import React, { useState, memo, useRef, useCallback, useMemo, useEffect } from 'react'
import GithubSlugger from 'github-slugger'
import LinkIcon from '../svgs/link.svg'
import Thumb from '../svgs/thumb-up-fill.svg'
import { toString } from 'mdast-util-to-string'
import copy from 'clipboard-copy'
import ZoomableImage, { decodeOriginalUrl } from './image'
import { IMGPROXY_URL_REGEXP } from '../lib/url'
import reactStringReplace from 'react-string-replace'
import { rehypeInlineCodeProperty } from '../lib/md'
import { Button } from 'react-bootstrap'

export function SearchText ({ text }) {
  return (
    <div className={styles.text}>
      <p className={styles.p}>
        {reactStringReplace(text, /\*\*\*([^*]+)\*\*\*/g, (match, i) => {
          return <mark key={`strong-${match}-${i}`}>{match}</mark>
        })}
      </p>
    </div>
  )
}

// this is one of the slowest components to render
export default memo(function Text ({ nofollow, imgproxyUrls, children, tab, ...outerProps }) {
  const [overflowing, setOverflowing] = useState(false)
  const [show, setShow] = useState(false)
  const containerRef = useRef(null)

  useEffect(() => {
    const container = containerRef.current
    if (!container || overflowing) return

    setOverflowing(container.scrollHeight > window.innerHeight * 2)
  }, [containerRef.current])

  // all the reactStringReplace calls are to facilitate search highlighting
  const slugger = useRef(new GithubSlugger())

  const Heading = useCallback(({ children, node, ...props }) => {
    const [copied, setCopied] = useState(false)
    const { noFragments, topLevel } = outerProps
    const id = useMemo(() =>
      noFragments ? undefined : slugger?.current?.slug(toString(node).replace(/[^\w\-\s]+/gi, '')), [node, noFragments, slugger])
    const h = useMemo(() => {
      if (topLevel) {
        return node?.TagName
      }

      const h = parseInt(node?.tagName?.replace('h', '') || 0)
      if (h < 4) return `h${h + 3}`

      return 'h6'
    }, [node, topLevel])
    const Icon = copied ? Thumb : LinkIcon

    return (
      <span className={styles.heading}>
        {React.createElement(h || node?.tagName, { id, ...props }, children)}
        {!noFragments && topLevel &&
          <a className={`${styles.headingLink} ${copied ? styles.copied : ''}`} href={`#${id}`}>
            <Icon
              onClick={() => {
                const location = new URL(window.location)
                location.hash = `${id}`
                copy(location.href)
                setTimeout(() => setCopied(false), 1500)
                setCopied(true)
              }}
              width={18}
              height={18}
              className='fill-grey'
            />
          </a>}
      </span>
    )
  }, [outerProps, slugger.current])

  const Table = useCallback(({ node, ...props }) =>
    <span className='table-responsive'>
      <table className='table table-bordered table-sm' {...props} />
    </span>, [])

  const Code = useCallback(({ node, inline, className, children, style, ...props }) => {
    return inline
      ? (
        <code className={className} {...props}>
          {children}
        </code>
        )
      : (
        <SyntaxHighlighter style={atomDark} language='text' PreTag='div' {...props}>
          {children}
        </SyntaxHighlighter>
        )
  }, [])

  const P = useCallback(({ children, node, ...props }) => <div className={styles.p} {...props}>{children}</div>, [])

  const Img = useCallback(({ node, src, ...props }) => {
    const url = IMGPROXY_URL_REGEXP.test(src) ? decodeOriginalUrl(src) : src
    const srcSet = imgproxyUrls?.[url]
    return <ZoomableImage srcSet={srcSet} tab={tab} src={src} {...props} {...outerProps} />
  }, [imgproxyUrls, outerProps, tab])

  return (
    <div className={`${styles.text} ${show ? styles.textUncontained : overflowing ? styles.textContained : ''}`} ref={containerRef}>
      <ReactMarkdown
        components={{
          h1: Heading,
          h2: Heading,
          h3: Heading,
          h4: Heading,
          h5: Heading,
          h6: Heading,
          table: Table,
          p: P,
          code: Code,
          a: ({ node, href, children, ...props }) => {
            children = children ? Array.isArray(children) ? children : [children] : []
            // don't allow zoomable images to be wrapped in links
            if (children.some(e => e?.props?.node?.tagName === 'img')) {
              return <>{children}</>
            }

            // If [text](url) was parsed as <a> and text is not empty and not a link itself,
            // we don't render it as an image since it was probably a conscious choice to include text.
            const text = children[0]
            if (!!text && !/^https?:\/\//.test(text)) {
              return (
                <a target='_blank' rel={`noreferrer ${nofollow ? 'nofollow' : ''} noopener`} href={href}>{text}</a>
              )
            }

            // if the link is to a youtube video, render the video
            const youtube = href.match(/(https?:\/\/)?((www\.)?(youtube(-nocookie)?|youtube.googleapis)\.com.*(v\/|v=|vi=|vi\/|e\/|embed\/|user\/.*\/u\/\d+\/)|youtu\.be\/)(?<id>[_0-9a-z-]+)((?:\?|&)(?:t|start)=(?<start>\d+))?/i)
            if (youtube?.groups?.id) {
              return (
                <div style={{ maxWidth: outerProps.topLevel ? '640px' : '320px', paddingRight: '15px', margin: '0.5rem 0' }}>
                  <YouTube
                    videoId={youtube.groups.id} className={styles.youtubeContainer} opts={{
                      playerVars: {
                        start: youtube?.groups?.start
                      }
                    }}
                  />
                </div>
              )
            }

            // assume the link is an image which will fallback to link if it's not
            return <Img src={href} nofollow={nofollow} {...props}>{children}</Img>
          },
          img: Img
        }}
        remarkPlugins={[gfm, mention, sub]}
        rehypePlugins={[rehypeInlineCodeProperty]}
      >
        {children}
      </ReactMarkdown>
      {overflowing && !show &&
        <Button size='lg' variant='info' className={styles.textShowFull} onClick={() => setShow(true)}>
          show full text
        </Button>}
    </div>
  )
})