import axios from "axios";
import { useCommonStore } from "@/stores/common";
import { useI18n, useModuleI18n } from "@/i18n/composables";
import { getPlatformDisplayName } from "@/utils/platformUtils";
import { resolveErrorMessage } from "@/utils/errorUtils";
import {
  buildSearchQuery,
  matchesPluginSearch,
  normalizeStr,
  toInitials,
  toPinyinText,
} from "@/utils/pluginSearch";
import {
  getValidHashTab,
  replaceTabRoute,
} from "@/utils/hashRouteTabs.mjs";
import { ref, computed, onMounted, onUnmounted, reactive, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useDisplay } from "vuetify";

const useRandomPluginsDisplay = ({ activeTab, marketSearch, currentPage }) => {
  const showRandomPlugins = ref(true);

  const toggleRandomPluginsVisibility = () => {
    showRandomPlugins.value = !showRandomPlugins.value;
  };

  const collapseRandomPlugins = () => {
    showRandomPlugins.value = false;
  };

  watch(marketSearch, () => {
    if (activeTab.value === "market") {
      collapseRandomPlugins();
    }
  });

  watch(currentPage, (newPage, oldPage) => {
    if (newPage === oldPage) return;
    if (activeTab.value !== "market") return;
    collapseRandomPlugins();
  });

  return {
    showRandomPlugins,
    toggleRandomPluginsVisibility,
    collapseRandomPlugins,
  };
};

const buildFailedPluginItems = (raw) => {
  return Object.entries(raw || {}).map(([dirName, info]) => {
    const detail = info && typeof info === "object" ? info : {};
    return {
      ...detail,
      dir_name: dirName,
      name: detail.name || dirName,
      display_name: detail.display_name || detail.name || dirName,
      error: detail.error || "",
      traceback: detail.traceback || "",
      reserved: !!detail.reserved,
    };
  });
};

