import React, {
  cloneElement,
  ComponentPropsWithoutRef,
  FC,
  forwardRef,
  ReactElement,
  ReactNode,
} from "react";

import { Icon, IconProps } from "../icon/Icon";
import { Tooltip, TooltipProps } from "../tooltip/Tooltip";
import { twMerge } from "../../../tailwind.config";
import { LinkArrow } from "./LinkArrow";

export interface LinkProps
  extends Omit<ComponentPropsWithoutRef<"a">, "className"> {
  href: string;
  children?: ReactNode;
  size?: "16" | "20" | "24";
  disabled?: boolean;
  tooltip?: string;
  tooltipProps?: Partial<TooltipProps>;
  /**
   * Optionally offer programmatic navigation.
   * This will be called with the `href`, and the event's default action will be prevented.
   *
   * Interoperates with `react-router`'s `Link` component.
   */
  navigate?: (href: string) => void;
  ESCAPE_className?: string;
}

const textStyleForSize = {
  "16": "text-style-R100",
  "20": "text-style-R200",
  "24": "text-style-R300",
};

function isIcon(child: ReactNode): child is ReactElement<IconProps> {
  return (
    typeof child === "object" &&
    child !== null &&
    "type" in child &&
    child.type === Icon
  );
}

export type LinkComponent = FC<LinkProps> & { Arrow: typeof LinkArrow };

// False positive because of the type assertion
// eslint-disable-next-line react/display-name
export const Link = forwardRef<HTMLAnchorElement, LinkProps>(
  (
    {
      href,
      children,
      size = "20",
      disabled = false,
      tooltip = undefined,
      tooltipProps = {},
      ESCAPE_className = undefined,
      navigate = undefined,
      ...restProps
    },
    ref
  ) => (
    <Tooltip
      tooltip={tooltip ?? ""}
      disabled={tooltip === undefined}
      {...tooltipProps}
    >
      <a
        ref={ref}
        className={twMerge(
          "group inline-flex items-center no-underline",
          disabled
            ? "cursor-not-allowed text-N80"
            : " cursor-pointer text-B400 hover:text-B500 hover:underline",
          textStyleForSize[size],
          size !== "16" && "gap-2",
          ESCAPE_className
        )}
        href={disabled ? undefined : href}
        aria-disabled={disabled ? "true" : undefined}
        onClick={
          navigate &&
          ((e) => {
            e.preventDefault();
            navigate(href);
          })
        }
        {...restProps}
      >
        {React.Children.map(children, (child) => {
          if (isIcon(child)) {
            return cloneElement(child, {
              size,
              weight: "medium",
              ...child.props,
            });
          }

          return child;
        })}
      </a>
    </Tooltip>
  )
  // ↓ Required to assign `LinkArrow`
) as unknown as LinkComponent;

Link.Arrow = LinkArrow;

Link.displayName = "Link";
