export { mmdPlaylistNodes, mmdPlaylistNodesRelations, mmdPlaylists, mmdPlaylistsRelations, mmdPresetItems, mmdResourceOptions } from '../../chunk-EV6BCVOQ.mjs';
import '../../chunk-BJTO5JO5.mjs';
import React2, { useState, useCallback, useEffect } from 'react';
import { clsx } from 'clsx';
import { Upload, CheckCircle2, X, Loader2, AlertTriangle, Search, Save, Settings, ChevronUp, ChevronDown, Plus, GripVertical, Trash2, List, Database, BarChart3, FileText, Music, Film } from 'lucide-react';
import JSZip from 'jszip';

// src/mmd/admin/types.ts
var MMD_RESOURCE_TYPE_CONFIGS = {
  model: {
    moduleId: "mmd-models",
    acceptedTypes: [".zip"],
    maxFileSize: 150,
    description: "MMD\u6A21\u578B\u538B\u7F29\u5305 (ZIP)",
    hint: "\u8BF7\u4E0A\u4F20\u5305\u542B PMX/\u8D34\u56FE\u7B49\u5B8C\u6574\u76EE\u5F55\u7684 ZIP \u538B\u7F29\u5305\uFF0C\u4FDD\u6301\u539F\u59CB\u6587\u4EF6\u7ED3\u6784"
  },
  motion: {
    moduleId: "mmd-motions",
    acceptedTypes: [".vmd"],
    maxFileSize: 20,
    description: "MMD\u52A8\u4F5C\u6587\u4EF6"
  },
  camera: {
    moduleId: "mmd-cameras",
    acceptedTypes: [".vmd"],
    maxFileSize: 10,
    description: "MMD\u76F8\u673A\u52A8\u753B\u6587\u4EF6"
  },
  audio: {
    moduleId: "mmd-audios",
    acceptedTypes: [".mp3", ".wav", ".ogg", ".m4a"],
    maxFileSize: 20,
    description: "\u97F3\u9891\u6587\u4EF6"
  },
  stage: {
    moduleId: "mmd-stages",
    acceptedTypes: [".zip"],
    maxFileSize: 200,
    description: "\u821E\u53F0/\u573A\u666F\u538B\u7F29\u5305 (ZIP)",
    hint: "\u9700\u5C06\u821E\u53F0\u6A21\u578B\u4E0E\u4F9D\u8D56\u8D34\u56FE\u4E00\u8D77\u6253\u5305 ZIP\uFF0C\u5E76\u4FDD\u6301\u76EE\u5F55\u7ED3\u6784"
  },
  thumbnail: {
    moduleId: "mmd-thumbnails",
    acceptedTypes: [".jpg", ".jpeg", ".png", ".webp"],
    maxFileSize: 5,
    description: "\u7F29\u7565\u56FE"
  }
};

