"use client"
import {
  Link as UILink,
  NonAnchorLink,
  classNames,
  formatHref,
  isExternalLink,
  useNavigation,
} from "@opensea/ui-kit"
import type { Locale, LinkProps as UILinkProps, Url } from "@opensea/ui-kit"
import { useLocale } from "@opensea/ui-kit/hooks"
import NextLink from "next/link"
import { useRouter } from "next/navigation"
import React, { createContext, forwardRef, useContext } from "react"

type onNavigationCompleted = (startTime: number, duration: number) => unknown

export type LinkPrefetch = "onHover" | "inViewport" | false

export type LinkProps = Omit<UILinkProps, "href"> &
  Omit<
    React.ComponentProps<typeof NextLink>,
    "href" | "prefetch" | "legacyBehavior" | "locale"
  > & {
    href: Url
    onNavigationCompleted?: onNavigationCompleted
    prefetch?: LinkPrefetch
    locale?: Locale
  }

const CONTEXT_VALUE = {}
const LinkContext = createContext<typeof CONTEXT_VALUE | undefined>(undefined)

let destination: string | undefined
const originalWindowHistoryPushState =
  typeof window === "undefined" ? () => {} : window.history.pushState

function clearPushState() {
  window.history.pushState = originalWindowHistoryPushState
}

export const Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
  {
    href,
    scroll,
    shallow,
    prefetch = "onHover",
    locale: localeProp,
    onNavigationCompleted,
    ...rest
  },
  ref,
) {
  const { renderLink: NavigationLink } = useNavigation()
  const localeContext = useLocale()
  const locale = localeProp ?? localeContext

  const context = useContext(LinkContext)
  const router = useRouter()
  const LinkComponent = NavigationLink ?? NextLink

  if (isExternalLink(href)) {
    if (context != undefined) {
      return <NonAnchorLink {...rest} href={href} ref={ref} />
    }

    return (
      <LinkContext.Provider value={CONTEXT_VALUE}>
        <UILink href={href} {...rest} ref={ref} />
      </LinkContext.Provider>
    )
  }

  function onPointerEnter(
    event: React.MouseEvent<HTMLAnchorElement | HTMLSpanElement>,
  ) {
    if (prefetch === "onHover") {
      rest.onPointerEnter?.(event as React.PointerEvent<HTMLAnchorElement>)
      router.prefetch(formatHref(href))
    } else {
      rest.onPointerEnter?.(event as React.PointerEvent<HTMLAnchorElement>)
    }
  }

  function onClick(event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) {
    // We want to open the link in a new tab if the user uses the middle mouse button or the command key.
    if (event.metaKey || event.ctrlKey || event.button === 1) {
      event.preventDefault()
      event.stopPropagation()
      window.open(formatHref(href), "_blank")
      return
    }
    rest.onClick?.(event)
    if (onNavigationCompleted) {
      trackNavigation(formatHref(href), onNavigationCompleted)
    }
  }

  const link =
    context === undefined ? (
      <UILink
        {...rest}
        ref={ref}
        onClick={onClick}
        onPointerEnter={onPointerEnter}
      />
    ) : (
      // We are rendeing a Link that's nested within another Link. This is not valid HTML (https://www.w3.org/TR/html401/struct/links.html#h-12.2.2),
      // and can cause rendering issues in React. Instead, we render a span which will behave as a Link (using js APIs)
      <span
        {...rest}
        className={classNames("cursor-pointer", rest.className)}
        ref={ref}
        role="link"
        tabIndex={0}
        onClick={onClick}
        onPointerEnter={onPointerEnter}
      />
    )

  return (
    <LinkContext.Provider value={CONTEXT_VALUE}>
      <LinkComponent
        // Locale prop isn't supported in app router so manually prepend the locale
        href={
          locale === "en-US"
            ? href
            : typeof href === "object"
              ? {
                  ...href,
                  pathname: `/${locale}${href.pathname}`,
                }
              : `/${locale}${href}`
        }
        legacyBehavior
        passHref
        prefetch={prefetch === "inViewport"}
        scroll={scroll}
        shallow={shallow}
      >
        {link}
      </LinkComponent>
    </LinkContext.Provider>
  )
})

const trackNavigation = (
  href: string,
  onNavigationCompleted: onNavigationCompleted,
) => {
  if (href === destination) {
    return
  } else if (destination !== undefined) {
    clearPushState()
  }

  destination = href
  const navigationStart = Date.now()

  type PushStateInput = Parameters<typeof window.history.pushState>

  window.history.pushState = new Proxy(window.history.pushState, {
    apply: (target, thisArg, argArray: PushStateInput) => {
      const duration = Date.now() - navigationStart
      onNavigationCompleted(navigationStart, duration)
      destination = undefined
      clearPushState()
      return target.apply(thisArg, argArray)
    },
  })
}