export const useExtensionPage = () => {
  
  
  const commonStore = useCommonStore();
  const { t } = useI18n();
  const { tm } = useModuleI18n("features/extension");
  const router = useRouter();
  const route = useRoute();
  const { width } = useDisplay();
  
  const getSelectedGitHubProxy = () => {
    if (typeof window === "undefined" || !window.localStorage) return "";
    return localStorage.getItem("githubProxyRadioValue") === "1"
      ? localStorage.getItem("selectedGitHubProxy") || ""
      : "";
  };
  
  // 检查指令冲突并提示
  const conflictDialog = reactive({
    show: false,
    count: 0,
  });
  const checkAndPromptConflicts = async () => {
    try {
      const res = await axios.get("/api/commands");
      if (res.data.status === "ok") {
        const conflicts = res.data.data.summary?.conflicts || 0;
        if (conflicts > 0) {
          conflictDialog.count = conflicts;
          conflictDialog.show = true;
        }
      }
    } catch (err) {
      console.debug("Failed to check command conflicts:", err);
    }
  };
  const handleConflictConfirm = () => {
    activeTab.value = "commands";
  };
  
  const fileInput = ref(null);
  const activeTab = ref("installed");
  const validTabs = ["installed", "market", "mcp", "skills", "components"];
  const isValidTab = (tab) => validTabs.includes(tab);
  const getLocationHash = () => route.hash || "";
  const extractTabFromHash = (hash) => getValidHashTab(hash, validTabs);
  const syncTabFromHash = (hash) => {
    const tab = extractTabFromHash(hash);
    if (tab) {
      activeTab.value = tab;
      return true;
    }
    return false;
  };
  const extension_data = reactive({
    data: [],
    message: "",
  });
  
  // 从 localStorage 恢复显示系统插件的状态，默认为 false（隐藏）
  const getInitialShowReserved = () => {
    if (typeof window !== "undefined" && window.localStorage) {
      const saved = localStorage.getItem("showReservedPlugins");
      return saved === "true";
    }
    return false;
  };
  const showReserved = ref(getInitialShowReserved());
  const snack_message = ref("");
  const snack_show = ref(false);
  const snack_success = ref("success");
  const configDialog = ref(false);
  const extension_config = reactive({
    metadata: {},
    config: {},
  });
  const pluginMarketData = ref([]);
  const loadingDialog = reactive({
    show: false,
    title: "",
    statusCode: 0, // 0: loading, 1: success, 2: error,
    result: "",
  });
  const showPluginInfoDialog = ref(false);
  const selectedPlugin = ref({});
  const curr_namespace = ref("");
  const updatingAll = ref(false);
  
  const readmeDialog = reactive({
    show: false,
    pluginName: "",
    repoUrl: null,
  });
  
  // 强制更新确认对话框
  const forceUpdateDialog = reactive({
    show: false,
    extensionName: "",
  });
  
  // 更新全部插件确认对话框
  const updateAllConfirmDialog = reactive({
    show: false,
  });
  
  // 插件更新日志对话框（复用 ReadmeDialog）
  const changelogDialog = reactive({
    show: false,
    pluginName: "",
    repoUrl: null,
  });
  
  // 新增变量支持列表视图
  // 从 localStorage 恢复显示模式，默认为 false（卡片视图）
  const getInitialListViewMode = () => {
    if (typeof window !== "undefined" && window.localStorage) {
      return localStorage.getItem("pluginListViewMode") === "true";
    }
    return false;
  };
  const isListView = ref(getInitialListViewMode());
  const pluginSearch = ref("");
  const installedStatusFilter = ref("all");
  const installedSortBy = ref("default");
  const installedSortOrder = ref("desc");
  const loading_ = ref(false);
  
  // 分页相关
  const currentPage = ref(1);
  
  // 危险插件确认对话框
  const dangerConfirmDialog = ref(false);
  const selectedDangerPlugin = ref(null);
  const selectedMarketInstallPlugin = ref(null);
  const installCompat = reactive({
    checked: false,
    compatible: true,
    message: "",
  });
  
  // AstrBot 版本范围不兼容警告对话框
  const versionCompatibilityDialog = reactive({
    show: false,
    message: "",
  });
  
  // 卸载插件确认对话框（列表模式用）
  const showUninstallDialog = ref(false);
  const uninstallTarget = ref(null);
  
  // 自定义插件源相关
  const showSourceDialog = ref(false);
  const showSourceManagerDialog = ref(false);
  const sourceName = ref("");
  const sourceUrl = ref("");
  const customSources = ref([]);
  const selectedSource = ref(null);
  const showRemoveSourceDialog = ref(false);
  const sourceToRemove = ref(null);
  const editingSource = ref(false);
  const originalSourceUrl = ref("");
  
  // 插件市场相关
  const extension_url = ref("");
  const dialog = ref(false);
  const upload_file = ref(null);
  const uploadTab = ref("file");
  const showPluginFullName = ref(false);
  const marketSearch = ref("");
  const debouncedMarketSearch = ref("");
  const refreshingMarket = ref(false);
  const sortBy = ref("default"); // default, stars, author, updated
  const sortOrder = ref("desc"); // desc (降序) or asc (升序)
  const randomPluginNames = ref([]);
  const {
    showRandomPlugins,
    toggleRandomPluginsVisibility,
    collapseRandomPlugins,
  } = useRandomPluginsDisplay({
    activeTab,
    marketSearch,
    currentPage,
  });
  
  // 插件市场拼音搜索
  
  const plugin_handler_info_headers = computed(() => [
    { title: tm("table.headers.eventType"), key: "event_type_h" },
    { title: tm("table.headers.description"), key: "desc", maxWidth: "250px" },
    { title: tm("table.headers.specificType"), key: "type" },
    { title: tm("table.headers.trigger"), key: "cmd" },
  ]);

  const installedSortItems = computed(() => [
    { title: tm("sort.default"), value: "default" },
    { title: tm("sort.installTime"), value: "install_time" },
    { title: tm("sort.name"), value: "name" },
    { title: tm("sort.author"), value: "author" },
    { title: tm("sort.updateStatus"), value: "update_status" },
  ]);

  const installedSortUsesOrder = computed(
    () => installedSortBy.value !== "default",
  );
  
  // 插件表格的表头定义
  const showAuthorColumn = computed(() => width.value >= 1280);
  const pluginHeaders = computed(() => {
    const headers = [
      {
        title: tm("table.headers.name"),
        key: "name",
        sortable: false,
        width: showAuthorColumn.value ? "24%" : "26%",
      },
      {
        title: tm("table.headers.description"),
        key: "desc",
        sortable: false,
        width: showAuthorColumn.value ? "32%" : "36%",
      },
      {
        title: tm("table.headers.version"),
        key: "version",
        sortable: false,
        width: showAuthorColumn.value ? "12%" : "14%",
      },
    ];

    if (showAuthorColumn.value) {
      headers.push({
        title: tm("table.headers.author"),
        key: "author",
        sortable: false,
        width: "10%",
      });
    }

    headers.push({
      title: tm("table.headers.actions"),
      key: "actions",
      sortable: false,
      width: showAuthorColumn.value ? "22%" : "24%",
    });

    return headers;
  });
  
  // 过滤要显示的插件
  const filteredExtensions = computed(() => {
    const data = Array.isArray(extension_data?.data) ? extension_data.data : [];
    if (!showReserved.value) {
      return data.filter((ext) => !ext.reserved);
    }
    return data;
  });

  const compareInstalledPluginNames = (left, right) =>
    normalizeStr(left?.name ?? "").localeCompare(
      normalizeStr(right?.name ?? ""),
      undefined,
      {
        sensitivity: "base",
      },
    );

  const compareInstalledPluginAuthors = (left, right) =>
    normalizeStr(left?.author ?? "").localeCompare(
      normalizeStr(right?.author ?? ""),
      undefined,
      { sensitivity: "base" },
    );

  const getInstalledAtTimestamp = (plugin) => {
    const parsed = Date.parse(plugin?.installed_at ?? "");
    return Number.isFinite(parsed) ? parsed : null;
  };

  const sortInstalledPlugins = (plugins) => {
    return plugins
      .map((plugin, index) => ({
        plugin,
        index,
        installedAtTimestamp: getInstalledAtTimestamp(plugin),
      }))
      .sort((left, right) => {
        const fallbackNameCompare = compareInstalledPluginNames(
          left.plugin,
          right.plugin,
        );
        const fallbackResult =
          fallbackNameCompare !== 0 ? fallbackNameCompare : left.index - right.index;

        if (installedSortBy.value === "install_time") {
          const leftTimestamp = left.installedAtTimestamp;
          const rightTimestamp = right.installedAtTimestamp;

          if (leftTimestamp == null && rightTimestamp == null) {
            return fallbackResult;
          }
          if (leftTimestamp == null) {
            return 1;
          }
          if (rightTimestamp == null) {
            return -1;
          }

          const timeDiff =
            installedSortOrder.value === "desc"
              ? rightTimestamp - leftTimestamp
              : leftTimestamp - rightTimestamp;
          return timeDiff !== 0 ? timeDiff : fallbackResult;
        }

        if (installedSortBy.value === "name") {
          const nameCompare = compareInstalledPluginNames(left.plugin, right.plugin);
          if (nameCompare !== 0) {
            return installedSortOrder.value === "desc"
              ? -nameCompare
              : nameCompare;
          }
          return left.index - right.index;
        }

        if (installedSortBy.value === "author") {
          const authorCompare = compareInstalledPluginAuthors(
            left.plugin,
            right.plugin,
          );
          if (authorCompare !== 0) {
            return installedSortOrder.value === "desc"
              ? -authorCompare
              : authorCompare;
          }
          return fallbackResult;
        }

        if (installedSortBy.value === "update_status") {
          const leftHasUpdate = left.plugin?.has_update ? 1 : 0;
          const rightHasUpdate = right.plugin?.has_update ? 1 : 0;
          const updateDiff =
            installedSortOrder.value === "desc"
              ? rightHasUpdate - leftHasUpdate
              : leftHasUpdate - rightHasUpdate;
          return updateDiff !== 0 ? updateDiff : fallbackResult;
        }

        return fallbackResult;
      })
      .map((item) => item.plugin);
  };

  // 通过搜索过滤插件
  const filteredPlugins = computed(() => {
    const plugins = filteredExtensions.value.filter((plugin) => {
      if (installedStatusFilter.value === "enabled") {
        return !!plugin.activated;
      }
      if (installedStatusFilter.value === "disabled") {
        return !plugin.activated;
      }
      return true;
    });

    const query = buildSearchQuery(pluginSearch.value);
    const filtered = query
      ? plugins.filter((plugin) => matchesPluginSearch(plugin, query))
      : plugins;

    return sortInstalledPlugins(filtered);
  });
  
  // 过滤后的插件市场数据（带搜索）
  const filteredMarketPlugins = computed(() => {
    const query = buildSearchQuery(debouncedMarketSearch.value);
    if (!query) {
      return pluginMarketData.value;
    }

    return pluginMarketData.value.filter((plugin) =>
      matchesPluginSearch(plugin, query),
    );
  });
  
  // 所有插件列表，推荐插件排在前面
  const sortedPlugins = computed(() => {
    let plugins = [...filteredMarketPlugins.value];
  
    // 根据排序选项排序
    if (sortBy.value === "stars") {
      // 按 star 数排序
      plugins.sort((a, b) => {
        const starsA = a.stars ?? 0;
        const starsB = b.stars ?? 0;
        return sortOrder.value === "desc" ? starsB - starsA : starsA - starsB;
      });
    } else if (sortBy.value === "author") {
      // 按作者名字典序排序
      plugins.sort((a, b) => {
        const authorA = (a.author ?? "").toLowerCase();
        const authorB = (b.author ?? "").toLowerCase();
        const result = authorA.localeCompare(authorB);
        return sortOrder.value === "desc" ? -result : result;
      });
    } else if (sortBy.value === "updated") {
      // 按更新时间排序
      plugins.sort((a, b) => {
        const dateA = a.updated_at ? new Date(a.updated_at).getTime() : 0;
        const dateB = b.updated_at ? new Date(b.updated_at).getTime() : 0;
        return sortOrder.value === "desc" ? dateB - dateA : dateA - dateB;
      });
    } else {
      // default: 推荐插件排在前面
      const pinned = plugins.filter((plugin) => plugin?.pinned);
      const notPinned = plugins.filter((plugin) => !plugin?.pinned);
      return [...pinned, ...notPinned];
    }
  
    return plugins;
  });
  
  const RANDOM_PLUGINS_COUNT = 3;
  
  const randomPlugins = computed(() => {
    const allPlugins = pluginMarketData.value;
    if (allPlugins.length === 0) return [];
  
    const pluginsByName = new Map(allPlugins.map((plugin) => [plugin.name, plugin]));
    const selected = randomPluginNames.value
      .map((name) => pluginsByName.get(name))
      .filter(Boolean);
  
    if (selected.length > 0) {
      return selected;
    }
  
    return allPlugins.slice(0, Math.min(RANDOM_PLUGINS_COUNT, allPlugins.length));
  });
  
  const shufflePlugins = (plugins) => {
    const shuffled = [...plugins];
    for (let i = shuffled.length - 1; i > 0; i -= 1) {
      const j = Math.floor(Math.random() * (i + 1));
      [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
    }
    return shuffled;
  };
  
  const refreshRandomPlugins = () => {
    const shuffled = shufflePlugins(pluginMarketData.value);
    randomPluginNames.value = shuffled
      .slice(0, Math.min(RANDOM_PLUGINS_COUNT, shuffled.length))
      .map((plugin) => plugin.name);
  };
  
  // 分页计算属性
  const displayItemsPerPage = 9; // 固定每页显示9个卡片（3行）
  
  const totalPages = computed(() => {
    return Math.ceil(sortedPlugins.value.length / displayItemsPerPage);
  });
  
  const paginatedPlugins = computed(() => {
    const start = (currentPage.value - 1) * displayItemsPerPage;
    const end = start + displayItemsPerPage;
    return sortedPlugins.value.slice(start, end);
  });
  
  const updatableExtensions = computed(() => {
    const data = Array.isArray(extension_data?.data) ? extension_data.data : [];
    return data.filter((ext) => ext.has_update);
  });
  
  // 方法
  const toggleShowReserved = () => {
    showReserved.value = !showReserved.value;
    // 保存到 localStorage
    if (typeof window !== "undefined" && window.localStorage) {
      localStorage.setItem("showReservedPlugins", showReserved.value.toString());
    }
  };
  
  const toast = (message, success) => {
    snack_message.value = message;
    snack_show.value = true;
    snack_success.value = success;
  };
  
  const resetLoadingDialog = () => {
    loadingDialog.show = false;
    loadingDialog.title = tm("dialogs.loading.title");
    loadingDialog.statusCode = 0;
    loadingDialog.result = "";
  };
  
  const onLoadingDialogResult = (statusCode, result, timeToClose = 2000) => {
    loadingDialog.statusCode = statusCode;
    loadingDialog.result = result;
    if (timeToClose === -1) return;
    setTimeout(resetLoadingDialog, timeToClose);
  };
  
  const failedPluginsDict = ref({});
  const failedPluginItems = computed(() =>
    buildFailedPluginItems(failedPluginsDict.value),
  );
  
  const getExtensions = async () => {
    loading_.value = true;
    try {
      const res = await axios.get("/api/plugin/get");   
      Object.assign(extension_data, res.data);
      
      const failRes = await axios.get("/api/plugin/source/get-failed-plugins");    
      failedPluginsDict.value = failRes.data.data || {};
      
      checkUpdate();
    } catch (err) {
      toast(err, "error");
    } finally {
      loading_.value = false;
    }
  };
  
  const handleReloadAllFailed = async () => {
      const dirNames = Object.keys(failedPluginsDict.value);
      if (dirNames.length === 0) {
          toast("没有需要重载的失败插件", "info");
          return;
      }
  
      loading_.value = true;
      try {
          const promises = dirNames.map(dir => 
              axios.post("/api/plugin/reload-failed", { dir_name: dir })
          );
          await Promise.all(promises);
          
          toast("已尝试重载所有失败插件", "success");
          
          // 清空 message 关闭对话框
          extension_data.message = "";
          
          // 刷新列表
          await getExtensions();
          
      } catch (e) {
          console.error("重载失败:", e);
          toast("批量重载过程中出现错误", "error");
      } finally {
          loading_.value = false;
      }
  };

  const reloadFailedPlugin = async (dirName) => {
    if (!dirName) return;

    try {
      const res = await axios.post("/api/plugin/reload-failed", { dir_name: dirName });
      if (res.data.status === "error") {
        toast(res.data.message || tm("messages.reloadFailed"), "error");
        return;
      }
      toast(res.data.message || tm("messages.reloadSuccess"), "success");
      await getExtensions();
    } catch (err) {
      toast(resolveErrorMessage(err, tm("messages.reloadFailed")), "error");
    }
  };

  const requestUninstall = (target) => {
    if (!target?.id || !target?.kind) return;
    uninstallTarget.value = target;
    showUninstallDialog.value = true;
  };

  const uninstall = async (
    target,
    { deleteConfig = false, deleteData = false, skipConfirm = false } = {},
  ) => {
    if (!target?.id || !target?.kind) return;

    if (!skipConfirm) {
      requestUninstall(target);
      return;
    }

    const isFailed = target.kind === "failed";
    const endpoint = isFailed
      ? "/api/plugin/uninstall-failed"
      : "/api/plugin/uninstall";
    const payload = isFailed
      ? { dir_name: target.id, delete_config: deleteConfig, delete_data: deleteData }
      : { name: target.id, delete_config: deleteConfig, delete_data: deleteData };

    toast(`${tm("messages.uninstalling")} ${target.id}`, "primary");

    try {
      const res = await axios.post(endpoint, payload);
      if (res.data.status === "error") {
        toast(res.data.message, "error");
        return;
      }
      if (!isFailed) {
        Object.assign(extension_data, res.data);
      }
      toast(res.data.message, "success");
      await getExtensions();
    } catch (err) {
      toast(resolveErrorMessage(err, tm("messages.operationFailed")), "error");
    }
  };

  const requestUninstallPlugin = (name) => {
    if (!name) return;
    uninstall({ kind: "normal", id: name }, { skipConfirm: false });
  };

  const requestUninstallFailedPlugin = (dirName) => {
    if (!dirName) return;
    uninstall({ kind: "failed", id: dirName }, { skipConfirm: false });
  };
  
  const checkUpdate = () => {
    const onlinePluginsMap = new Map();
    const onlinePluginsNameMap = new Map();
  
    pluginMarketData.value.forEach((plugin) => {
      if (plugin.repo) {
        onlinePluginsMap.set(plugin.repo.toLowerCase(), plugin);
      }
      onlinePluginsNameMap.set(plugin.name, plugin);
    });
  
    const data = Array.isArray(extension_data?.data) ? extension_data.data : [];
    data.forEach((extension) => {
      const repoKey = extension.repo?.toLowerCase();
      const onlinePlugin = repoKey ? onlinePluginsMap.get(repoKey) : null;
      const onlinePluginByName = onlinePluginsNameMap.get(extension.name);
      const matchedPlugin = onlinePlugin || onlinePluginByName;
  
      if (matchedPlugin) {
        extension.online_version = matchedPlugin.version;
        extension.has_update =
          extension.version !== matchedPlugin.version &&
          matchedPlugin.version !== tm("status.unknown");
      } else {
        extension.has_update = false;
      }
    });
  };
  
  const uninstallExtension = async (
    extensionName,
    optionsOrSkipConfirm = false,
  ) => {
    if (!extensionName) return;

    if (typeof optionsOrSkipConfirm === "boolean") {
      return uninstall(
        { kind: "normal", id: extensionName },
        { skipConfirm: optionsOrSkipConfirm },
      );
    }

    return uninstall(
      { kind: "normal", id: extensionName },
      { ...(optionsOrSkipConfirm || {}), skipConfirm: true },
    );
  };
  
  // 处理卸载确认对话框的确认事件
  const handleUninstallConfirm = async (options) => {
    const target = uninstallTarget.value;
    if (!target) return;

    try {
      await uninstall(target, { ...(options || {}), skipConfirm: true });
    } finally {
      uninstallTarget.value = null;
      showUninstallDialog.value = false;
    }
  };
  
  const updateExtension = async (extension_name, forceUpdate = false) => {
    // 查找插件信息
    const data = Array.isArray(extension_data?.data) ? extension_data.data : [];
    const ext = data.find((e) => e.name === extension_name);
  
    // 如果没有检测到更新且不是强制更新，则弹窗确认
    if (!ext?.has_update && !forceUpdate) {
      forceUpdateDialog.extensionName = extension_name;
      forceUpdateDialog.show = true;
      return;
    }
  
    loadingDialog.title = tm("status.loading");
    loadingDialog.show = true;
    try {
      const res = await axios.post("/api/plugin/update", {
        name: extension_name,
        proxy: getSelectedGitHubProxy(),
      });
  
      if (res.data.status === "error") {
        onLoadingDialogResult(2, res.data.message, -1);
        return;
      }
  
      Object.assign(extension_data, res.data);
      onLoadingDialogResult(1, res.data.message);
      setTimeout(async () => {
        toast(tm("messages.refreshing"), "info", 2000);
        try {
          await getExtensions();
          toast(tm("messages.refreshSuccess"), "success");
  
          // 更新完成后弹出更新日志
          viewChangelog({
            name: extension_name,
            repo: ext?.repo || null,
          });
        } catch (error) {
          const errorMsg =
            error.response?.data?.message || error.message || String(error);
          toast(`${tm("messages.refreshFailed")}: ${errorMsg}`, "error");
        }
      }, 1000);
    } catch (err) {
      toast(err, "error");
    }
  };
  
  // 确认强制更新
  // 显示更新全部插件确认对话框
  const showUpdateAllConfirm = () => {
    if (updatableExtensions.value.length === 0) return;
    updateAllConfirmDialog.show = true;
  };
  
  // 确认更新全部插件
  const confirmUpdateAll = () => {
    updateAllConfirmDialog.show = false;
    updateAllExtensions();
  };
  
  // 取消更新全部插件
  const cancelUpdateAll = () => {
    updateAllConfirmDialog.show = false;
  };
  
  const confirmForceUpdate = () => {
    const name = forceUpdateDialog.extensionName;
    forceUpdateDialog.show = false;
    forceUpdateDialog.extensionName = "";
    updateExtension(name, true);
  };
  
  const updateAllExtensions = async () => {
    if (updatingAll.value || updatableExtensions.value.length === 0) return;
    updatingAll.value = true;
    loadingDialog.title = tm("status.loading");
    loadingDialog.statusCode = 0;
    loadingDialog.result = "";
    loadingDialog.show = true;
  
    const targets = updatableExtensions.value.map((ext) => ext.name);
    try {
      const res = await axios.post("/api/plugin/update-all", {
        names: targets,
        proxy: getSelectedGitHubProxy(),
      });
  
      if (res.data.status === "error") {
        onLoadingDialogResult(
          2,
          res.data.message ||
            tm("messages.updateAllFailed", {
              failed: targets.length,
              total: targets.length,
            }),
          -1,
        );
        return;
      }
  
      const results = res.data.data?.results || [];
      const failures = results.filter((r) => r.status !== "ok");
      try {
        await getExtensions();
      } catch (err) {
        const errorMsg =
          err.response?.data?.message || err.message || String(err);
        failures.push({ name: "refresh", status: "error", message: errorMsg });
      }
  
      if (failures.length === 0) {
        onLoadingDialogResult(1, tm("messages.updateAllSuccess"));
      } else {
        const failureText = tm("messages.updateAllFailed", {
          failed: failures.length,
          total: targets.length,
        });
        const detail = failures.map((f) => `${f.name}: ${f.message}`).join("\n");
        onLoadingDialogResult(2, `${failureText}\n${detail}`, -1);
      }
    } catch (err) {
      const errorMsg = err.response?.data?.message || err.message || String(err);
      onLoadingDialogResult(2, errorMsg, -1);
    } finally {
      updatingAll.value = false;
    }
  };
  
  const pluginOn = async (extension) => {
    try {
      const res = await axios.post("/api/plugin/on", { name: extension.name });
      if (res.data.status === "error") {
        toast(res.data.message, "error");
        return;
      }
      toast(res.data.message, "success");
      await getExtensions();
  
      await checkAndPromptConflicts();
    } catch (err) {
      toast(err, "error");
    }
  };
  
  const pluginOff = async (extension) => {
    try {
      const res = await axios.post("/api/plugin/off", { name: extension.name });
      if (res.data.status === "error") {
        toast(res.data.message, "error");
        return;
      }
      toast(res.data.message, "success");
      getExtensions();
    } catch (err) {
      toast(err, "error");
    }
  };
  
  const openExtensionConfig = async (extension_name) => {
    curr_namespace.value = extension_name;
    configDialog.value = true;
    try {
      const res = await axios.get(
        "/api/config/get?plugin_name=" + extension_name,
      );
      extension_config.metadata = res.data.data.metadata;
      extension_config.config = res.data.data.config;
    } catch (err) {
      toast(err, "error");
    }
  };
  
  const updateConfig = async () => {
    try {
      const res = await axios.post(
        "/api/config/plugin/update?plugin_name=" + curr_namespace.value,
        extension_config.config,
      );
      if (res.data.status === "ok") {
        toast(res.data.message, "success");
      } else {
        toast(res.data.message, "error");
      }
      configDialog.value = false;
      extension_config.metadata = {};
      extension_config.config = {};
      getExtensions();
    } catch (err) {
      toast(err, "error");
    }
  };
  
  const showPluginInfo = (plugin) => {
    selectedPlugin.value = plugin;
    showPluginInfoDialog.value = true;
  };
  
  const reloadPlugin = async (plugin_name) => {
    try {
      const res = await axios.post("/api/plugin/reload", { name: plugin_name });
      if (res.data.status === "error") {
        toast(res.data.message || tm("messages.reloadFailed"), "error");
        return;
      }
      toast(tm("messages.reloadSuccess"), "success");
      await getExtensions();
    } catch (err) {
      toast(resolveErrorMessage(err, tm("messages.reloadFailed")), "error");
    }
  };
  
  const viewReadme = (plugin) => {
    readmeDialog.pluginName = plugin.name;
    readmeDialog.repoUrl = plugin.repo;
    readmeDialog.show = true;
  };
  
  // 查看更新日志
  const viewChangelog = (plugin) => {
    changelogDialog.pluginName = plugin.name;
    changelogDialog.repoUrl = plugin.repo;
    changelogDialog.show = true;
  };
  
  // 为表格视图创建一个处理安装插件的函数
  const handleInstallPlugin = async (plugin) => {
    if (plugin.tags && plugin.tags.includes("danger")) {
      selectedDangerPlugin.value = plugin;
      dangerConfirmDialog.value = true;
    } else {
      selectedMarketInstallPlugin.value = plugin;
      extension_url.value = plugin.repo;
      dialog.value = true;
      uploadTab.value = "url";
    }
  };
  
  // 确认安装危险插件
  const confirmDangerInstall = () => {
    if (selectedDangerPlugin.value) {
      selectedMarketInstallPlugin.value = selectedDangerPlugin.value;
      extension_url.value = selectedDangerPlugin.value.repo;
      dialog.value = true;
      uploadTab.value = "url";
    }
    dangerConfirmDialog.value = false;
    selectedDangerPlugin.value = null;
  };
  
  // 取消安装危险插件
  const cancelDangerInstall = () => {
    dangerConfirmDialog.value = false;
    selectedDangerPlugin.value = null;
  };
  
  // 自定义插件源管理方法
  const loadCustomSources = async () => {
    try {
      const res = await axios.get("/api/plugin/source/get");
      if (res.data.status === "ok") {
        customSources.value = res.data.data;
      } else {
        toast(res.data.message, "error");
      }
    } catch (e) {
      console.warn("Failed to load custom sources:", e);
      customSources.value = [];
    }
  
    // 加载当前选中的插件源
    const currentSource = localStorage.getItem("selectedPluginSource");
    if (currentSource) {
      selectedSource.value = currentSource;
    }
  };
  
  const saveCustomSources = async () => {
    try {
      const res = await axios.post("/api/plugin/source/save", {
        sources: customSources.value,
      });
      if (res.data.status !== "ok") {
        toast(res.data.message, "error");
      }
    } catch (e) {
      toast(e, "error");
    }
  };
  
  const addCustomSource = () => {
    showSourceManagerDialog.value = false;
    editingSource.value = false;
    originalSourceUrl.value = "";
    sourceName.value = "";
    sourceUrl.value = "";
    showSourceDialog.value = true;
  };
  
  const openSourceManagerDialog = async () => {
    await loadCustomSources();
    showSourceManagerDialog.value = true;
  };
  
  const selectPluginSource = (sourceUrl) => {
    selectedSource.value = sourceUrl;
    if (sourceUrl) {
      localStorage.setItem("selectedPluginSource", sourceUrl);
    } else {
      localStorage.removeItem("selectedPluginSource");
    }
    // 重新加载插件市场数据
    refreshPluginMarket();
  };
  
  const sourceSelectItems = computed(() => [
    { title: tm("market.defaultSource"), value: "__default__" },
    ...customSources.value.map((source) => ({
      title: source.name,
      value: source.url,
    })),
  ]);
  
  const editCustomSource = (source) => {
    if (!source) return;
    showSourceManagerDialog.value = false;
    editingSource.value = true;
    originalSourceUrl.value = source.url;
    sourceName.value = source.name;
    sourceUrl.value = source.url;
    showSourceDialog.value = true;
  };
  
  const removeCustomSource = (source) => {
    if (!source) return;
    showSourceManagerDialog.value = false;
    sourceToRemove.value = source;
    showRemoveSourceDialog.value = true;
  };
  
  const confirmRemoveSource = () => {
    if (sourceToRemove.value) {
      customSources.value = customSources.value.filter(
        (s) => s.url !== sourceToRemove.value.url,
      );
      saveCustomSources();
  
      // 如果删除的是当前选中的源，切换到默认源
      if (selectedSource.value === sourceToRemove.value.url) {
        selectedSource.value = null;
        localStorage.removeItem("selectedPluginSource");
        // 重新加载插件市场数据
        refreshPluginMarket();
      }
  
      toast(tm("market.sourceRemoved"), "success");
      showRemoveSourceDialog.value = false;
      sourceToRemove.value = null;
    }
  };
  
  const saveCustomSource = () => {
    const normalizedUrl = sourceUrl.value.trim();
  
    if (!sourceName.value.trim() || !normalizedUrl) {
      toast(tm("messages.fillSourceNameAndUrl"), "error");
      return;
    }
  
    // 检查URL格式
    try {
      new URL(normalizedUrl);
    } catch (e) {
      toast(tm("messages.invalidUrl"), "error");
      return;
    }
  
    if (editingSource.value) {
      // 编辑模式：更新现有源
      const index = customSources.value.findIndex(
        (s) => s.url === originalSourceUrl.value,
      );
      if (index !== -1) {
        customSources.value[index] = {
          name: sourceName.value.trim(),
          url: normalizedUrl,
        };
  
        // 如果编辑的是当前选中的源，更新选中源
        if (selectedSource.value === originalSourceUrl.value) {
          selectedSource.value = normalizedUrl;
          localStorage.setItem("selectedPluginSource", selectedSource.value);
          // 重新加载插件市场数据
          refreshPluginMarket();
        }
      }
    } else {
      // 添加模式：检查是否已存在
      if (customSources.value.some((source) => source.url === normalizedUrl)) {
        toast(tm("market.sourceExists"), "error");
        return;
      }
  
      customSources.value.push({
        name: sourceName.value.trim(),
        url: normalizedUrl,
      });
    }
  
    saveCustomSources();
    toast(
      editingSource.value ? tm("market.sourceUpdated") : tm("market.sourceAdded"),
      "success",
    );
  
    // 重置表单
    sourceName.value = "";
    sourceUrl.value = "";
    editingSource.value = false;
    originalSourceUrl.value = "";
    showSourceDialog.value = false;
  };
  
  // 插件市场显示完整插件名称
  const trimExtensionName = () => {
    pluginMarketData.value.forEach((plugin) => {
      if (plugin.name) {
        let name = plugin.name.trim().toLowerCase();
        if (name.startsWith("astrbot_plugin_")) {
          plugin.trimmedName = name.substring(15);
        } else if (name.startsWith("astrbot_") || name.startsWith("astrbot-")) {
          plugin.trimmedName = name.substring(8);
        } else plugin.trimmedName = plugin.name;
      }
    });
  };
  
  const checkAlreadyInstalled = () => {
    const data = Array.isArray(extension_data?.data) ? extension_data.data : [];
    const installedRepos = new Set(data.map((ext) => ext.repo?.toLowerCase()));
    const installedNames = new Set(data.map((ext) => ext.name));
    const installedByRepo = new Map(
      data
        .filter((ext) => ext.repo)
        .map((ext) => [ext.repo.toLowerCase(), ext]),
    );
    const installedByName = new Map(data.map((ext) => [ext.name, ext]));
  
    for (let i = 0; i < pluginMarketData.value.length; i++) {
      const plugin = pluginMarketData.value[i];
      const matchedInstalled =
        (plugin.repo && installedByRepo.get(plugin.repo.toLowerCase())) ||
        installedByName.get(plugin.name);
  
      // 兜底：市场源未提供字段时，回填本地已安装插件中的元数据，便于在市场页直接展示
      if (matchedInstalled) {
        if (
          (!Array.isArray(plugin.support_platforms) ||
            plugin.support_platforms.length === 0) &&
          Array.isArray(matchedInstalled.support_platforms)
        ) {
          plugin.support_platforms = matchedInstalled.support_platforms;
        }
        if (!plugin.astrbot_version && matchedInstalled.astrbot_version) {
          plugin.astrbot_version = matchedInstalled.astrbot_version;
        }
      }
  
      plugin.installed =
        installedRepos.has(plugin.repo?.toLowerCase()) ||
        installedNames.has(plugin.name);
    }
  
    let installed = [];
    let notInstalled = [];
    for (let i = 0; i < pluginMarketData.value.length; i++) {
      if (pluginMarketData.value[i].installed) {
        installed.push(pluginMarketData.value[i]);
      } else {
        notInstalled.push(pluginMarketData.value[i]);
      }
    }
    pluginMarketData.value = notInstalled.concat(installed);
  };
  
  const showVersionCompatibilityWarning = (message) => {
    versionCompatibilityDialog.message = message;
    versionCompatibilityDialog.show = true;
  };

  const refreshExtensionsAfterInstallFailure = async () => {
    try {
      await getExtensions();
    } catch (error) {
      console.debug("Failed to refresh extensions after install failure:", error);
    }
  };
  
  const continueInstallIgnoringVersionWarning = async () => {
    versionCompatibilityDialog.show = false;
    await newExtension(true);
  };
  
  const cancelInstallOnVersionWarning = () => {
    versionCompatibilityDialog.show = false;
  };

  const handleInstallResponse = async (resData, { toastStatus = false } = {}) => {
    if (
      resData.status === "warning" &&
      resData.data?.warning_type === "astrbot_version_incompatible"
    ) {
      onLoadingDialogResult(2, resData.message, -1);
      showVersionCompatibilityWarning(resData.message);
      await refreshExtensionsAfterInstallFailure();
      return false;
    }

    if (toastStatus) {
      toast(resData.message, resData.status === "ok" ? "success" : "error");
    }

    if (resData.status === "error") {
      onLoadingDialogResult(2, resData.message, -1);
      await refreshExtensionsAfterInstallFailure();
      return false;
    }

    return true;
  };

  const performInstallRequest = async ({ source, ignoreVersionCheck }) => {
    if (source === "file") {
      const formData = new FormData();
      formData.append("file", upload_file.value);
      formData.append("ignore_version_check", String(ignoreVersionCheck));
      return axios.post("/api/plugin/install-upload", formData, {
        headers: {
          "Content-Type": "multipart/form-data",
        },
      });
    }

    return axios.post("/api/plugin/install", {
      url: extension_url.value,
      proxy: getSelectedGitHubProxy(),
      ignore_version_check: ignoreVersionCheck,
    });
  };

  const finalizeSuccessfulInstall = async (resData, source) => {
    if (source === "file") {
      upload_file.value = null;
    } else {
      extension_url.value = "";
    }

    onLoadingDialogResult(1, resData.message);
    dialog.value = false;
    await getExtensions();
    checkAlreadyInstalled();

    viewReadme({
      name: resData.data.name,
      repo: resData.data.repo || null,
    });

    await checkAndPromptConflicts();
  };
  
  const newExtension = async (ignoreVersionCheck = false) => {
    if (extension_url.value === "" && upload_file.value === null) {
      toast(tm("messages.fillUrlOrFile"), "error");
      return;
    }
  
    if (extension_url.value !== "" && upload_file.value !== null) {
      toast(tm("messages.dontFillBoth"), "error");
      return;
    }
    loading_.value = true;
    loadingDialog.title = tm("status.loading");
    loadingDialog.show = true;

    const source = upload_file.value !== null ? "file" : "url";
    toast(
      source === "file"
        ? tm("messages.installing")
        : tm("messages.installingFromUrl") + " " + extension_url.value,
      "primary",
    );

    try {
      const res = await performInstallRequest({ source, ignoreVersionCheck });
      loading_.value = false;

      const canContinue = await handleInstallResponse(res.data, {
        toastStatus: source === "url",
      });
      if (!canContinue) return;

      await finalizeSuccessfulInstall(res.data, source);
    } catch (err) {
      loading_.value = false;
      const message = resolveErrorMessage(err, tm("messages.installFailed"));
      if (source === "url") {
        toast(message, "error");
      }
      onLoadingDialogResult(2, message, -1);
      await refreshExtensionsAfterInstallFailure();
    }
  };
  
  const normalizePlatformList = (platforms) => {
    if (!Array.isArray(platforms)) return [];
    return platforms.filter((item) => typeof item === "string");
  };
  
  const getPlatformDisplayList = (platforms) => {
    return normalizePlatformList(platforms).map((platformId) =>
      getPlatformDisplayName(platformId),
    );
  };
  
  const resolveSelectedInstallPlugin = () => {
    if (
      selectedMarketInstallPlugin.value &&
      selectedMarketInstallPlugin.value.repo === extension_url.value
    ) {
      return selectedMarketInstallPlugin.value;
    }
    return pluginMarketData.value.find((plugin) => plugin.repo === extension_url.value) || null;
  };
  
  const selectedInstallPlugin = computed(() => resolveSelectedInstallPlugin());
  
  const checkInstallCompatibility = async () => {
    installCompat.checked = false;
    installCompat.compatible = true;
    installCompat.message = "";
  
    const plugin = selectedInstallPlugin.value;
    if (!plugin?.astrbot_version || uploadTab.value !== "url") {
      return;
    }
  
    try {
      const res = await axios.post("/api/plugin/check-compat", {
        astrbot_version: plugin.astrbot_version,
      });
      if (res.data.status === "ok") {
        installCompat.checked = true;
        installCompat.compatible = !!res.data.data?.compatible;
        installCompat.message = res.data.data?.message || "";
      }
    } catch (err) {
      console.debug("Failed to check plugin compatibility:", err);
    }
  };
  
  // 刷新插件市场数据
  const refreshPluginMarket = async () => {
    refreshingMarket.value = true;
    try {
      // 强制刷新插件市场数据
      const data = await commonStore.getPluginCollections(
        true,
        selectedSource.value,
      );
      pluginMarketData.value = data;
      trimExtensionName();
      checkAlreadyInstalled();
      checkUpdate();
      refreshRandomPlugins();
      currentPage.value = 1; // 重置到第一页
  
      toast(tm("messages.refreshSuccess"), "success");
    } catch (err) {
      toast(tm("messages.refreshFailed") + " " + err, "error");
    } finally {
      refreshingMarket.value = false;
    }
  };
  
  // 生命周期
  onMounted(async () => {
    if (!syncTabFromHash(getLocationHash())) {
      await replaceTabRoute(router, route, activeTab.value);
    }
    await getExtensions();
  
    // 加载自定义插件源
    loadCustomSources();
  
    // 检查是否有 open_config 参数
    const plugin_name = Array.isArray(route.query.open_config)
      ? route.query.open_config[0]
      : route.query.open_config;
    if (plugin_name) {
      console.log(`Opening config for plugin: ${plugin_name}`);
      openExtensionConfig(plugin_name);
    }
  
    try {
      const data = await commonStore.getPluginCollections(
        false,
        selectedSource.value,
      );
      pluginMarketData.value = data;
      trimExtensionName();
      checkAlreadyInstalled();
      checkUpdate();
      refreshRandomPlugins();
    } catch (err) {
      toast(tm("messages.getMarketDataFailed") + " " + err, "error");
    }
  });
  
  // 处理语言切换事件，重新加载插件配置以获取插件的 i18n 数据
  const handleLocaleChange = () => {
    // 如果配置对话框是打开的，重新加载当前插件的配置
    if (configDialog.value && currentConfigPlugin.value) {
      openExtensionConfig(currentConfigPlugin.value);
    }
  };
  
  // 监听语言切换事件
  window.addEventListener("astrbot-locale-changed", handleLocaleChange);
  
  // 清理事件监听器
  onUnmounted(() => {
    window.removeEventListener("astrbot-locale-changed", handleLocaleChange);
  });
  
  // 搜索防抖处理
  let searchDebounceTimer = null;
  watch(marketSearch, (newVal) => {
    if (searchDebounceTimer) {
      clearTimeout(searchDebounceTimer);
    }
  
    searchDebounceTimer = setTimeout(() => {
      debouncedMarketSearch.value = newVal;
      // 搜索时重置到第一页
      currentPage.value = 1;
    }, 300); // 300ms 防抖延迟
  });
  
  // 监听显示模式变化并保存到 localStorage
  watch(isListView, (newVal) => {
    if (typeof window !== "undefined" && window.localStorage) {
      localStorage.setItem("pluginListViewMode", String(newVal));
    }
  });
  
  watch(
    [() => dialog.value, () => extension_url.value, () => uploadTab.value],
    async ([dialogOpen, _, currentUploadTab]) => {
      if (!dialogOpen || currentUploadTab !== "url") {
        installCompat.checked = false;
        installCompat.compatible = true;
        installCompat.message = "";
        return;
      }
      await checkInstallCompatibility();
    },
  );
  
  watch(
    () => route.hash,
    (newHash) => {
      const tab = extractTabFromHash(newHash);
      if (tab && tab !== activeTab.value) {
        activeTab.value = tab;
      }
    },
  );
  
  watch(activeTab, (newTab) => {
    if (!isValidTab(newTab)) return;
    if (route.hash === `#${newTab}`) return;
    void replaceTabRoute(router, route, newTab);
  });

  return {
    commonStore,
    t,
    tm,
    router,
    route,
    getSelectedGitHubProxy,
    conflictDialog,
    checkAndPromptConflicts,
    handleConflictConfirm,
    fileInput,
    activeTab,
    validTabs,
    isValidTab,
    getLocationHash,
    extractTabFromHash,
    syncTabFromHash,
    extension_data,
    getInitialShowReserved,
    showReserved,
    snack_message,
    snack_show,
    snack_success,
    configDialog,
    extension_config,
    pluginMarketData,
    loadingDialog,
    showPluginInfoDialog,
    selectedPlugin,
    curr_namespace,
    updatingAll,
    readmeDialog,
    forceUpdateDialog,
    updateAllConfirmDialog,
    changelogDialog,
    getInitialListViewMode,
    isListView,
    pluginSearch,
    installedStatusFilter,
    installedSortBy,
    installedSortOrder,
    loading_,
    currentPage,
    dangerConfirmDialog,
    selectedDangerPlugin,
    selectedMarketInstallPlugin,
    installCompat,
    versionCompatibilityDialog,
    showUninstallDialog,
    uninstallTarget,
    showSourceDialog,
    showSourceManagerDialog,
    sourceName,
    sourceUrl,
    customSources,
    selectedSource,
    showRemoveSourceDialog,
    sourceToRemove,
    editingSource,
    originalSourceUrl,
    extension_url,
    dialog,
    upload_file,
    uploadTab,
    showPluginFullName,
    marketSearch,
    debouncedMarketSearch,
    refreshingMarket,
    sortBy,
    sortOrder,
    randomPluginNames,
    showRandomPlugins,
    normalizeStr,
    toPinyinText,
    toInitials,
    plugin_handler_info_headers,
    installedSortItems,
    installedSortUsesOrder,
    pluginHeaders,
    filteredExtensions,
    filteredPlugins,
    filteredMarketPlugins,
    sortedPlugins,
    RANDOM_PLUGINS_COUNT,
    randomPlugins,
    shufflePlugins,
    refreshRandomPlugins,
    toggleRandomPluginsVisibility,
    collapseRandomPlugins,
    displayItemsPerPage,
    totalPages,
    paginatedPlugins,
    updatableExtensions,
    toggleShowReserved,
    toast,
    resetLoadingDialog,
    onLoadingDialogResult,
    failedPluginsDict,
    failedPluginItems,
    getExtensions,
    handleReloadAllFailed,
    reloadFailedPlugin,
    checkUpdate,
    uninstallExtension,
    requestUninstallPlugin,
    requestUninstallFailedPlugin,
    handleUninstallConfirm,
    updateExtension,
    showUpdateAllConfirm,
    confirmUpdateAll,
    cancelUpdateAll,
    confirmForceUpdate,
    updateAllExtensions,
    pluginOn,
    pluginOff,
    openExtensionConfig,
    updateConfig,
    showPluginInfo,
    reloadPlugin,
    viewReadme,
    viewChangelog,
    handleInstallPlugin,
    confirmDangerInstall,
    cancelDangerInstall,
    loadCustomSources,
    saveCustomSources,
    addCustomSource,
    openSourceManagerDialog,
    selectPluginSource,
    sourceSelectItems,
    editCustomSource,
    removeCustomSource,
    confirmRemoveSource,
    saveCustomSource,
    trimExtensionName,
    checkAlreadyInstalled,
    showVersionCompatibilityWarning,
    continueInstallIgnoringVersionWarning,
    cancelInstallOnVersionWarning,
    newExtension,
    normalizePlatformList,
    getPlatformDisplayList,
    resolveSelectedInstallPlugin,
    selectedInstallPlugin,
    checkInstallCompatibility,
    refreshPluginMarket,
    handleLocaleChange,
    searchDebounceTimer,
  };
};
