import { Check, Copy, Eye, EyeOff, MoreHorizontal } from "lucide-react";
import * as React from "react";
import { Button } from "@/shared/ui/button";
import {
  copyTextToClipboard,
  writeTextToClipboard,
} from "@/shared/lib/clipboard";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/shared/ui/dropdown-menu";

type NsecAction = {
  icon?: React.ReactNode;
  label: string;
  onSelect: () => void;
  testId?: string;
};

type NsecMaskedDisplayProps = {
  nsec: string;
  /** "bare" drops the boxed chrome for the onboarding spotlight treatment. */
  variant?: "boxed" | "bare";
  /**
   * Called when the user reveals or copies the key. Lets flows that require
   * a backup (e.g. sign-out) gate on actual interaction with the key.
   */
  onKeyInteraction?: () => void;
  /** Replaces the copy icon with an overflow menu containing Copy plus these actions. */
  actions?: readonly NsecAction[];
};

export const ONBOARDING_KEY_FRAME_CLASS =
  "w-full min-w-0 rounded-xl bg-white/50 px-8 py-6";
export const ONBOARDING_KEY_ROW_CLASS = "flex min-w-0 items-center gap-4";
export const ONBOARDING_KEY_TEXT_CLASS = "buzz-onboarding-key-text";

/**
 * Masked nsec display with reveal toggle and copy button.
 *
 * Security invariants:
 * - nsec is not present in the DOM until the user clicks Reveal
 * - select is disabled while masked (user-select: none)
 * - state is cleared when the component unmounts
 */
export function NsecMaskedDisplay({
  nsec,
  variant = "boxed",
  onKeyInteraction,
  actions,
}: NsecMaskedDisplayProps) {
  const [isRevealed, setIsRevealed] = React.useState(false);
  const [isCopied, setIsCopied] = React.useState(false);
  const copyTimerRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);

  React.useEffect(() => {
    return () => {
      setIsRevealed(false);
      if (copyTimerRef.current) {
        clearTimeout(copyTimerRef.current);
      }
    };
  }, []);

  function handleRevealToggle() {
    if (!isRevealed) onKeyInteraction?.();
    setIsRevealed((prev) => !prev);
  }

  async function handleCopy() {
    await writeTextToClipboard(nsec);
    onKeyInteraction?.();
    setIsCopied(true);
    if (copyTimerRef.current) clearTimeout(copyTimerRef.current);
    copyTimerRef.current = setTimeout(() => setIsCopied(false), 2000);
  }

  function handleMenuCopy() {
    copyTextToClipboard(nsec);
    onKeyInteraction?.();
  }

  const isBare = variant === "bare";
  // Mask every character (no plaintext prefix leak), matching the real key's
  // length so toggling reveal never reflows the monospace text (no layout shift).
  // Bullets are joined with a zero-width space: WebKit (WKWebView) will not
  // line-break a run of U+2022 even with break-all/overflow-wrap, so without an
  // explicit break opportunity the masked key overflows its container. The mask
  // is decorative and non-selectable, and copy uses the raw nsec, so the
  // injected ZWSP is never surfaced.
  const maskedNsec = React.useMemo(
    () => Array.from(nsec, () => "•").join("\u200b"),
    [nsec],
  );
  const iconSize = isBare ? "h-6 w-6" : "h-4 w-4";

  return (
    <div
      className={
        isBare
          ? ""
          : "overflow-hidden rounded-lg border border-border/70 bg-muted/30"
      }
    >
      <div
        className={
          isBare
            ? ONBOARDING_KEY_ROW_CLASS
            : "flex min-w-0 items-center gap-2 px-3 py-2"
        }
      >
        {/* Wrapping element is a block inside the flex item, not the flex item
            itself: WebKit (WKWebView) does not wrap a long unbroken string when
            the text node is the flex child directly, even with break-all /
            overflow-wrap. A plain block wraps reliably in every engine. */}
        <div className="min-w-0 flex-1">
          <p
            className={`${
              isBare ? ONBOARDING_KEY_TEXT_CLASS : "text-xs leading-5"
            } ${
              isRevealed
                ? `select-text ${isBare ? "" : "text-foreground"}`
                : `select-none blur-[4px] ${isBare ? "" : "text-muted-foreground"}`
            }`}
            data-testid="nsec-value"
          >
            {isRevealed ? nsec : maskedNsec}
          </p>
        </div>
        <div className={`flex shrink-0 ${isBare ? "gap-1.5" : "gap-1"}`}>
          <Button
            aria-label={isRevealed ? "Hide private key" : "Reveal private key"}
            className={`${isBare ? "h-10 w-10" : "h-7 w-7"} text-muted-foreground hover:text-foreground`}
            data-testid="nsec-reveal-toggle"
            onClick={handleRevealToggle}
            size="icon"
            type="button"
            variant="ghost"
          >
            {isRevealed ? (
              <EyeOff className={iconSize} aria-hidden="true" />
            ) : (
              <Eye className={iconSize} aria-hidden="true" />
            )}
          </Button>
          {actions?.length ? (
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button
                  aria-label="Private key actions"
                  className={`${isBare ? "h-10 w-10" : "h-7 w-7"} text-muted-foreground hover:text-foreground`}
                  data-testid="nsec-actions"
                  size="icon"
                  type="button"
                  variant="ghost"
                >
                  <MoreHorizontal className={iconSize} aria-hidden="true" />
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end" className="min-w-40!">
                <DropdownMenuItem
                  data-testid="nsec-copy"
                  onSelect={handleMenuCopy}
                >
                  <Copy aria-hidden="true" />
                  Copy
                </DropdownMenuItem>
                {actions.map((action) => (
                  <DropdownMenuItem
                    data-testid={action.testId}
                    key={action.label}
                    onSelect={action.onSelect}
                  >
                    {action.icon}
                    {action.label}
                  </DropdownMenuItem>
                ))}
              </DropdownMenuContent>
            </DropdownMenu>
          ) : (
            <Button
              aria-label="Copy private key"
              className={`${isBare ? "h-10 w-10" : "h-7 w-7"} text-muted-foreground hover:text-foreground`}
              data-testid="nsec-copy"
              onClick={() => void handleCopy()}
              size="icon"
              type="button"
              variant="ghost"
            >
              {isCopied ? (
                <Check
                  className={`${iconSize} text-primary`}
                  aria-hidden="true"
                />
              ) : (
                <Copy className={iconSize} aria-hidden="true" />
              )}
            </Button>
          )}
        </div>
      </div>
    </div>
  );
}