// src/mmd/admin/components/MmdResourceSelector.tsx
var MmdResourceSelector = ({
  resourceType,
  fileService,
  userId,
  value,
  onChange,
  required = false
}) => {
  const [selectedFileId, setSelectedFileId] = useState(value);
  const [selectedFile, setSelectedFile] = useState(null);
  const [files, setFiles] = useState([]);
  const [loading, setLoading] = useState(false);
  const [uploading, setUploading] = useState(false);
  const [searchTerm, setSearchTerm] = useState("");
  const [showUploader, setShowUploader] = useState(false);
  const [zipValidationError, setZipValidationError] = useState(null);
  const config = MMD_RESOURCE_TYPE_CONFIGS[resourceType];
  if (!config) {
    return /* @__PURE__ */ React2.createElement("div", { className: "p-4 bg-red-50 text-red-600 rounded-lg" }, "\u672A\u627E\u5230\u8D44\u6E90\u7C7B\u578B\u914D\u7F6E\uFF1A", resourceType);
  }
  const getFileIcon = () => {
    switch (resourceType) {
      case "model":
      case "stage":
        return /* @__PURE__ */ React2.createElement(Film, { className: "w-5 h-5" });
      case "motion":
      case "camera":
        return /* @__PURE__ */ React2.createElement(FileText, { className: "w-5 h-5" });
      case "audio":
        return /* @__PURE__ */ React2.createElement(Music, { className: "w-5 h-5" });
      default:
        return /* @__PURE__ */ React2.createElement(FileText, { className: "w-5 h-5" });
    }
  };
  const loadFiles = useCallback(async () => {
    if (!fileService || !config) return;
    setLoading(true);
    try {
      const result = await fileService.queryFiles({
        moduleId: config.moduleId,
        pageSize: 50,
        page: 1,
        sortBy: "uploadTime",
        sortOrder: "desc"
      });
      setFiles(result.items || []);
    } catch (error) {
      console.error("\u52A0\u8F7D\u6587\u4EF6\u5217\u8868\u5931\u8D25:", error);
    } finally {
      setLoading(false);
    }
  }, [fileService, config]);
  useEffect(() => {
    loadFiles();
  }, [loadFiles]);
  useEffect(() => {
    if (selectedFileId && fileService) {
      fileService.getFileMetadata(selectedFileId).then((file) => setSelectedFile(file)).catch((error) => console.error("\u52A0\u8F7D\u6587\u4EF6\u4FE1\u606F\u5931\u8D25:", error));
    } else {
      setSelectedFile(null);
    }
  }, [selectedFileId, fileService]);
  const handleFileSelect = (file) => {
    setSelectedFileId(file.id);
    setSelectedFile(file);
    fileService.getFileUrl(file.id).then((url) => onChange(file.id, url)).catch((error) => console.error("\u83B7\u53D6\u6587\u4EF6URL\u5931\u8D25:", error));
  };
  const validateZipContents = async (buffer, type) => {
    const zip = await JSZip.loadAsync(buffer);
    const entries = Object.keys(zip.files);
    if (!entries.length) {
      throw new Error("\u538B\u7F29\u5305\u4E3A\u7A7A\uFF0C\u8BF7\u68C0\u67E5\u6587\u4EF6\u5185\u5BB9");
    }
    const hasModel = entries.some((name) => /\.[pP][mM][xX]$/.test(name)) || type === "stage" && entries.some((name) => /\.[pP][mM][dD]$/.test(name));
    const hasAssets = entries.some(
      (name) => /\.(png|jpg|jpeg|bmp|tga|dds|spa|sph)$/i.test(name)
    );
    if (!hasModel) {
      throw new Error(type === "stage" ? "\u538B\u7F29\u5305\u4E2D\u672A\u627E\u5230 PMX/PMD \u821E\u53F0\u6A21\u578B\u6587\u4EF6" : "\u538B\u7F29\u5305\u4E2D\u672A\u627E\u5230 PMX \u6A21\u578B\u6587\u4EF6");
    }
    if (!hasAssets) {
      throw new Error("\u538B\u7F29\u5305\u4E2D\u672A\u53D1\u73B0\u8D34\u56FE\u6587\u4EF6\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u5305\u542B texture \u76EE\u5F55");
    }
  };
  const handleFileUpload = async (file) => {
    if (!fileService) return;
    setUploading(true);
    setZipValidationError(null);
    try {
      if (resourceType === "model" || resourceType === "stage") {
        const arrayBuffer = await file.arrayBuffer();
        await validateZipContents(arrayBuffer, resourceType);
      }
      const fileMetadata = await fileService.uploadFile({
        file,
        moduleId: config.moduleId,
        businessId: userId,
        permission: "public"
      });
      await loadFiles();
      handleFileSelect(fileMetadata);
      setShowUploader(false);
    } catch (error) {
      console.error("\u6587\u4EF6\u4E0A\u4F20\u5931\u8D25:", error);
      const message = error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF";
      setZipValidationError(message);
      alert("\u4E0A\u4F20\u5931\u8D25: " + message);
    } finally {
      setUploading(false);
    }
  };
  const filteredFiles = files.filter(
    (file) => file.originalName.toLowerCase().includes(searchTerm.toLowerCase())
  );
  return /* @__PURE__ */ React2.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("label", { className: "flex items-center gap-2 text-sm font-medium text-gray-700 dark:text-gray-300" }, getFileIcon(), config.description, required && /* @__PURE__ */ React2.createElement("span", { className: "text-red-500" }, "*")), /* @__PURE__ */ React2.createElement(
    "button",
    {
      onClick: () => setShowUploader(!showUploader),
      className: "px-3 py-1 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
    },
    /* @__PURE__ */ React2.createElement(Upload, { className: "w-4 h-4" }),
    "\u4E0A\u4F20\u65B0\u6587\u4EF6"
  )), selectedFile && /* @__PURE__ */ React2.createElement("div", { className: "p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-green-800 rounded-lg flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-3" }, /* @__PURE__ */ React2.createElement(CheckCircle2, { className: "w-5 h-5 text-green-600 dark:text-green-400" }), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("div", { className: "text-sm font-medium text-gray-900 dark:text-white" }, selectedFile.originalName), /* @__PURE__ */ React2.createElement("div", { className: "text-xs text-gray-500 dark:text-gray-400" }, (selectedFile.size / 1024 / 1024).toFixed(2), " MB"))), /* @__PURE__ */ React2.createElement(
    "button",
    {
      onClick: () => {
        setSelectedFileId(void 0);
        setSelectedFile(null);
        onChange("", "");
      },
      className: "p-1 hover:bg-white/50 dark:hover:bg-black/20 rounded transition-colors"
    },
    /* @__PURE__ */ React2.createElement(X, { className: "w-4 h-4" })
  )), showUploader && /* @__PURE__ */ React2.createElement("div", { className: "p-4 bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg" }, /* @__PURE__ */ React2.createElement("div", { className: "text-sm text-gray-600 dark:text-gray-400 mb-3 space-y-1" }, /* @__PURE__ */ React2.createElement("p", null, "\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B: ", config.acceptedTypes.join(", ")), /* @__PURE__ */ React2.createElement("p", null, "\u6700\u5927\u6587\u4EF6\u5927\u5C0F: ", config.maxFileSize, "MB"), config.hint && /* @__PURE__ */ React2.createElement("p", { className: "text-xs text-amber-600 dark:text-amber-400" }, config.hint)), /* @__PURE__ */ React2.createElement(
    "input",
    {
      type: "file",
      accept: config.acceptedTypes.join(","),
      onChange: (e) => {
        const file = e.target.files?.[0];
        if (file) {
          if (file.size > config.maxFileSize * 1024 * 1024) {
            alert("\u6587\u4EF6\u5927\u5C0F\u8D85\u8FC7\u9650\u5236\uFF08\u6700\u5927 " + config.maxFileSize + "MB\uFF09");
            return;
          }
          handleFileUpload(file);
        }
      },
      disabled: uploading,
      className: "w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-lg file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100 disabled:opacity-50"
    }
  ), uploading && /* @__PURE__ */ React2.createElement("div", { className: "mt-3 flex items-center gap-2 text-sm text-blue-600" }, /* @__PURE__ */ React2.createElement(Loader2, { className: "w-4 h-4 animate-spin" }), "\u4E0A\u4F20\u4E2D..."), zipValidationError && /* @__PURE__ */ React2.createElement("div", { className: "mt-3 flex items-center gap-2 text-sm text-amber-600 dark:text-amber-400" }, /* @__PURE__ */ React2.createElement(AlertTriangle, { className: "w-4 h-4" }), zipValidationError)), /* @__PURE__ */ React2.createElement("div", { className: "relative" }, /* @__PURE__ */ React2.createElement(Search, { className: "absolute left-3 top-1/2 transform -translate-y-1/2 w-4 h-4 text-gray-400" }), /* @__PURE__ */ React2.createElement(
    "input",
    {
      type: "text",
      placeholder: "\u641C\u7D22\u6587\u4EF6...",
      value: searchTerm,
      onChange: (e) => setSearchTerm(e.target.value),
      className: "w-full pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
    }
  )), /* @__PURE__ */ React2.createElement("div", { className: "max-h-96 overflow-y-auto border border-gray-200 dark:border-gray-700 rounded-lg divide-y divide-gray-200 dark:divide-gray-700" }, loading ? /* @__PURE__ */ React2.createElement("div", { className: "p-8 text-center text-gray-500" }, /* @__PURE__ */ React2.createElement(Loader2, { className: "w-6 h-6 animate-spin mx-auto mb-2" }), "\u52A0\u8F7D\u4E2D...") : filteredFiles.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "p-8 text-center text-gray-500" }, searchTerm ? "\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u6587\u4EF6" : "\u6682\u65E0\u6587\u4EF6\uFF0C\u8BF7\u4E0A\u4F20") : filteredFiles.map((file) => /* @__PURE__ */ React2.createElement(
    "button",
    {
      key: file.id,
      onClick: () => handleFileSelect(file),
      className: clsx("w-full p-3 text-left hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors", selectedFileId === file.id ? "bg-blue-50 dark:bg-blue-900/20" : "")
    },
    /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React2.createElement("div", { className: "text-sm font-medium text-gray-900 dark:text-white truncate" }, file.originalName), /* @__PURE__ */ React2.createElement("div", { className: "text-xs text-gray-500 dark:text-gray-400 mt-0.5" }, (file.size / 1024 / 1024).toFixed(2), " MB \u2022", " ", new Date(file.uploadTime).toLocaleDateString())), selectedFileId === file.id && /* @__PURE__ */ React2.createElement(CheckCircle2, { className: "w-5 h-5 text-blue-600 dark:text-blue-400 flex-shrink-0 ml-2" }))
  ))), required && !selectedFileId && /* @__PURE__ */ React2.createElement("div", { className: "text-sm text-red-600 dark:text-red-400" }, "\u8BF7\u9009\u62E9\u4E00\u4E2A", config.description));
};

