import { Button, type ButtonProps } from "@ariakit/react";
import { useState, type ReactNode } from "react";
import { useRevertingState } from "../use-reverting-state.ts";

type AriakitButtonProps = Omit<
  ButtonProps,
  "children" | "onClick" | "value" | "type"
>;

export type CopyToClipboardButtonProps = AriakitButtonProps & {
  value: string | null;
  children: ReactNode;
  revertAfterMs: number;
};

export function CopyToClipboardButton(props: CopyToClipboardButtonProps) {
  const { children, value, revertAfterMs, disabled, ...buttonProps } = props;
  const [copiedTimestamp, setCopiedTimestamp] = useRevertingState<
    number | null
  >(null, revertAfterMs);

  return (
    <Button
      {...buttonProps}
      disabled={disabled || !value}
      type="button"
      onClick={async () => {
        if (value) {
          await navigator.clipboard.writeText(value);
          setCopiedTimestamp(Date.now());
        }
      }}
    >
      {copiedTimestamp ? "Copied!" : children}
    </Button>
  );
}

export type WebShareButtonProps = AriakitButtonProps & {
  children: ReactNode;
  shareData: ShareData | null;
  revertAfterMs: number;
};

export function WebShareButton(props: WebShareButtonProps) {
  const { shareData, children, revertAfterMs, disabled, ...buttonProps } =
    props;

  // Tracking sharing state to prevent duplicate invocations of
  // the share dialog.
  const [sharing, setSharing] = useState(false);

  const [copiedTimestamp, setCopiedTimestamp] = useRevertingState<
    number | null
  >(null, revertAfterMs);

  return (
    <Button
      {...buttonProps}
      disabled={disabled || !shareData || sharing}
      type="button"
      onClick={async () => {
        if (shareData) {
          try {
            setSharing(true);

            await navigator.share(shareData);
            setCopiedTimestamp(Date.now());
          } catch {
            console.info("Not shared");
          } finally {
            setSharing(false);
          }
        }
      }}
    >
      {copiedTimestamp ? "Shared!" : children}
    </Button>
  );
}

export type ShareButtonProps = AriakitButtonProps & {
  /**
   * Button label to use when copy to clipboard is used.
   */
  copyLabel: string;

  /**
   * Button label to use when web share is used.
   */
  shareLabel: string;

  /**
   * Data to be shared by the button.
   *
   * If using copy to clipboard, only the URL value will be used.
   */
  shareData: ShareData | null;

  /**
   * How long should the confirmation message be shown within the button?
   *
   * @default 1500
   */
  revertAfterMs?: number;

  /**
   * Should web share be used instead of copy to clipboard?
   *
   * By default, web share will be used if the browser supports it.
   *
   * @default !!navigator.share
   */
  webShare?: boolean;
};

/**
 * An unstyled button that is pre-configured to either copy provided share data
 * to clipboard, or to use web share, depending on which feature is detected
 * in the browser where the button is running.
 *
 * Props that are not consumed by the Share Button itself are passed to the
 * underlying Ariakit Button component.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share
 */
export function ShareButton(props: ShareButtonProps) {
  const {
    copyLabel,
    shareLabel,
    shareData,
    revertAfterMs = 1500,
    webShare = !!navigator.share,
    ...buttonProps
  } = props;

  if (webShare) {
    return (
      <WebShareButton
        {...buttonProps}
        shareData={shareData}
        revertAfterMs={revertAfterMs}
      >
        {shareLabel}
      </WebShareButton>
    );
  }

  return (
    <CopyToClipboardButton
      {...buttonProps}
      revertAfterMs={revertAfterMs}
      value={shareData?.url ?? null}
    >
      {copyLabel}
    </CopyToClipboardButton>
  );
}
