"use client";
import { jsx, jsxs } from "../../packages/jsx/src/jsx-runtime.js";
import { forwardRef, useContext, Fragment } from "react";
import { LinkArrowIcon, linkArrowIconStyles } from "./LinkArrowIcon.js";
import { ButtonOrLinkContext, buttonOrLinkHrefClientLinkProps, ButtonOrLink } from "../../atoms/ButtonOrLink/ButtonOrLink.js";
import { useTheme } from "../../theme/useTheme.js";
import { getChildrenChunks } from "../../utils/getChildrenChunks.js";
import { Spacing } from "../../tokens/Spacing.js";
import { buildTransition, buildBorder } from "../../tokens/utils.js";
import { MaybeButtonOrLink } from "../../atoms/ButtonOrLink/MaybeButtonOrLink.js";
import { classNames } from "../../utils/classNames.js";
import { BorderRadius } from "../../tokens/BorderRadius.js";
import { Text } from "../../atoms/Text/Text.js";
const forbiddenPropsIfAncestorButtonOrLink = [
  "disabled",
  "selected",
  "href",
  "type",
  ...buttonOrLinkHrefClientLinkProps
];
const LinkBase = forwardRef(
  ({
    variant = "primary",
    inline = false,
    underline = inline,
    color = variant === "primary" ? "White88" : "White48",
    weight,
    size,
    wrap = true,
    autoIcon = !underline,
    className,
    children,
    ...props
  }, ref) => {
    var _a;
    const { theme } = useTheme();
    const ancestorButtonOrLink = useContext(ButtonOrLinkContext);
    let maybeButtonOrLinkProps;
    if (ancestorButtonOrLink) {
      for (const forbiddenProp of forbiddenPropsIfAncestorButtonOrLink) {
        if (forbiddenProp in props && props[forbiddenProp] != null) {
          throw new Error(
            `A \`Link\` with a \`ButtonOrLink\` ancestor (e.g. \`<Link.Area />\`, \`<ExperimentalCard href />\` with no \`as\` prop, etc.) cannot have a \`${forbiddenProp}\` prop; move it to the ancestor`
          );
        }
      }
      maybeButtonOrLinkProps = {
        ...props,
        onClick: props.onClick,
        as: "span"
      };
    } else if (props.href === void 0) {
      maybeButtonOrLinkProps = { ...props, onClick: props.onClick ?? (() => {
      }), asSpan: inline };
    } else {
      maybeButtonOrLinkProps = { ...props, as: void 0 };
    }
    const childrenChunks = getChildrenChunks(children, true);
    const chunkGap = `min(0.333333em, ${Spacing["8px"]})`;
    const underlineThickness = `max(0.1em, ${Spacing["2px"]})`;
    const underlineOffset = `min(0.1em, ${Spacing["2px"]})`;
    const underlineHoverOffset = `min(0.2em, ${Spacing["4px"]})`;
    const underlineStyles = {
      pb: underlineHoverOffset,
      backgroundImage: `linear-gradient(to bottom, ${theme.colors.Purple88}, ${theme.colors.Purple88})`,
      // Using `chunkGap` in `backgroundSize` and `backgroundPosition` because the chunks' wrapper has `ml: calc(-1 * ${chunkGap})` (see below for why) and we want the underline to be flush with the first chunk
      backgroundSize: `calc(100% - ${chunkGap}) ${underlineThickness}`,
      backgroundPosition: `${chunkGap} calc(100% - ${underlineHoverOffset} + ${underlineOffset})`,
      transition: buildTransition("background-position"),
      '.gds-ButtonOrLink:not(:disabled, [aria-disabled="true"]):hover &': {
        backgroundImage: `linear-gradient(to bottom, ${theme.colors.Purple}, ${theme.colors.Purple})`,
        backgroundPosition: `${chunkGap} 100%`
      },
      '.gds-ButtonOrLink:not(:disabled, [aria-disabled="true"]):active &': {
        backgroundImage: `linear-gradient(to bottom, ${theme.colors.Purple64}, ${theme.colors.Purple64})`,
        backgroundPosition: `${chunkGap} 100%`,
        transition: "none"
      },
      '.gds-ButtonOrLink:is(:disabled, [aria-disabled="true"]) &': {
        backgroundImage: `linear-gradient(to bottom, ${theme.colors.Purple48}, ${theme.colors.Purple48})`
      },
      // Hide the underline when the focus ring is visible
      ".gds-Link--focusRing:focus-visible &": {
        backgroundImage: "none !important"
      }
    };
    if (autoIcon && !((_a = childrenChunks[childrenChunks.length - 1]) == null ? void 0 : _a.isIcon)) {
      childrenChunks.push({
        isIcon: true,
        // eslint-disable-next-line react/jsx-key
        children: [/* @__PURE__ */ jsx(LinkArrowIcon, {})]
      });
    }
    return /* @__PURE__ */ jsx(Text, { as: "child", color, weight, size, children: /* @__PURE__ */ jsx(
      MaybeButtonOrLink,
      {
        ref,
        sx: {
          display: inline ? "inline" : "block",
          width: "fit-content",
          outline: "none",
          transition: buildTransition("NONE")
          // Set the transition duration, timing function, and delay so the child element can inherit them; this allows them to be customized with `sx`
        },
        className: classNames(["gds-Link", !ancestorButtonOrLink ? "gds-Link--focusRing" : null, className]),
        ...maybeButtonOrLinkProps,
        children: /* @__PURE__ */ jsx(
          "span",
          {
            sx: {
              position: "static",
              // Required for the horizontal padding to be pointable in Safari
              mx: `-${Spacing["12px"]}`,
              my: `-${Spacing["4px"]}`,
              px: Spacing["12px"],
              py: Spacing["4px"],
              borderRadius: BorderRadius.FULL,
              boxDecorationBreak: "clone",
              transition: buildTransition("COLORS"),
              transitionDuration: "inherit",
              transitionTimingFunction: "inherit",
              transitionDelay: "inherit",
              ".gds-Link--focusRing:focus-visible &": {
                outline: buildBorder("Purple16", "8px"),
                outlineOffset: Spacing["4px"]
                // Using `outline-offset` to achieve the design's padding without having a ridiculous tap area
              },
              '.gds-ButtonOrLink:not(:disabled, [aria-disabled="true"]):is(:hover, :focus-visible) &': {
                color: variant === "primary" ? "White" : "White88"
              },
              '.gds-ButtonOrLink:not(:disabled, [aria-disabled="true"]):active &': {
                color: "White64",
                transition: "none"
              },
              '.gds-ButtonOrLink:is(:disabled, [aria-disabled="true"]) &': {
                color: "White32"
              },
              // Safari and Firefox hacks to fix issue where links wrap too early
              "@supports (-webkit-hyphens: none)": {
                boxDecorationBreak: "slice"
              },
              "@-moz-document url-prefix()": {
                boxDecorationBreak: "slice",
                // This doesn't fix the issue in Firefox, but it makes focus rings look better
                // This is the actual fix for Firefox, which is really annoying... hopefully the bug gets fixed soon: https://bugzilla.mozilla.org/show_bug.cgi?id=1849469
                mx: 0,
                px: 0
              }
            },
            children: /* @__PURE__ */ jsx(
              "span",
              {
                sx: {
                  position: "inherit",
                  mx: "inherit",
                  px: "inherit",
                  py: "inherit",
                  borderRadius: "inherit",
                  boxDecorationBreak: "inherit",
                  ".gds-Link--focusRing:focus-visible &": {
                    outline: buildBorder("Purple"),
                    outlineOffset: "inherit"
                  }
                },
                children: /* @__PURE__ */ jsx(
                  "span",
                  {
                    className: "nested-icon:inline-flex",
                    sx: {
                      // The following `z-index: 1` is for multiple reasons:
                      // 1. Prevent the link's underline from rendering behind sibling elements on the next line, if any (e.g. inline code)
                      // 2. Create a stacking context to prevent a nested inline code from rendering behind an ancestor's background due to its `z-index: -1` (see below)
                      // 3. Prioritize this part of the link when hovering two links whose tap areas overlap each other due to the parents' padding
                      //    (this last one is why the `zIndex` is not on a parent element)
                      zIndex: 1,
                      // Prevent the link's underline from rendering behind a nested inline code's background, if any
                      ".gds-Code": { zIndex: -1 },
                      // Default to not wrapping to prevent wrapping between chunks; text chunks set this back to `normal` if `wrap` is `true`
                      whiteSpace: "nowrap",
                      // Underline styles
                      ...underline ? underlineStyles : {},
                      // Chunk styles
                      "& > *": {
                        ml: chunkGap,
                        // Force a specificity of (0,0,1) to allow consumers to override these styles
                        "*:is(html, *) *:where(&:is(.gds-icon, .gds-Icon))": {
                          "--gds-Icon-display-override": "inline-block",
                          "--gds-Icon-size-default": "1em",
                          verticalAlign: "middle",
                          top: "-0.1em",
                          ...linkArrowIconStyles
                          // Make sure any arrow icon passed manually is still animated like `LinkArrowIcon`
                        },
                        "&:not(.gds-icon, .gds-Icon)": {
                          ...wrap ? { whiteSpace: "normal" } : {}
                          // The underline styles could also be here if we didn't want the underline to span the icon(s), but in the latest design, we do
                        }
                      },
                      // Cancel out the `ml` on the first chunk (using this instead of `:first-child` because the first chunk might have `display: none`)
                      ml: `calc(-1 * ${chunkGap})`
                    },
                    children: childrenChunks.map((childrenChunk, childrenChunkIndex) => {
                      if (childrenChunk.isIcon) {
                        const hasTextOrIconBefore = childrenChunkIndex > 0;
                        const hasTextAfter = childrenChunks[childrenChunkIndex + 1] && !childrenChunks[childrenChunkIndex + 1].isIcon;
                        return /* @__PURE__ */ jsxs(Fragment, { children: [
                          hasTextOrIconBefore ? /* @__PURE__ */ jsx(Fragment, { children: "⁠" }) : null,
                          childrenChunk.children[0],
                          hasTextAfter ? /* @__PURE__ */ jsx(Fragment, { children: "⁠" }) : null
                        ] }, childrenChunkIndex);
                      } else {
                        if (childrenChunk.children.length > 1 || childrenChunk.children.some((child) => typeof child === "string")) {
                          return /* @__PURE__ */ jsx("span", { children: childrenChunk.children }, childrenChunkIndex);
                        } else {
                          return /* @__PURE__ */ jsx(Fragment, { children: childrenChunk.children }, childrenChunkIndex);
                        }
                      }
                    })
                  }
                )
              }
            )
          }
        )
      }
    ) });
  }
);
LinkBase.displayName = "Link";
const LinkPrimary = forwardRef((props, ref) => /* @__PURE__ */ jsx(LinkBase, { ref, ...props, variant: "primary" }));
LinkPrimary.displayName = "Link.Primary";
const LinkSecondary = forwardRef((props, ref) => /* @__PURE__ */ jsx(LinkBase, { ref, ...props, variant: "secondary" }));
LinkSecondary.displayName = "Link.Secondary";
const LinkInline = forwardRef((props, ref) => (
  // Intentionally allowing `inline` and `underline` to be overridden because there are two reasons why a consumer might use `Link.Inline`:
  // 1. `display: inline`, in which case they may not want an underline so they can disable it with `underline={false}`
  // 2. The underline, in which case they may want `display: block` instead of `display: inline` so they can with `inline={false}` (or with `sx`, but whatever)
  /* @__PURE__ */ jsx(LinkBase, { ref, inline: true, underline: true, ...props })
));
LinkInline.displayName = "Link.Inline";
const LinkUnstyled = forwardRef((props, ref) => {
  return /* @__PURE__ */ jsx(ButtonOrLink, { ref, ...props });
});
LinkUnstyled.displayName = "Link.Unstyled";
const LinkArea = forwardRef(
  ({ innerFocusRing = false, className, ...props }, ref) => {
    return /* @__PURE__ */ jsx(
      ButtonOrLink,
      {
        ref,
        className: classNames(["gds-LinkArea", innerFocusRing ? "gds-Link--focusRing" : null, className]),
        ...props
      }
    );
  }
);
LinkArea.displayName = "Link.Area";
const Link = Object.assign(LinkBase, {
  /** Primary link; the default variant. */
  Primary: LinkPrimary,
  /** Secondary link; more faded than primary. */
  Secondary: LinkSecondary,
  /** Inline link; is `display: inline` and has an underline by default. */
  Inline: LinkInline,
  /** Unstyled link; use instead of `<a>` to automatically use the client-side router defined in `GDSProvider` when needed. */
  Unstyled: LinkUnstyled,
  /**
   * Unstyled wrapper component for `Link` that acts as the link's tap area. When a `Link` is wrapped in a `Link.Area`, it cannot
   * have a `href` or related props; those should go on the `Link.Area` instead. The link's hover and active styles get triggered
   * when the area is hovered or active, but only the area's focus styles get triggered by default, since there should be a single
   * focus ring covering the whole tap area (this can be customized with `innerFocusRing`).
   */
  Area: LinkArea
});
export {
  Link
};