// src/mmd/admin/components/MmdPlaylistEditor.tsx
var MmdPlaylistEditor = ({
  playlistId,
  fileService,
  userId,
  onSave,
  onCancel
}) => {
  const [playlistName, setPlaylistName] = useState("");
  const [playlistDescription, setPlaylistDescription] = useState("");
  const [loop, setLoop] = useState(false);
  const [autoPlay, setAutoPlay] = useState(false);
  const [preloadStrategy, setPreloadStrategy] = useState("none");
  const [nodes, setNodes] = useState([]);
  const [expandedNodes, setExpandedNodes] = useState(/* @__PURE__ */ new Set([0]));
  const [saving, setSaving] = useState(false);
  const [showAdvanced, setShowAdvanced] = useState(false);
  const addNode = () => {
    const newNode = {
      name: "\u8282\u70B9 " + (nodes.length + 1),
      description: "",
      loop: false,
      sortOrder: nodes.length,
      modelFileId: ""
    };
    setNodes([...nodes, newNode]);
    setExpandedNodes(/* @__PURE__ */ new Set([...expandedNodes, nodes.length]));
  };
  const removeNode = (index) => {
    const newNodes = nodes.filter((_, i) => i !== index);
    newNodes.forEach((node, i) => {
      node.sortOrder = i;
    });
    setNodes(newNodes);
    const newExpanded = /* @__PURE__ */ new Set();
    expandedNodes.forEach((i) => {
      if (i < index) newExpanded.add(i);
      else if (i > index) newExpanded.add(i - 1);
    });
    setExpandedNodes(newExpanded);
  };
  const moveNode = (index, direction) => {
    const newNodes = [...nodes];
    const targetIndex = direction === "up" ? index - 1 : index + 1;
    if (targetIndex < 0 || targetIndex >= newNodes.length) return;
    if (!newNodes[index] || !newNodes[targetIndex]) return;
    [newNodes[index], newNodes[targetIndex]] = [newNodes[targetIndex], newNodes[index]];
    newNodes.forEach((node, i) => {
      node.sortOrder = i;
    });
    setNodes(newNodes);
    const newExpanded = /* @__PURE__ */ new Set();
    expandedNodes.forEach((i) => {
      if (i === index) newExpanded.add(targetIndex);
      else if (i === targetIndex) newExpanded.add(index);
      else newExpanded.add(i);
    });
    setExpandedNodes(newExpanded);
  };
  const toggleNode = (index) => {
    const newExpanded = new Set(expandedNodes);
    if (newExpanded.has(index)) {
      newExpanded.delete(index);
    } else {
      newExpanded.add(index);
    }
    setExpandedNodes(newExpanded);
  };
  const updateNode = (index, updates) => {
    const newNodes = [...nodes];
    if (!newNodes[index]) return;
    newNodes[index] = { ...newNodes[index], ...updates };
    setNodes(newNodes);
  };
  const validateForm = () => {
    if (!playlistName.trim()) {
      alert("\u8BF7\u8F93\u5165\u64AD\u653E\u5217\u8868\u540D\u79F0");
      return false;
    }
    if (nodes.length === 0) {
      alert("\u8BF7\u81F3\u5C11\u6DFB\u52A0\u4E00\u4E2A\u64AD\u653E\u8282\u70B9");
      return false;
    }
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      if (!node || !node.name?.trim()) {
        alert("\u8282\u70B9 " + (i + 1) + ": \u8BF7\u8F93\u5165\u8282\u70B9\u540D\u79F0");
        return false;
      }
      if (!node.modelFileId) {
        alert("\u8282\u70B9 " + (i + 1) + ": \u8BF7\u9009\u62E9\u6A21\u578B\u6587\u4EF6");
        return false;
      }
    }
    return true;
  };
  const handleSave = async () => {
    if (!validateForm()) return;
    setSaving(true);
    try {
      console.log("\u4FDD\u5B58\u64AD\u653E\u5217\u8868:", {
        name: playlistName,
        description: playlistDescription,
        loop,
        autoPlay,
        preloadStrategy,
        nodes
      });
      alert("\u4FDD\u5B58\u6210\u529F\uFF01");
      onSave?.(null);
    } catch (error) {
      console.error("\u4FDD\u5B58\u5931\u8D25:", error);
      alert("\u4FDD\u5B58\u5931\u8D25: " + (error instanceof Error ? error.message : "\u672A\u77E5\u9519\u8BEF"));
    } finally {
      setSaving(false);
    }
  };
  return /* @__PURE__ */ React2.createElement("div", { className: "max-w-6xl mx-auto p-6 bg-white dark:bg-gray-900 rounded-lg shadow-lg" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between mb-6" }, /* @__PURE__ */ React2.createElement("h2", { className: "text-2xl font-bold text-gray-900 dark:text-white" }, playlistId ? "\u7F16\u8F91\u64AD\u653E\u5217\u8868" : "\u521B\u5EFA\u64AD\u653E\u5217\u8868"), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement(
    "button",
    {
      onClick: handleSave,
      disabled: saving,
      className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 flex items-center gap-2"
    },
    /* @__PURE__ */ React2.createElement(Save, { className: "w-4 h-4" }),
    saving ? "\u4FDD\u5B58\u4E2D..." : "\u4FDD\u5B58"
  ), /* @__PURE__ */ React2.createElement(
    "button",
    {
      onClick: onCancel,
      className: "px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors flex items-center gap-2"
    },
    /* @__PURE__ */ React2.createElement(X, { className: "w-4 h-4" }),
    "\u53D6\u6D88"
  ))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-4 mb-8 p-6 bg-gray-50 dark:bg-gray-800 rounded-lg" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white mb-4" }, "\u57FA\u672C\u4FE1\u606F"), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u64AD\u653E\u5217\u8868\u540D\u79F0 ", /* @__PURE__ */ React2.createElement("span", { className: "text-red-500" }, "*")), /* @__PURE__ */ React2.createElement(
    "input",
    {
      type: "text",
      value: playlistName,
      onChange: (e) => setPlaylistName(e.target.value),
      placeholder: "\u8F93\u5165\u64AD\u653E\u5217\u8868\u540D\u79F0...",
      className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
    }
  )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
    "textarea",
    {
      value: playlistDescription,
      onChange: (e) => setPlaylistDescription(e.target.value),
      placeholder: "\u8F93\u5165\u63CF\u8FF0\u4FE1\u606F...",
      rows: 3,
      className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
    }
  )), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-6" }, /* @__PURE__ */ React2.createElement("label", { className: "flex items-center gap-2 cursor-pointer" }, /* @__PURE__ */ React2.createElement(
    "input",
    {
      type: "checkbox",
      checked: loop,
      onChange: (e) => setLoop(e.target.checked),
      className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
    }
  ), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-700 dark:text-gray-300" }, "\u5217\u8868\u5FAA\u73AF")), /* @__PURE__ */ React2.createElement("label", { className: "flex items-center gap-2 cursor-pointer" }, /* @__PURE__ */ React2.createElement(
    "input",
    {
      type: "checkbox",
      checked: autoPlay,
      onChange: (e) => setAutoPlay(e.target.checked),
      className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
    }
  ), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-700 dark:text-gray-300" }, "\u81EA\u52A8\u64AD\u653E"))), /* @__PURE__ */ React2.createElement(
    "button",
    {
      onClick: () => setShowAdvanced(!showAdvanced),
      className: "flex items-center gap-2 text-sm text-blue-600 dark:text-blue-400 hover:text-blue-700 dark:hover:text-blue-300"
    },
    /* @__PURE__ */ React2.createElement(Settings, { className: "w-4 h-4" }),
    "\u9AD8\u7EA7\u9009\u9879",
    showAdvanced ? /* @__PURE__ */ React2.createElement(ChevronUp, { className: "w-4 h-4" }) : /* @__PURE__ */ React2.createElement(ChevronDown, { className: "w-4 h-4" })
  ), showAdvanced && /* @__PURE__ */ React2.createElement("div", { className: "space-y-4 pt-4 border-t border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u9884\u52A0\u8F7D\u7B56\u7565"), /* @__PURE__ */ React2.createElement(
    "select",
    {
      value: preloadStrategy,
      onChange: (e) => setPreloadStrategy(e.target.value),
      className: "w-full px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-900 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
    },
    /* @__PURE__ */ React2.createElement("option", { value: "none" }, "\u4E0D\u9884\u52A0\u8F7D"),
    /* @__PURE__ */ React2.createElement("option", { value: "next" }, "\u9884\u52A0\u8F7D\u4E0B\u4E00\u4E2A"),
    /* @__PURE__ */ React2.createElement("option", { value: "all" }, "\u9884\u52A0\u8F7D\u5168\u90E8")
  )))), /* @__PURE__ */ React2.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React2.createElement("h3", { className: "text-lg font-semibold text-gray-900 dark:text-white" }, "\u64AD\u653E\u8282\u70B9 (", nodes.length, ")"), /* @__PURE__ */ React2.createElement(
    "button",
    {
      onClick: addNode,
      className: "px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors flex items-center gap-2"
    },
    /* @__PURE__ */ React2.createElement(Plus, { className: "w-4 h-4" }),
    "\u6DFB\u52A0\u8282\u70B9"
  )), nodes.length === 0 ? /* @__PURE__ */ React2.createElement("div", { className: "p-8 text-center text-gray-500 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-lg" }, '\u6682\u65E0\u8282\u70B9\uFF0C\u8BF7\u70B9\u51FB"\u6DFB\u52A0\u8282\u70B9"\u6309\u94AE\u5F00\u59CB') : nodes.map((node, index) => /* @__PURE__ */ React2.createElement(
    "div",
    {
      key: index,
      className: "border border-gray-200 dark:border-gray-700 rounded-lg overflow-hidden"
    },
    /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-3 p-4 bg-gray-50 dark:bg-gray-800" }, /* @__PURE__ */ React2.createElement(
      "button",
      {
        className: "cursor-move text-gray-400 hover:text-gray-600 dark:hover:text-gray-300",
        title: "\u62D6\u62FD\u6392\u5E8F"
      },
      /* @__PURE__ */ React2.createElement(GripVertical, { className: "w-5 h-5" })
    ), /* @__PURE__ */ React2.createElement(
      "button",
      {
        onClick: () => toggleNode(index),
        className: "flex-1 flex items-center justify-between text-left"
      },
      /* @__PURE__ */ React2.createElement("span", { className: "font-medium text-gray-900 dark:text-white" }, index + 1, ". ", node.name || "\u672A\u547D\u540D\u8282\u70B9"),
      expandedNodes.has(index) ? /* @__PURE__ */ React2.createElement(ChevronUp, { className: "w-5 h-5 text-gray-400" }) : /* @__PURE__ */ React2.createElement(ChevronDown, { className: "w-5 h-5 text-gray-400" })
    ), /* @__PURE__ */ React2.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React2.createElement(
      "button",
      {
        onClick: () => moveNode(index, "up"),
        disabled: index === 0,
        className: "p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 disabled:opacity-30",
        title: "\u4E0A\u79FB"
      },
      /* @__PURE__ */ React2.createElement(ChevronUp, { className: "w-5 h-5" })
    ), /* @__PURE__ */ React2.createElement(
      "button",
      {
        onClick: () => moveNode(index, "down"),
        disabled: index === nodes.length - 1,
        className: "p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 disabled:opacity-30",
        title: "\u4E0B\u79FB"
      },
      /* @__PURE__ */ React2.createElement(ChevronDown, { className: "w-5 h-5" })
    ), /* @__PURE__ */ React2.createElement(
      "button",
      {
        onClick: () => removeNode(index),
        className: "p-1 text-red-400 hover:text-red-600 dark:hover:text-red-300",
        title: "\u5220\u9664"
      },
      /* @__PURE__ */ React2.createElement(Trash2, { className: "w-5 h-5" })
    ))),
    expandedNodes.has(index) && /* @__PURE__ */ React2.createElement("div", { className: "p-6 space-y-6 bg-white dark:bg-gray-900" }, /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-2 gap-4" }, /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u8282\u70B9\u540D\u79F0 ", /* @__PURE__ */ React2.createElement("span", { className: "text-red-500" }, "*")), /* @__PURE__ */ React2.createElement(
      "input",
      {
        type: "text",
        value: node.name,
        onChange: (e) => updateNode(index, { name: e.target.value }),
        className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
      }
    )), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u65F6\u957F\uFF08\u79D2\uFF09"), /* @__PURE__ */ React2.createElement(
      "input",
      {
        type: "number",
        value: node.duration || "",
        onChange: (e) => updateNode(index, { duration: parseInt(e.target.value) || void 0 }),
        className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
      }
    ))), /* @__PURE__ */ React2.createElement("div", null, /* @__PURE__ */ React2.createElement("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2" }, "\u63CF\u8FF0"), /* @__PURE__ */ React2.createElement(
      "textarea",
      {
        value: node.description || "",
        onChange: (e) => updateNode(index, { description: e.target.value }),
        rows: 2,
        className: "w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-transparent"
      }
    )), /* @__PURE__ */ React2.createElement("label", { className: "flex items-center gap-2 cursor-pointer" }, /* @__PURE__ */ React2.createElement(
      "input",
      {
        type: "checkbox",
        checked: node.loop,
        onChange: (e) => updateNode(index, { loop: e.target.checked }),
        className: "w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
      }
    ), /* @__PURE__ */ React2.createElement("span", { className: "text-sm text-gray-700 dark:text-gray-300" }, "\u5355\u66F2\u5FAA\u73AF")), /* @__PURE__ */ React2.createElement("div", { className: "grid grid-cols-1 gap-6 pt-4 border-t border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement(
      MmdResourceSelector,
      {
        resourceType: "model",
        fileService,
        userId,
        value: node.modelFileId,
        onChange: (fileId) => updateNode(index, { modelFileId: fileId }),
        required: true
      }
    ), /* @__PURE__ */ React2.createElement(
      MmdResourceSelector,
      {
        resourceType: "motion",
        fileService,
        userId,
        value: node.motionFileId,
        onChange: (fileId) => updateNode(index, { motionFileId: fileId })
      }
    ), /* @__PURE__ */ React2.createElement(
      MmdResourceSelector,
      {
        resourceType: "audio",
        fileService,
        userId,
        value: node.audioFileId,
        onChange: (fileId) => updateNode(index, { audioFileId: fileId })
      }
    )))
  ))));
};

