import { jsx, jsxs } from "react/jsx-runtime";
import * as React from "react";
import { X, Upload } from "lucide-react";
import { cn } from "../../lib/utils.js";
import { Empty } from "../Empty/empty.js";
import { CircularProgress } from "../CircularProgress/circular-progress.js";
import { FileItem } from "../FileItem/file-item.js";
import { Button } from "../Button/button.js";
const FileUpload = React.forwardRef(
  ({
    onUpload,
    onReject,
    multiple = false,
    accept,
    maxSize,
    disabled = false,
    variant = "dropzone",
    buttonLabel = "Choose file",
    // i18n-allow: DS default; consumer override via buttonLabel prop
    loading = false,
    loadingTitle = "上傳中…",
    // i18n-allow: DS default; consumer override via loadingTitle prop
    title = "Click or drag file here to upload",
    // i18n-allow: DS default; consumer override via title prop
    description = multiple ? "Support for a single or bulk upload" : "Support for a single file upload",
    // i18n-allow: DS default; consumer override via description prop
    children,
    files,
    fileListMode = "compact",
    onRemove,
    removeAriaLabel = (name) => `移除 ${name}`,
    // i18n-allow: DS default; consumer override via removeAriaLabel prop
    className,
    onClick,
    ...props
  }, ref) => {
    const inputRef = React.useRef(null);
    const [isDragOver, setDragOver] = React.useState(false);
    const filterAndDispatch = (files2) => {
      if (!files2 || files2.length === 0) return;
      const accepted = [];
      const rejectedBySize = [];
      const rejectedByType = [];
      Array.from(files2).forEach((f) => {
        if (maxSize != null && f.size > maxSize) {
          rejectedBySize.push(f);
          return;
        }
        if (accept && !matchAccept(f, accept)) {
          rejectedByType.push(f);
          return;
        }
        accepted.push(f);
      });
      if (rejectedBySize.length) onReject == null ? void 0 : onReject(rejectedBySize, "size");
      if (rejectedByType.length) onReject == null ? void 0 : onReject(rejectedByType, "type");
      if (accepted.length) onUpload == null ? void 0 : onUpload(multiple ? accepted : accepted.slice(0, 1));
    };
    const handleClick = (e) => {
      var _a;
      if (!disabled && !loading) (_a = inputRef.current) == null ? void 0 : _a.click();
      onClick == null ? void 0 : onClick(e);
    };
    const state = disabled ? "disabled" : loading ? "loading" : isDragOver ? "drag-over" : "idle";
    const isBlocked = disabled || loading;
    const hasFiles = Array.isArray(files) && files.length > 0;
    const fileListNode = hasFiles ? /* @__PURE__ */ jsx(
      "ul",
      {
        className: cn(
          "flex flex-col w-full",
          // 2026-06-03 gap SSOT:列間 gap + control→list gap(mt)由「item 有無邊框」單一規則決定,
          // 同值貫穿整個垂直堆疊 — rich(form surface = border card)→ 8px;compact(borderless/bg-pill)→ 4px。
          // 消費 FileItem「List wrapper canonical」(file-item.spec.md),取代原硬寫 gap-2 / mt-3(不分 mode)。
          fileListMode === "rich" ? "gap-2 mt-2" : "gap-1 mt-1"
        ),
        "aria-label": "Uploaded files",
        children: files.map((f) => /* @__PURE__ */ jsx("li", { className: "list-none", children: /* @__PURE__ */ jsx(
          FileItem,
          {
            mode: fileListMode,
            name: f.name,
            status: f.status,
            progress: f.progress,
            description: f.description ?? (f.size != null ? formatBytes(f.size) : void 0),
            thumbnailSrc: f.thumbnailSrc,
            actions: onRemove ? (
              // Collection remove(per-file)— 不是 dismiss surface,故不套 `dismiss` prop。
              // 視覺與 dismiss 一致(text variant + fg-muted dim)— 對齊 inline-action.spec.md
              // 「Dismiss canonical — X close only」(section L204);L229 明文:onRemove callback 不觸發 dismiss prop。
              /* @__PURE__ */ jsx(
                Button,
                {
                  iconOnly: true,
                  variant: "text",
                  size: "xs",
                  startIcon: X,
                  "aria-label": removeAriaLabel(f.name),
                  onClick: (e) => {
                    e.stopPropagation();
                    onRemove(f.id);
                  },
                  className: "text-fg-muted hover:text-foreground"
                }
              )
            ) : void 0
          }
        ) }, f.id))
      }
    ) : null;
    return /* @__PURE__ */ jsxs("div", { ref, className: cn("w-full", hasFiles && "flex flex-col"), children: [
      variant === "button" ? (
        // ── button variant:緊湊觸發(form-friendly,省空間),click-only(無拖放區)──
        /* @__PURE__ */ jsxs("div", { className, ...props, children: [
          /* @__PURE__ */ jsx(
            "input",
            {
              ref: inputRef,
              type: "file",
              className: "hidden",
              multiple,
              accept,
              disabled,
              onChange: (e) => filterAndDispatch(e.target.files)
            }
          ),
          /* @__PURE__ */ jsx(
            Button,
            {
              variant: "tertiary",
              startIcon: Upload,
              loading,
              disabled,
              "aria-busy": loading || void 0,
              onClick: () => {
                var _a;
                if (!disabled && !loading) (_a = inputRef.current) == null ? void 0 : _a.click();
              },
              children: buttonLabel
            }
          )
        ] })
      ) : /* @__PURE__ */ jsxs(
        "div",
        {
          role: "button",
          tabIndex: isBlocked ? -1 : 0,
          "aria-disabled": disabled || void 0,
          "aria-busy": loading || void 0,
          "data-state": state,
          onClick: handleClick,
          onKeyDown: (e) => {
            var _a;
            if (isBlocked) return;
            if (e.key === "Enter" || e.key === " ") {
              e.preventDefault();
              (_a = inputRef.current) == null ? void 0 : _a.click();
            }
          },
          onDragEnter: (e) => {
            if (isBlocked) return;
            e.preventDefault();
            setDragOver(true);
          },
          onDragLeave: (e) => {
            if (isBlocked) return;
            e.preventDefault();
            setDragOver(false);
          },
          onDragOver: (e) => {
            if (isBlocked) return;
            e.preventDefault();
          },
          onDrop: (e) => {
            if (isBlocked) return;
            e.preventDefault();
            setDragOver(false);
            filterAndDispatch(e.dataTransfer.files);
          },
          className: cn(
            // 寬度 w-full 填滿 consumer 容器(user 明示「寬度填滿」);高度由 padding + 內容物決定(不固定 h)
            "flex flex-col items-center justify-center gap-2 text-center w-full",
            // 對稱 padding p-[var(--layout-space-loose)]:四邊等距,density-aware(md=16px / lg=24px),對齊 DS chrome padding canonical。
            // 不再硬寫 px-6 py-10(不對稱+非 token)。內容物(icon + title + description)垂直堆疊由 gap-2 控制
            "rounded-md border-2 border-dashed p-[var(--layout-space-loose)]",
            "cursor-pointer transition-colors",
            "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
            // idle:--border(元件邊框,非 --divider 分隔線 — 2026-06-03 Q2 token 修正)+ surface 底
            "border-border bg-surface",
            // hover = drag-over 統一(2026-06-03 Q2-A 純 border-driven,對齊 Ant Dragger colorPrimaryHover):
            // 兩者都 → primary 邊框,底色維持 surface(不變 bg)。state 信號靠邊框,非底色。
            "hover:border-primary data-[state=drag-over]:border-primary",
            // loading(2026-06-03 Q4:移除 pointer-events-none — 它會讓 cursor-progress 失效;
            // 互動已由 handleClick + drag/key handlers 的 isBlocked guard 擋,不需 pointer-events-none)
            "data-[state=loading]:cursor-progress",
            // disabled(2026-06-03 Q3:語意 token 非 opacity — dashed outline surface 走 DS outline-disabled 慣例;
            // bg→disabled,border 不變色,文字/icon 由 Empty disabled 控;cursor-not-allowed 現在生效)
            "data-[state=disabled]:bg-disabled data-[state=disabled]:cursor-not-allowed",
            className
          ),
          ...props,
          children: [
            /* @__PURE__ */ jsx(
              "input",
              {
                ref: inputRef,
                type: "file",
                className: "hidden",
                multiple,
                accept,
                disabled,
                onChange: (e) => filterAndDispatch(e.target.files)
              }
            ),
            loading ? /* @__PURE__ */ jsx(
              Empty,
              {
                icon: /* @__PURE__ */ jsx(CircularProgress, { size: 48 }),
                title: loadingTitle
              }
            ) : children ?? /* @__PURE__ */ jsx(
              Empty,
              {
                icon: Upload,
                title,
                description,
                disabled
              }
            )
          ]
        }
      ),
      fileListNode
    ] });
  }
);
FileUpload.displayName = "FileUpload";
function formatBytes(n) {
  if (n < 1024) return `${n} B`;
  if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
  if (n < 1024 * 1024 * 1024) return `${(n / (1024 * 1024)).toFixed(1)} MB`;
  return `${(n / (1024 * 1024 * 1024)).toFixed(1)} GB`;
}
function matchAccept(file, accept) {
  const patterns = accept.split(",").map((s) => s.trim().toLowerCase());
  const fileName = file.name.toLowerCase();
  const fileType = file.type.toLowerCase();
  return patterns.some((p) => {
    if (p.startsWith(".")) return fileName.endsWith(p);
    if (p.endsWith("/*")) return fileType.startsWith(p.slice(0, -1));
    return fileType === p;
  });
}
const fileUploadMeta = {
  component: "FileUpload",
  family: null,
  // non-family composite / overlay / layout
  variants: {},
  sizes: {},
  states: ["default", "hover", "active", "focus-visible", "disabled"],
  tokens: {
    bg: ["bg-surface", "bg-disabled"],
    fg: [],
    ring: ["ring-ring"]
  }
};
export {
  FileUpload,
  fileUploadMeta
};
//# sourceMappingURL=file-upload.js.map
