import NextLink, { LinkProps as NextLinkProps } from 'next/link';
import React from 'react';
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
import { useRouter } from 'next/router';
import { UrlObject } from 'url';

/**
 * Represents the route strategy options.
 */
export enum LinkStrategy {
  Default = 'default',
  Prefetch = 'prefetch',
  None = 'none',
}

function getLinkStrategy(): LinkStrategy {
  let linkStrategy = LinkStrategy.Default;
  if (typeof process !== 'undefined' && process.env.LINK_STRATEGY) {
    linkStrategy = process.env.LINK_STRATEGY as LinkStrategy;
  }
  return linkStrategy;
}

export const linkStrategy: LinkStrategy = getLinkStrategy();

export type LinkProps = Omit<NextLinkProps, 'href'> & {
  href?: string | UrlObject;
  target?: string;
  children: React.ReactNode;
};

export type LinkComponent = React.ForwardRefExoticComponent<Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps> & LinkProps & React.RefAttributes<HTMLAnchorElement>>;

const Link_: LinkComponent = React.forwardRef<HTMLAnchorElement, LinkProps>(
  function Link_({
    children,
    href,
    passHref,
    legacyBehavior,
    prefetch,
    ...props
  }: LinkProps, forwardedRef) {
    passHref = passHref === false ? false : true;
    legacyBehavior = legacyBehavior === false ? false : true;
    /**
     * original prefetch is setted by default to true and activates with viewport visibility
     * we are setting it by default to false, however prefetching will still occur on hover
     */
    prefetch = linkStrategy === LinkStrategy.Default ?
      (prefetch === true ? true : false) :
      (prefetch === false ? false : true);
    if (!href) {
      console.warn('LinkComponent missing href');
    }
    return href ? (
      <NextLink
        ref={forwardedRef}
        href={href}
        passHref={passHref}
        prefetch={prefetch}
        legacyBehavior={legacyBehavior}
        {...props}>{children}</NextLink>
    ) : (
      <>{children}</>
    );
  }
);

const RouterLink_: LinkComponent = React.forwardRef<HTMLAnchorElement, LinkProps>(
  function RouterLink_({
    children: childrenProp,
    href: hrefProp,
    target,
    as:asProp,
    passHref,
    legacyBehavior,
    prefetch,
    locale,
    replace,
    shallow,
    scroll,
    onClick,
    ...props
  }: LinkProps, forwardedRef) {
    const router = useRouter();

    legacyBehavior = legacyBehavior === false ? false : true;
    passHref = passHref === false ? false : true;
    let children: React.ReactNode = childrenProp;
    if (legacyBehavior &&
      (typeof children === 'string' || typeof children === 'number')
    ) {
      children = <a>{children}</a>;
    }

    // This will return the first child, if multiple are provided it will throw an error
    let child: any;
    if (legacyBehavior) {
      if (process.env.NODE_ENV === 'development') {
        if (onClick) {
          console.warn(
            `"onClick" was passed to <Link> with \`href\` of \`${hrefProp}\` but "legacyBehavior" was set. The legacy behavior requires onClick be set on the child of next/link`
          );
        }
        try {
          child = React.Children.only(children);
        } catch (err) {
          if (!children) {
            throw new Error(
              `No children were passed to <Link> with \`href\` of \`${hrefProp}\` but one child is required https://nextjs.org/docs/messages/link-no-children`
            );
          }
          throw new Error(
            `Multiple children were passed to <Link> with \`href\` of \`${hrefProp}\` but only one child is supported https://nextjs.org/docs/messages/link-multiple-children` +
                (typeof window !== 'undefined'
                  ? ' \nOpen your browser\'s console to view the Component stack trace.'
                  : '')
          );
        }
      } else {
        child = React.Children.only(children);
      }
    } else {
      if (process.env.NODE_ENV === 'development') {
        if ((children as any)?.type === 'a') {
          throw new Error(
            'Invalid <Link> with <a> child. Please remove <a> or use <Link legacyBehavior>.\nLearn more: https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor'
          );
        }
      }
    }

    const childRef: any = legacyBehavior
      ? child && typeof child === 'object' && child.ref
      : forwardedRef;

    const setRef = React.useCallback((el: Element) => {
      if (childRef) {
        if (typeof childRef === 'function') {
          childRef(el);
        } else if (typeof childRef === 'object') {
          childRef.current = el;
        }
      }
    }, [childRef]);

    const href = hrefProp?.toString();

    if (!href) {
      console.warn('LinkComponent missing href');
      return <>{childrenProp}</>;
    }

    // console.log('RouterLink_', href, passHref);
    const onNavToUrl = (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
      // const { nodeName } = event.currentTarget;
      // anchors inside an svg have a lowercase nodeName
      // const isAnchorNodeName = nodeName.toUpperCase() === 'A';
      if (href && (href.indexOf('http') !== -1 || target === '_blank')) {
        // ignore click for browser’s default behavior
        return;
      }
      event.preventDefault();
      const routerScroll = scroll ?? true;
      if (href) {
        if (replace) {
          router.replace(href, asProp, { shallow, locale, scroll:routerScroll });
        } else {
          router.push(href, asProp, { shallow, locale, scroll:routerScroll });
        }
      }
    };

    const childProps: {
      href?: string;
      target?: string;
      ref?: any;
      onClick: React.MouseEventHandler<HTMLAnchorElement>;
    } = {
      ref: setRef,
      onClick(event) {
        if (process.env.NODE_ENV !== 'production') {
          if (!event) {
            throw new Error(
              'Component rendered inside next/link has to pass click event to "onClick" prop.'
            );
          }
        }
        if (!legacyBehavior && typeof onClick === 'function') {
          onClick(event);
        }
        if (child.props && typeof child.props.onClick === 'function') {
          child.props.onClick(event);
        }
        if (!router) {
          return;
        }
        if (event.defaultPrevented) {
          return;
        }
        onNavToUrl(event);
      },
    };

    // If child is an <a> tag and doesn't have a href attribute, or if the 'passHref' property is
    // defined, we specify the current 'href', so that repetition is not needed by the user.
    // If the url is absolute, we can bypass the logic to prepend the domain and locale.
    if (href && (href.indexOf('http') !== -1 || target === '_blank')) {
      childProps.href = href;
      childProps.target = target;
    } else if (
      passHref ||
      (child.type === 'a' && !('href' in child.props))
    ) {
      childProps.href = href;
      childProps.target = target;
    }

    return legacyBehavior ? (
      React.cloneElement(child, childProps)
    ) : (
      <a {...props} {...childProps}>
        {children}
      </a>
    );

    /*
    const childrenWithProps = React.Children.map(children, (child, index) => {
      if (React.isValidElement(child)) {
        return React.cloneElement(child, {
          ref: forwardedRef,
          href,
          onClick: onNavToUrl,
          ...props,
        } as any);
      } else {
        return child;
      }
    });
    if (passHref && href) {
      return <>{childrenWithProps}</>;
    }
    if (!href) {
      console.warn('LinkComponent missing href');
    }
    return href ? (
      <a ref={forwardedRef} href={String(href)} onClick={onNavToUrl} {...props}>{children}</a>
    ) : (
      <>{children}</>
    );
    */
  }
);

export const Link = linkStrategy === LinkStrategy.None ? RouterLink_ : Link_;

Link.displayName = 'Link';