// src/mmd/admin/components/MmdAdminPanel.tsx
var MmdAdminPanel = ({
  fileService,
  userId,
  apiBaseUrl = "/api/mmd",
  showAdvancedOptions = true,
  className = ""
}) => {
  const [activeTab, setActiveTab] = useState("playlists");
  const [showEditor, setShowEditor] = useState(false);
  const [editingPlaylistId, setEditingPlaylistId] = useState();
  const tabs = [
    { id: "playlists", label: "\u64AD\u653E\u5217\u8868", icon: List },
    { id: "presets", label: "\u9884\u8BBE\u9879", icon: Database },
    { id: "resources", label: "\u8D44\u6E90\u7BA1\u7406", icon: Settings },
    ...showAdvancedOptions ? [{ id: "stats", label: "\u7EDF\u8BA1", icon: BarChart3 }] : []
  ];
  const handleCreatePlaylist = () => {
    setEditingPlaylistId(void 0);
    setShowEditor(true);
  };
  const handleCloseEditor = () => {
    setShowEditor(false);
    setEditingPlaylistId(void 0);
  };
  const handleSaveSuccess = (playlist) => {
    console.log("\u4FDD\u5B58\u6210\u529F:", playlist);
    handleCloseEditor();
  };
  if (showEditor) {
    return /* @__PURE__ */ React2.createElement(
      MmdPlaylistEditor,
      {
        playlistId: editingPlaylistId,
        fileService,
        userId,
        onSave: handleSaveSuccess,
        onCancel: handleCloseEditor
      }
    );
  }
  return /* @__PURE__ */ React2.createElement("div", { className: clsx("min-h-screen bg-gray-50 dark:bg-gray-900", className) }, /* @__PURE__ */ React2.createElement("div", { className: "bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700" }, /* @__PURE__ */ React2.createElement("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8" }, /* @__PURE__ */ React2.createElement("div", { className: "flex items-center justify-between h-16" }, /* @__PURE__ */ React2.createElement("h1", { className: "text-2xl font-bold text-gray-900 dark:text-white" }, "MMD \u540E\u53F0\u7BA1\u7406"), activeTab === "playlists" && /* @__PURE__ */ React2.createElement(
    "button",
    {
      onClick: handleCreatePlaylist,
      className: "px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center gap-2"
    },
    /* @__PURE__ */ React2.createElement(Plus, { className: "w-4 h-4" }),
    "\u521B\u5EFA\u64AD\u653E\u5217\u8868"
  )), /* @__PURE__ */ React2.createElement("div", { className: "flex space-x-8" }, tabs.map((tab) => {
    const Icon = tab.icon;
    return /* @__PURE__ */ React2.createElement(
      "button",
      {
        key: tab.id,
        onClick: () => setActiveTab(tab.id),
        className: clsx("flex items-center gap-2 px-1 py-4 border-b-2 font-medium text-sm transition-colors", activeTab === tab.id ? "border-blue-600 text-blue-600 dark:text-blue-400" : "border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300")
      },
      /* @__PURE__ */ React2.createElement(Icon, { className: "w-4 h-4" }),
      tab.label
    );
  })))), /* @__PURE__ */ React2.createElement("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8" }, activeTab === "playlists" && /* @__PURE__ */ React2.createElement("div", { className: "space-y-4" }, /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2.createElement(List, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2.createElement("p", null, "\u64AD\u653E\u5217\u8868\u7BA1\u7406\u529F\u80FD\u5F00\u53D1\u4E2D..."), /* @__PURE__ */ React2.createElement("p", { className: "text-sm mt-2" }, '\u70B9\u51FB\u53F3\u4E0A\u89D2"\u521B\u5EFA\u64AD\u653E\u5217\u8868"\u6309\u94AE\u5F00\u59CB'))), activeTab === "presets" && /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2.createElement(Database, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2.createElement("p", null, "\u9884\u8BBE\u9879\u7BA1\u7406\u529F\u80FD\u5F00\u53D1\u4E2D...")), activeTab === "resources" && /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2.createElement(Settings, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2.createElement("p", null, "\u8D44\u6E90\u7BA1\u7406\u529F\u80FD\u5F00\u53D1\u4E2D...")), activeTab === "stats" && /* @__PURE__ */ React2.createElement("div", { className: "text-center py-12 text-gray-500" }, /* @__PURE__ */ React2.createElement(BarChart3, { className: "w-12 h-12 mx-auto mb-4 opacity-50" }), /* @__PURE__ */ React2.createElement("p", null, "\u7EDF\u8BA1\u529F\u80FD\u5F00\u53D1\u4E2D..."))));
};

