import { useQueryClient } from '@tanstack/react-query'
import { forwardRef, useMemo } from 'react'
import { flushSync } from 'react-dom'
import { createPath, useNavigate } from 'react-router-dom'

import { useOverlayStore } from '@/components/RouteBlockerOverlay'
import { prefetchForURL } from '@/lib/prefetchers'
import { cn } from '@/lib/utils'

import type { AnchorHTMLAttributes, MouseEvent, TouchEvent } from 'react'
import type { To } from 'react-router-dom'

type Props = AnchorHTMLAttributes<HTMLAnchorElement> & {
    to:             To
    state?:         Record<string, string>
    replace?:       boolean
    className?:     string
    cancelOnError?: boolean }


export default forwardRef<HTMLAnchorElement, Props> (({
    to,
    replace,
    className,
    state,
    onMouseEnter,
    onTouchStart,
    onClick,
    cancelOnError = false,
    ...rest }, ref) => {
  if ('onClick' in rest)
    delete rest['onClick']

  const navigate = useNavigate ()
  const qc = useQueryClient ()
  const url = useMemo (() => {
    const path = (typeof to === 'string') ? to : createPath (to)
    return (new URL (path, location.origin)).toString ()
  }, [to])
  const setOverlay = useOverlayStore (s => s.setActive)

  const doPrefetch = async () => {
    try
    {
      await prefetchForURL (qc, url)
      return true
    }
    catch (e)
    {
      console.error ('データ取得エラー', e)
      return false
    }
  }

  const handleMouseEnter = async (ev: MouseEvent<HTMLAnchorElement>) => {
    onMouseEnter?.(ev)
    await doPrefetch ()
  }

  const handleTouchStart = async (ev: TouchEvent<HTMLAnchorElement>) => {
    onTouchStart?.(ev)
    await doPrefetch ()
  }

  const handleClick = async (ev: MouseEvent<HTMLAnchorElement>) => {
    try
    {
      onClick?.(ev)

      if (ev.defaultPrevented
	  || ev.metaKey
	  || ev.ctrlKey
	  || ev.shiftKey
	  || ev.altKey
	  || (rest.target && rest.target !== '_self'))
	return

      ev.preventDefault ()

      flushSync (() => {
	setOverlay (true)
      })
      const ok = await doPrefetch ()
      flushSync (() => {
	setOverlay (false)
      })

      if (!(ok) && cancelOnError)
	return

      navigate (to, { replace, ...(state && { state }) })
    }
    catch (ex)
    {
      console.log (ex)
      ev.preventDefault ()
    }
  }

  return (
      <a ref={ref}
	 href={typeof to === 'string' ? to : createPath (to)}
	 onMouseEnter={handleMouseEnter}
	 onTouchStart={handleTouchStart}
	 onClick={handleClick}
	 className={cn ('cursor-pointer', className)}
	 {...rest}/>)
})
