"use client";

import type { ReactNode, Ref } from "react";
import { cx } from "../../utils";

export type FoxButtonType = "primary" | "secondary" | "default" | "error";
export type FoxButtonSize = "lg" | "md" | "sm" | "xsm";

export interface FoxButtonProps {
  /** 시각 계열. 디자인은 Figma 시안(node 390-439) 기준으로 추후 적용한다. */
  type?: FoxButtonType;
  size: FoxButtonSize;
  /** 네이티브 `type` 속성. prop 이름의 `type`이 시각 계열을 쓰므로 분리했다. */
  htmlType?: "button" | "submit" | "reset";
  /** 배치(마진·그리드) 조정용. 디자인 값 주입은 금지 — 모양이 달라야 하면 `type`을 추가한다. */
  className?: string;
  /**
   * 아이콘 엘리먼트. 계열별 색은 슬롯이 `color`로 내려주므로 **`currentColor`로 그린
   * SVG**를 넣어야 시안대로 물든다(primary는 흰색, secondary는 파랑 …).
   * 크기는 슬롯이 `form-icon-*` 토큰으로 정하므로 아이콘 자체에 크기를 두지 않아도 된다.
   */
  leadingIcon?: ReactNode;
  trailingIcon?: ReactNode;
  label: string;
  disabled?: boolean;
  /** 참이면 아무것도 렌더하지 않는다(DOM에 남지 않는다). */
  hidden?: boolean;
  /** 호출부가 소유하는 상태다 — 버튼은 이 값을 읽기만 하고 스스로 켜지 않는다. */
  loading?: boolean;
  fullWidth?: boolean;
  onAction?: () => void;
  /** 포인터가 올라오면 `true`, 벗어나면 `false`. */
  onHoverChange?: (hovered: boolean) => void;
  ref?: Ref<HTMLButtonElement>;
}

/**
 * 클래스명은 `@fox/styles/_fox-button.scss`의 BEM 이름을 그대로 쓴다 — CSS Modules로
 * 감싸지 않는 이유는 이 스타일이 React 밖에서도 쓰여야 하기 때문이다. 이름이 `fox-`
 * 접두사로 전역 유일해서 스코프가 필요 없다.
 *
 * variant마다 `Record`로 고정해 계열을 추가하면 항목 누락이 타입 에러가 되게 한다.
 */
const TYPE_CLASS: Record<FoxButtonType, string> = {
  primary: "fox-button--primary",
  secondary: "fox-button--secondary",
  default: "fox-button--default",
  error: "fox-button--error",
};

const SIZE_CLASS: Record<FoxButtonSize, string> = {
  lg: "fox-button--lg",
  md: "fox-button--md",
  sm: "fox-button--sm",
  xsm: "fox-button--xsm",
};

/**
 * @fox 공용 버튼.
 *
 * 상태를 갖지 않는 프레젠테이션 컴포넌트다 — `loading`·`disabled`는 호출부가 소유하고
 * 버튼은 그에 따라 상호작용을 막기만 한다. 버튼이 스스로 로딩을 켜면 호출부의 상태와
 * 어긋날 수 있어 그 책임을 지지 않는다.
 *
 * 모양은 Figma 시안(관리자페이지 node 390:439)을 토큰으로만 옮긴 것이며 규칙은
 * `@fox/styles/_fox-button.scss`에 있다. `error` 계열만 시안 미정이라 비어 있다.
 *
 * ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
 * (또는 `@use "@fox/styles/fox-button"`)로 한 번 불러와야 한다.
 */
export function FoxButton({
  type = "default",
  size,
  htmlType = "button",
  className,
  leadingIcon,
  trailingIcon,
  label,
  disabled = false,
  hidden = false,
  loading = false,
  fullWidth = false,
  onAction,
  onHoverChange,
  ref,
}: FoxButtonProps) {
  if (hidden) {
    return null;
  }

  // 로딩 중에도 클릭을 막아야 같은 요청이 두 번 나가지 않는다.
  const inactive = disabled || loading;

  return (
    <button
      ref={ref}
      type={htmlType}
      className={cx(
        "fox-button",
        TYPE_CLASS[type],
        SIZE_CLASS[size],
        fullWidth && "fox-button--full-width",
        className
      )}
      disabled={inactive}
      aria-busy={loading || undefined}
      onClick={inactive ? undefined : onAction}
      onMouseEnter={onHoverChange ? () => onHoverChange(true) : undefined}
      onMouseLeave={onHoverChange ? () => onHoverChange(false) : undefined}
    >
      {loading ? (
        // 스피너 비주얼은 시안에 없어 자리만 잡아둔다. 아이콘과 같은 크기를 차지하므로
        // 로딩으로 바뀔 때 버튼 폭이 흔들리지 않는다.
        <span className="fox-button__spinner" aria-hidden="true" />
      ) : (
        leadingIcon && (
          <span className="fox-button__icon" aria-hidden="true">
            {leadingIcon}
          </span>
        )
      )}

      <span className="fox-button__label">{label}</span>

      {trailingIcon && (
        <span className="fox-button__icon" aria-hidden="true">
          {trailingIcon}
        </span>
      )}
    </button>
  );
}