// src/mmd/admin/utils.ts
function extractFileIdsFromPlaylist(playlist, nodes) {
  const fileIds = /* @__PURE__ */ new Set();
  if (playlist.thumbnailFileId) {
    fileIds.add(playlist.thumbnailFileId);
  }
  for (const node of nodes) {
    if (node.thumbnailFileId) fileIds.add(node.thumbnailFileId);
    if (node.modelFileId) fileIds.add(node.modelFileId);
    if (node.motionFileId) fileIds.add(node.motionFileId);
    if (node.cameraFileId) fileIds.add(node.cameraFileId);
    if (node.audioFileId) fileIds.add(node.audioFileId);
    if (node.stageModelFileId) fileIds.add(node.stageModelFileId);
    if (node.additionalMotionFileIds) {
      node.additionalMotionFileIds.forEach((id) => fileIds.add(id));
    }
  }
  return Array.from(fileIds);
}
function extractFileIdsFromResourceOptions(options) {
  const fileIds = /* @__PURE__ */ new Set();
  for (const option of options) {
    fileIds.add(option.fileId);
    if (option.thumbnailFileId) {
      fileIds.add(option.thumbnailFileId);
    }
  }
  return Array.from(fileIds);
}
function extractFileIdsFromPresetItem(item) {
  const fileIds = /* @__PURE__ */ new Set();
  if (item.thumbnailFileId) fileIds.add(item.thumbnailFileId);
  if (item.modelFileId) fileIds.add(item.modelFileId);
  if (item.motionFileId) fileIds.add(item.motionFileId);
  if (item.cameraFileId) fileIds.add(item.cameraFileId);
  if (item.audioFileId) fileIds.add(item.audioFileId);
  if (item.stageModelFileId) fileIds.add(item.stageModelFileId);
  if (item.additionalMotionFileIds) {
    item.additionalMotionFileIds.forEach((id) => fileIds.add(id));
  }
  return Array.from(fileIds);
}
function convertPlaylistNodeToFrontend(node, fileUrls) {
  return {
    id: node.id,
    playlistId: node.playlistId,
    name: node.name,
    description: node.description,
    loop: node.loop,
    duration: node.duration,
    sortOrder: node.sortOrder,
    config: node.config,
    createdAt: node.createdAt,
    updatedAt: node.updatedAt,
    // URL 映射
    thumbnailUrl: node.thumbnailFileId ? fileUrls[node.thumbnailFileId] : void 0,
    modelUrl: fileUrls[node.modelFileId] || "",
    motionUrl: node.motionFileId ? fileUrls[node.motionFileId] : void 0,
    cameraUrl: node.cameraFileId ? fileUrls[node.cameraFileId] : void 0,
    audioUrl: node.audioFileId ? fileUrls[node.audioFileId] : void 0,
    stageModelUrl: node.stageModelFileId ? fileUrls[node.stageModelFileId] : void 0,
    additionalMotionUrls: node.additionalMotionFileIds?.map((id) => fileUrls[id]).filter((url) => Boolean(url))
  };
}
function convertPlaylistToFrontend(playlist, nodes, fileUrls) {
  return {
    id: playlist.id,
    name: playlist.name,
    description: playlist.description,
    loop: playlist.loop,
    preloadStrategy: playlist.preloadStrategy,
    autoPlay: playlist.autoPlay,
    status: playlist.status,
    sortOrder: playlist.sortOrder,
    config: playlist.config,
    createdBy: playlist.createdBy,
    createdAt: playlist.createdAt,
    updatedAt: playlist.updatedAt,
    deletedAt: playlist.deletedAt,
    // URL 映射
    thumbnailUrl: playlist.thumbnailFileId ? fileUrls[playlist.thumbnailFileId] : void 0,
    // 转换节点
    nodes: nodes.sort((a, b) => a.sortOrder - b.sortOrder).map((node) => convertPlaylistNodeToFrontend(node, fileUrls))
  };
}
function convertResourceOptionToFrontend(option, fileUrls) {
  return {
    id: option.id,
    name: option.name,
    description: option.description,
    resourceType: option.resourceType,
    tags: option.tags,
    sortOrder: option.sortOrder,
    isActive: option.isActive,
    createdBy: option.createdBy,
    createdAt: option.createdAt,
    updatedAt: option.updatedAt,
    // URL 映射
    fileUrl: fileUrls[option.fileId] || "",
    thumbnailUrl: option.thumbnailFileId ? fileUrls[option.thumbnailFileId] : void 0
  };
}
function convertPresetItemToFrontend(item, fileUrls) {
  return {
    id: item.id,
    name: item.name,
    description: item.description,
    sortOrder: item.sortOrder,
    isActive: item.isActive,
    tags: item.tags,
    createdBy: item.createdBy,
    createdAt: item.createdAt,
    updatedAt: item.updatedAt,
    // URL 映射
    thumbnailUrl: item.thumbnailFileId ? fileUrls[item.thumbnailFileId] : void 0,
    modelUrl: fileUrls[item.modelFileId] || "",
    motionUrl: item.motionFileId ? fileUrls[item.motionFileId] : void 0,
    cameraUrl: item.cameraFileId ? fileUrls[item.cameraFileId] : void 0,
    audioUrl: item.audioFileId ? fileUrls[item.audioFileId] : void 0,
    stageModelUrl: item.stageModelFileId ? fileUrls[item.stageModelFileId] : void 0,
    additionalMotionUrls: item.additionalMotionFileIds?.map((id) => fileUrls[id]).filter((url) => Boolean(url))
  };
}
function convertNodeToMmdFormat(node) {
  return {
    id: node.id,
    name: node.name,
    loop: node.loop,
    duration: node.duration,
    thumbnail: node.thumbnailUrl,
    resources: {
      modelPath: node.modelUrl,
      motionPath: node.motionUrl,
      cameraPath: node.cameraUrl,
      audioPath: node.audioUrl,
      stageModelPath: node.stageModelUrl,
      additionalMotions: node.additionalMotionUrls
    }
  };
}
function convertPlaylistToMmdConfig(playlist) {
  return {
    id: playlist.id,
    name: playlist.name,
    nodes: playlist.nodes.map(convertNodeToMmdFormat),
    loop: playlist.loop,
    preload: playlist.preloadStrategy,
    autoPlay: playlist.autoPlay
  };
}
function convertPresetItemToMmdResource(item) {
  return {
    id: item.id,
    name: item.name,
    thumbnail: item.thumbnailUrl,
    description: item.description,
    resources: {
      modelPath: item.modelUrl,
      motionPath: item.motionUrl,
      cameraPath: item.cameraUrl,
      audioPath: item.audioUrl,
      stageModelPath: item.stageModelUrl,
      additionalMotions: item.additionalMotionUrls
    }
  };
}
function convertResourceOptionsToMmdFormat(options) {
  const grouped = {
    models: [],
    motions: [],
    cameras: [],
    audios: [],
    stages: []
  };
  for (const option of options) {
    const resourceOption = {
      id: option.id,
      name: option.name,
      path: option.fileUrl,
      thumbnail: option.thumbnailUrl
    };
    switch (option.resourceType) {
      case "model":
        grouped.models.push(resourceOption);
        break;
      case "motion":
        grouped.motions.push(resourceOption);
        break;
      case "camera":
        grouped.cameras.push(resourceOption);
        break;
      case "audio":
        grouped.audios.push(resourceOption);
        break;
      case "stage":
        grouped.stages.push(resourceOption);
        break;
    }
  }
  return grouped;
}
function validateFileUrls(requiredFileIds, fileUrls) {
  const missingIds = requiredFileIds.filter((id) => !fileUrls[id]);
  return {
    valid: missingIds.length === 0,
    missingIds
  };
}
function generateMockFileUrls(fileIds) {
  const urls = {};
  for (const id of fileIds) {
    urls[id] = "/mock/files/" + id;
  }
  return urls;
}
function mergeFileUrlMaps(...maps) {
  return Object.assign({}, ...maps);
}
function extractPathsFromMmdResources(resources) {
  const paths = [resources.modelPath];
  if (resources.motionPath) paths.push(resources.motionPath);
  if (resources.cameraPath) paths.push(resources.cameraPath);
  if (resources.audioPath) paths.push(resources.audioPath);
  if (resources.stageModelPath) {
    if (Array.isArray(resources.stageModelPath)) {
      paths.push(...resources.stageModelPath);
    } else {
      paths.push(resources.stageModelPath);
    }
  }
  if (resources.additionalMotions) paths.push(...resources.additionalMotions);
  return paths;
}

export { MMD_RESOURCE_TYPE_CONFIGS, MmdAdminPanel, MmdPlaylistEditor, MmdResourceSelector, convertNodeToMmdFormat, convertPlaylistNodeToFrontend, convertPlaylistToFrontend, convertPlaylistToMmdConfig, convertPresetItemToFrontend, convertPresetItemToMmdResource, convertResourceOptionToFrontend, convertResourceOptionsToMmdFormat, extractFileIdsFromPlaylist, extractFileIdsFromPresetItem, extractFileIdsFromResourceOptions, extractPathsFromMmdResources, generateMockFileUrls, mergeFileUrlMaps, validateFileUrls };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map