import Choose from 'components/elements/Choose'
import { Link } from 'components/elements/Link'
import Notification from 'components/elements/Notification/Notification'
import React, { useCallback, useState, useEffect, useRef } from 'react'
import { findDemoLinkByVariations } from 'helpers/demo-links'
import { hasDomainLikeHostname, normalizeUrl } from 'helpers/url-input'
import { urlVariations } from 'helpers/url-variations'
import { useQueryState } from 'components/hook/use-query-state'
import mql from '@microlink/mql'
import prependHttp from 'prepend-http'

const ErrorMessage = ({ more }) => {
  const text = 'The URL has something weird.'

  return (
    <Notification.Error>
      <Choose>
        <Choose.When condition={!!more}>
          {text}{' '}
          <Link display='inline' href={more}>
            Report it.
          </Link>
        </Choose.When>
        <Choose.Otherwise>{text}</Choose.Otherwise>
      </Choose>
    </Notification.Error>
  )
}

const fetchFromApi = (url, fromCache, opts) => {
  const variations = urlVariations(url)
  return fromCache(variations, opts) || mql(url, opts)
}

const defaultFromCache = variations => findDemoLinkByVariations(variations)
const withProtocol = url =>
  Array.isArray(url) ? url.map(prependHttp) : prependHttp(url)
const isValidQueryUrl = url => {
  const urls = Array.isArray(url) ? url : [url]
  return urls.every(value => hasDomainLikeHostname(normalizeUrl(value)))
}
const INVALID_URL_WARNING = 'The URL value is not valid'

const FetchProvider = ({ fromCache = defaultFromCache, mqlOpts, children }) => {
  const [status, setStatus] = useState('initial')
  const [response, setResponse] = useState({})
  const [error, setError] = useState(null)
  const [warning, setWarning] = useState(null)
  const [data, setData] = useState(null)
  const [query, setQuery] = useQueryState()

  const didInitialFetch = useRef(false)
  const warningId = useRef(0)

  const fetchData = useCallback(
    async (url, opts) => {
      try {
        const { queryUrl, syncQuery = true, ...fetchOpts } = opts || {}
        if (syncQuery) {
          setQuery({ url: queryUrl || url, ...fetchOpts })
        }
        setError(null)
        setStatus('fetching')

        const doFetch = url =>
          fetchFromApi(url, fromCache, { ...mqlOpts, ...fetchOpts })

        const { data, response } = Array.isArray(url)
          ? await Promise.all(url.map(doFetch))
          : await doFetch(url)

        setStatus('fetched')
        setData(data)
        setResponse(response)
      } catch (err) {
        console.error('FetchProvider:', err)
        setStatus('error')
        setError(err)
      }
    },
    [setQuery, fromCache, mqlOpts]
  )

  const doFetch = (url, opts) => {
    setWarning(null)
    if (!url || !isValidQueryUrl(url)) {
      warningId.current += 1
      setWarning({ id: warningId.current, children: INVALID_URL_WARNING })
      return
    }
    return fetchData(url, opts)
  }

  useEffect(() => {
    if (didInitialFetch.current) return

    const { url, ...opts } = query
    if (!url) return

    didInitialFetch.current = true
    if (isValidQueryUrl(url)) {
      fetchData(withProtocol(url), { ...opts, queryUrl: url, syncQuery: false })
    }
  }, [query?.url, fetchData])

  return (
    <>
      {error ? <ErrorMessage {...error} /> : null}
      {!error && warning && (
        <Notification.Warning key={warning.id} {...warning} />
      )}
      {children({ status, doFetch, data, response, error, warning })}
    </>
  )
}

export default FetchProvider
