(function () {
  'use strict';

  // 工具方法
  function getDomainFromUrlString(urlString) {
    if (!urlString) {
      return;
    }
    if (!urlString.includes("://")) {
      urlString = "http://" + urlString;
    }
    try {
      const url = new URL(urlString);
      return url.hostname;
    } catch (error) {
      return "";
    }
  }
  function getFallbackLangValue() {
    return navigator?.language?.includes("zh")
      ? "zh"
      : navigator?.language?.includes("ja")
      ? "ja"
      : navigator?.language?.includes("ko")
      ? "ko"
      : "en";
  }

  // 内建站点列表
  const defaultWebsites = [
    {
      i18n: "zhongGuoZhanDian",
      value: "https://boardmix.cn",
      api: "https://api.boardmix.cn",
    },
    {
      i18n: "haiWaiZhanDian",
      value: "https://boardmix.com",
      api: "https://api.boardmix.com",
    },
  ];
  function getFallbackWebsite() {
    return navigator?.language?.includes("zh")
      ? defaultWebsites[0]
      : defaultWebsites[1];
  }

  // 内建团队列表
  async function getDefaultTeams() {
    const webSpace = await getStoragedWebSpace();
    if (webSpace) {
      return [
        {
          id: 0,
          i18n: "geRen",
        },
      ];
    }
  }

  async function getFallbackTeam() {
    return (await getDefaultTeams())?.[0];
  }

  // 监听站点切换
  let selectedWebsite$1;
  const watchStoragedSelectedWebsiteCallback$1 = (storagedSelectedWebsite) => {
    if (storagedSelectedWebsite) {
      selectedWebsite$1 = JSON.parse(storagedSelectedWebsite);
    } else {
      selectedWebsite$1 = getFallbackWebsite();
    }
  };
  watchStoragedSelectedWebsite(watchStoragedSelectedWebsiteCallback$1);

  // 监听cookie变化，广播给所有watch方法.
  const cookieKeyDepMap = new Map();
  chrome?.cookies?.onChanged?.addListener((changeInfo) => {
    if (!selectedWebsite$1) {
      return;
    }
    const domain = getDomainFromUrlString(selectedWebsite$1.value);
    if (
      changeInfo.cookie.domain === domain &&
      ["expired_overwrite", "explicit"].includes(changeInfo.cause)
    ) {
      console.log(changeInfo, changeInfo.cookie.name);
      const dep = cookieKeyDepMap.get(changeInfo.cookie.name);
      const newValue =
        changeInfo.cause === "expired_overwrite" ||
        (changeInfo.cause === "explicit" && changeInfo.removed)
          ? undefined
          : changeInfo.cookie.value;
      dep?.callbacks && dep.callbacks.forEach((item) => item(newValue));
    }
  });
  const watchCookie = (key, callback) => {
    let dep = cookieKeyDepMap.get(key);
    if (!dep) {
      dep = {};
      cookieKeyDepMap.set(key, dep);
    }
    if (!dep.callbacks) {
      dep.callbacks = [];
    }
    dep.callbacks.push(callback);
  };
  const unwatchCookie = (key, callback) => {
    const dep = cookieKeyDepMap.get(key);
    if (dep && dep.callbacks) {
      const index = dep.callbacks.findIndex((item) => item === callback);
      index >= 0 && dep.callbacks.splice(index, 1);
    }
  };

  // 监听storage变化，广播给所有watch方法.
  const storageKeyDepMap = new Map();
  chrome?.storage?.onChanged?.addListener((changes, namespace) => {
    for (let [key, { oldValue, newValue }] of Object.entries(changes)) {
      const dep = storageKeyDepMap.get(key);
      dep?.callbacks && dep.callbacks.forEach((item) => item(newValue));
    }
  });
  const watchStorage = (key, callback) => {
    let dep = storageKeyDepMap.get(key);
    if (!dep) {
      dep = {};
      storageKeyDepMap.set(key, dep);
    }
    if (!dep.callbacks) {
      dep.callbacks = [];
    }
    dep.callbacks.push(callback);
  };

  // userInfo有前置依赖selectedWebsite
  async function getCookieUserInfo() {
    return new Promise((resolve, reject) => {
      const runloop = () => {
        setTimeout(async () => {
          if (selectedWebsite$1.value) {
            const name = "BOSYUNCurrent";
            const domain = getDomainFromUrlString(selectedWebsite$1.value);
            const cookies = await chrome?.cookies?.getAll({ domain, name });
            const cookie = cookies?.find((cookie) => cookie.domain === domain);
            if (cookie) {
              resolve(decodeURIComponent(cookie.value));
            } else {
              resolve(undefined);
            }
          } else {
            runloop();
          }
        }, 50);
      };
      runloop();
    });
  }
  async function watchCookieUserInfo(callback) {
    callback(await getCookieUserInfo());
    watchCookie("BOSYUNCurrent", callback);
  }
  function unwatchCookieUserInfo(callback) {
    unwatchCookie("BOSYUNCurrent", callback);
  }
  async function updateCookieUserInfo(userInfo) {
    if (!selectedWebsite$1) {
      return;
    }
    const domain = getDomainFromUrlString(selectedWebsite$1.value);
    const name = "BOSYUNCurrent";
    // @ts-ignore
    const cookies = await chrome?.cookies?.getAll({ domain, name });
    const cookie = cookies?.find((cookie) => cookie.domain === domain);
    if (cookie) {
      if (userInfo) {
        cookie.value = encodeURIComponent(JSON.stringify(userInfo));
        delete cookie["hostOnly"];
        delete cookie["session"];
        delete cookie["domain"];
        cookie.url = selectedWebsite$1.value;
        await chrome?.cookies?.set(cookie);
      } else {
        await chrome?.cookies?.remove({ url: selectedWebsite$1.value, name });
      }
    }
  }
  async function updateStoragedCompanys(companys) {
    if (companys) {
      await chrome?.storage?.local?.set({
        companys: JSON.stringify(companys),
      });
    } else {
      await chrome?.storage?.local?.remove(["companys"]);
    }
  }

  // webSpace
  async function getStoragedWebSpace() {
    const result = await chrome?.storage?.local?.get(["webSpace"]);
    return result?.webSpace;
  }
  async function updateStoragedWebSpace(webSpace) {
    if (webSpace) {
      await chrome?.storage?.local?.set({
        webSpace: JSON.stringify(webSpace),
      });
    } else {
      await chrome?.storage?.local?.remove(["webSpace"]);
    }
  }

  // teams
  async function getStoragedTeams() {
    const result = await chrome?.storage?.local?.get(["teams"]);
    return result?.teams;
  }
  async function updateStoragedTeams(teams) {
    if (teams) {
      await chrome?.storage?.local?.set({
        teams: JSON.stringify(teams),
      });
    } else {
      await chrome?.storage?.local?.remove(["teams"]);
    }
  }

  // selectedLang
  async function getStoragedSelectedLang() {
    const result = await chrome?.storage?.local?.get(["selectedLang"]);
    return result?.selectedLang;
  }
  async function watchStoragedSelectedLang(callback) {
    callback(await getStoragedSelectedLang());
    watchStorage("selectedLang", callback);
  }

  // selectedTeam
  async function getStoragedSelectedTeam() {
    const result = await chrome?.storage?.local?.get(["selectedTeam"]);
    return result?.selectedTeam;
  }
  async function watchStoragedSelectedTeam(callback) {
    callback(await getStoragedSelectedTeam());
    watchStorage("selectedTeam", callback);
  }
  async function updateStoragedSelectedTeam(team) {
    if (team) {
      await chrome?.storage?.local?.set({
        selectedTeam: JSON.stringify(team),
      });
    } else {
      await chrome?.storage?.local?.remove(["selectedTeam"]);
    }
  }

  // selectedWebsite
  async function getStoragedSelectedWebsite() {
    const result = await chrome?.storage?.local?.get(["selectedWebsite"]);
    return result?.selectedWebsite;
  }
  async function watchStoragedSelectedWebsite(callback) {
    callback(await getStoragedSelectedWebsite());
    watchStorage("selectedWebsite", callback);
  }

  // 写入localStorage
  function updateLocalStorageCurrentId(value) {
    const _updateLocalStorageCurrentId = (value) => {
      const key = "currentId";
      if (localStorage.getItem(key) !== value) {
        localStorage.setItem(key, value);
        globalThis.dispatchEvent(
          new CustomEvent("bm_invoke", {
            detail: {
              method: "showModalChangeSpace",
            },
          })
        );
      }
    };
    // 在background脚本中获取当前活动窗口的标签页
    chrome.tabs.query({}, (tabs) => {
      // 遍历所有标签页
      for (const tab of tabs) {
        // 如果标签页的URL匹配指定的域名
        if (
          tab.url === selectedWebsite$1.value ||
          tab.url.startsWith(selectedWebsite$1.value)
        ) {
          // 向标签页注入脚本
          chrome.scripting.executeScript({
            target: { tabId: tab.id },
            func: _updateLocalStorageCurrentId,
            args: [value],
          });
          break;
        }
      }
    });
  }

  var localeZh = {
    wEBChaJian: "web插件_",
    wuCaoZuoCiZi: "无操作此资源权限，保存失败",
    qiYeKongJianBu: "企业空间不一致，保存失败",
    neiRongWeiGuiBao: "内容违规，保存失败",
    suoXuanKongJianRong: "所选空间容量不足，保存失败",
    baoCunShiBai: "保存失败",
    yiChengGongBaoCun: "已成功保存至素材库",
    baoCunZhong: "保存中...",
    tuPian: "图片",
    wenBen: "文本",
    lianJie: "链接",
    caiJiWenBen: "采集文字到Boardmix素材库",
    caiJiTuPian: "采集图片到Boardmix素材库",
    caiJiLianJie: "采集链接到Boardmix素材库",
    baoCunDaoSuCai: "保存到素材库失败",
    qingQiuShiBai: "请求失败",
    chaJianWeiDengLu: "插件未登录，请登录后使用",
    collect: "收藏",
    tagOversize: "标签数超过限制",
  };

  var localeEn = {
    wEBChaJian: "webPlugin_",
    wuCaoZuoCiZi: "No permission to operate this resource, save failed.",
    qiYeKongJianBu: "Workspace inconsistency, save failed.",
    neiRongWeiGuiBao: "Content violation, save failed.",
    suoXuanKongJianRong:
      "Insufficient storage of selected workspace, save failed.",
    baoCunShiBai: "Save failed",
    yiChengGongBaoCun: "saved to Boardmix resource library",
    baoCunZhong: "Saving...",
    tuPian: "Image",
    wenBen: "Text",
    lianJie: "URL",
    caiJiWenBen: "Capture text to Boardmix resource library",
    caiJiTuPian: "Capture the image to Boardmix resource library",
    caiJiLianJie: "Capture the URL to Boardmix resource library",
    baoCunDaoSuCai: "Failed to save to Boardmix resource library",
    qingQiuShiBai: "Request failed",
    chaJianWeiDengLu: "You are not logged in. Please log in to use the plugin.",
    collect: "Favorite",
    tagOversize: "Exceeded tag limit",
  };

  var localeJa = {
    wEBChaJian: "webプラグイン_",
    wuCaoZuoCiZi: "このリソース権限を操作せず、保存に失敗しました",
    qiYeKongJianBu: "エンタープライズスペースが一致せず、保存に失敗しました",
    neiRongWeiGuiBao: "コンテンツ違反、保存失敗",
    suoXuanKongJianRong: "選択したスペースの容量が不足し、保存に失敗しました",
    baoCunShiBai: "保存に失敗しました",
    yiChengGongBaoCun: "素材中継所への保存に成功しました",
    baoCunZhong: "保存中…",
    tuPian: "ピクチャー",
    wenBen: "テキスト",
    lianJie: "リンク",
    caiJiWenBen: "Boardmix素材中継所にテキストの取得",
    caiJiTuPian: "Boardmix素材中継所に画像を収集",
    caiJiLianJie: "Boardmix素材中継所にリンクの取得",
    baoCunDaoSuCai: "素材中継所に保存できなかった",
    qingQiuShiBai: "リクエスト失敗",
    chaJianWeiDengLu:
      "プラグインはログインしていません。ログインしてから使用してください",
    collect: "お気に入り",
    tagOversize: "タグの制限を超える",
  };

  var localeKo = {
    wEBChaJian: "웹 플러그인_",
    wuCaoZuoCiZi: "해당 리소스를 편집할 수 있는 권한이 없습니다. 저장에 실패했습니다.",
    qiYeKongJianBu: "기업 공간이 일치하지 않아 저장에 실패했습니다.",
    neiRongWeiGuiBao: "위반 내용이 있어 저장할 수 없습니다.",
    suoXuanKongJianRong: "선택한 공간 용량이 부족하여 저장에 실패했습니다.",
    baoCunShiBai: "저장할 수 없습니다.",
    yiChengGongBaoCun: "Boardmix 소재 라이브러리에 저장했습니다.",
    baoCunZhong: "저장 중...",
    tuPian: "이미지",
    wenBen: "텍스트",
    lianJie: "링크",
    caiJiWenBen: "Boardmix 소재 라이브러리에 텍스트 저장",
    caiJiTuPian: "Boardmix 소재 라이브러리에 사진 저장",
    caiJiLianJie: "Boardmix 소재 라이브러리에 링크 저장",
    baoCunDaoSuCai: "소재 라이브러리에 저장할 수 없습니다.",
    qingQiuShiBai: "요청 실패했습니다.",
    chaJianWeiDengLu:
      "플러그인이 로그인되지 않았습니다. 로그인한 후 사용하십시오",
    collect: "즐겨찾기",
    tagOversize: "태그 제한을 초과했습니다",
  };

  const onActionClick = async (tab) => {
    await chrome.tabs.sendMessage(tab.id, {
      origin: "background",
      target: "content",
      payload: {
        value: "togglePopup",
      },
    });
  };
  chrome.action.onClicked.addListener(onActionClick);

  // 多语言支持
  let I18N;
  const localeMap = {
    zh: localeZh,
    en: localeEn,
    ja: localeJa,
    ko: localeKo,
  };
  const watchStoragedSelectedLangCallback = (storagedSelectedLang) => {
    if (storagedSelectedLang) {
      const lang = JSON.parse(storagedSelectedLang);
      I18N = localeMap[lang.value] ?? localeMap[getFallbackLangValue()];
    } else {
      I18N = localeMap[getFallbackLangValue()];
    }
    reInitContextMenus();
  };
  watchStoragedSelectedLang(watchStoragedSelectedLangCallback);

  // 监听站点切换
  let apiUrl;
  let selectedWebsite;
  const watchStoragedSelectedWebsiteCallback = (storagedSelectedWebsite) => {
    if (storagedSelectedWebsite) {
      selectedWebsite = JSON.parse(storagedSelectedWebsite);
    } else {
      selectedWebsite = getFallbackWebsite();
    }
    apiUrl = selectedWebsite.api ?? selectedWebsite.value;
    // 站点切换后，重新监听用户信息。
    reWatchCookieUserInfo();
  };
  watchStoragedSelectedWebsite(watchStoragedSelectedWebsiteCallback);

  // 用户信息监听
  let userInfo;
  const watchCookieUserInfoCallback = async (cookieUserInfo) => {
    if (cookieUserInfo) {
      userInfo = JSON.parse(decodeURIComponent(cookieUserInfo));
      await getCompanys();
      await getWebSpace();
      await getTeams();
      // 如果不存在webspace，并且本地没有主动选择过team，则默认选中第一个team。
      const webSpace = await getStoragedWebSpace();
      const selectedTeam = await getStoragedSelectedTeam();
      const teams = JSON.parse(await getStoragedTeams());
      if (!webSpace && !selectedTeam && teams?.length > 0) {
        updateStoragedSelectedTeam(teams[0]);
      }
    } else {
      userInfo = null;
    }
    updateLocalStorageCurrentId(userInfo?.user_id);
  };
  const reWatchCookieUserInfo = () => {
    unwatchCookieUserInfo(watchCookieUserInfoCallback);
    watchCookieUserInfo(watchCookieUserInfoCallback);
  };

  // 选中团队监听
  let selectedTeam;
  const watchStoragedSelectedTeamCallback = async (storagedSelectedTeam) => {
    if (storagedSelectedTeam) {
      selectedTeam = JSON.parse(storagedSelectedTeam);
    } else {
      selectedTeam = await getFallbackTeam();
    }
  };
  watchStoragedSelectedTeam(watchStoragedSelectedTeamCallback);

  chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
    if (message.target === "background") {
      switch (message.payload.value) {
        case "screenshot": {
          chrome.tabs.captureVisibleTab().then((data) => {
            sendResponse(data);
          });
          return true;
        }
        case "proxyAccess": {
          proxyAccess(message.payload);
          break;
        }
        case "uploadFile": {
          uploadFile(message.payload.data)
            .then((data) => sendResponse(data))
            .catch((e) => sendResponse(undefined));
          return true;
        }
        case "thumbnail": {
          waitingMap[message.payload.id] = message.payload.data;
          break;
        }
        case "switchCompany": {
          switchCompany(message.payload.data);
          break;
        }
        case "getTags": {
          getTags().then((data) => {
            sendResponse(data);
          });
          return true;
        }
        case "createTag": {
          createTag(message.payload.data)
            .then((data) => {
              sendResponse(data);
            })
            .catch((e) => {
              showToast({
                state: "info",
                title: e.message,
                duration: 1600,
              });
              sendResponse(undefined);
            });
          return true;
        }
        case "exit": {
          chrome.tabs.query(
            { active: true, currentWindow: true },
            function (tabs) {
              var activeTab = tabs[0];
              activeTab && onActionClick(activeTab);
            }
          );
          break;
        }
        // 编辑图片
        case "edit": {
          chrome.tabs.create(
            { url: "src/views/editor/index.html" },
            function (editTab) {}
          );
          break;
        }
        default:
          message.onResponse({ code: 200, origin: "background" });
      }
    }
  });

  async function getTags() {
    if (!userInfo) {
      // 用户信息不存在
      throw new Error("User Not Found");
    }
    const params = new URLSearchParams();
    params.append("team_id", selectedTeam?.id ?? 0);
    const url = apiUrl + `/api/web/material_lib/tag/list?${params.toString()}`;
    const response = await fetch(url, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer " + userInfo.access_token,
      },
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        const json = await response.json();
        return json;
      } else {
        // http code错误
        throw new Error("Response Error");
      }
    } else {
      // 鉴权失败
      throw new Error("Auth Error");
    }
  }

  async function createTag(name) {
    if (!userInfo) {
      // 用户信息不存在
      // throw new Error("User Not Found");
      return;
    }
    const url = apiUrl + `/api/web/material_lib/tag/`;
    const response = await fetch(url, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer " + userInfo.access_token,
      },
      body: JSON.stringify({
        team_id: selectedTeam?.id ?? 0,
        tag: name,
        ext: { color: "rgb(240, 204, 24)" },
      }),
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        const json = await response.json();
        if (json.code === 0) {
          return true;
        } else if (json.code === 101) {
          throw new Error(I18N?.tagOversize);
        }
      } else {
        if (response.status === 400) {
          const json = await response.json();
          if (json.code === 100) {
            // 标签名重复，因为标签只是单纯的字符串，所以可以当作成功发送给前端。
            return true;
          }
        }
        // http code错误
        throw new Error(I18N?.qingQiuShiBai);
      }
    } else {
      // 鉴权失败
      // throw new Error("Auth Error");
      return;
    }
  }

  const waitingMap = {};

  function switchCompany(e_id) {
    resetToken(e_id);
    updateStoragedTeams();
    updateStoragedSelectedTeam();
  }

  const waitForUserInfoInit = () => {
    return new Promise((resolve, reject) => {
      let count = 0;
      const runloop = () => {
        setTimeout(() => {
          if (userInfo !== undefined) {
            resolve();
          } else if (count > 10) {
            reject();
          } else {
            count += 1;
            runloop();
          }
        }, 100);
      };
      runloop();
    });
  };

  const proxyAccess = async (payload) => {
    if (userInfo === null) {
      handleUserInfoNotExist();
      return;
    } else if (userInfo === undefined) {
      await waitForUserInfoInit();
      if (!userInfo) {
        return;
      }
    }
    const url =
      apiUrl +
      "/api/editor/proxy/spider?uri=" +
      encodeURIComponent(
        `/api/webpage/iframe/links?url=${payload.data}&isNeedImage=true`
      );
    const response = await fetch(url, {
      method: "GET",
      headers: {
        Authorization: "Bearer " + userInfo.access_token,
      },
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        const json = await response.json();
        chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
          var activeTab = tabs[0];
          activeTab &&
            chrome.tabs.sendMessage(activeTab.id, {
              origin: "background",
              target: "content",
              payload: {
                value: payload.value,
                id: payload.id,
                data: json,
              },
            });
        });
        chrome.runtime.sendMessage({
          origin: "background",
          target: "popup",
          payload: {
            value: payload.value,
            id: payload.id,
            data: json,
          },
        });
      }
    } else {
      // 鉴权失败
      handleAuthCheckFail();
    }
  };

  function handleUserInfoNotExist() {
    showToast({
      state: "info",
      title: I18N.chaJianWeiDengLu,
      duration: 1600,
    });
  }

  function handleAuthCheckFail(toastId) {
    showToast({
      state: "info",
      title: I18N.qingQiuShiBai,
      duration: 1600,
      toastId,
    });
    refreshToken();
  }

  function handleOtherError(toastId) {
    showToast({
      state: "info",
      title: I18N.baoCunDaoSuCai,
      duration: 1600,
      toastId,
    });
  }

  function showToast(data) {
    chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
      var activeTab = tabs[0];
      activeTab &&
        chrome.tabs.sendMessage(activeTab.id, {
          origin: "background",
          target: "content",
          payload: {
            value: "showToast",
            data,
          },
        });
    });
  }

  const getTypeDescription = (type) => {
    let description = "";
    switch (type) {
      case "link": {
        description = I18N.lianJie;
        break;
      }
      case "text": {
        description = I18N.wenBen;
        break;
      }
      case "image": {
        description = I18N.tuPian;
        break;
      }
    }
    return description;
  };

  const uploadFile = async (data) => {
    let toastId = data.toastId ?? new Date().getTime();
    if (userInfo === null) {
      handleUserInfoNotExist();
      return;
    } else if (userInfo === undefined) {
      await waitForUserInfoInit();
      if (!userInfo) {
        return;
      }
    } else {
      !data.silence &&
        showToast({
          state: "loading",
          title: I18N.baoCunZhong,
          duration: 10000,
          toastId,
        });
    }
    const url = apiUrl + "/api/netdisk/resource/sign_upload/apply";
    const file = await convertData2File(data);
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer " + userInfo.access_token,
      },
      body: JSON.stringify(file),
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        const json = await response.json();
        if (json.data?.url && json.data?.res_key) {
          if (!json.data.url.startsWith("http")) {
            json.data.url = apiUrl + json.data.url;
          }
          const response2 = await fetch(json.data?.url, {
            method: "PUT",
            headers: {
              "Content-Type": file.mime,
            },
            body: await convertData2File(data, true),
          });
          if (response2.status === 200) {
            const formdata3 = new FormData();
            formdata3.append("res_key", json.data?.res_key);
            formdata3.append("team_id", file.team_id);
            if (file.thumbnail) {
              formdata3.append("thumb", file.thumbnail);
            }
            const url3 = apiUrl + "/api/netdisk/resource/sign_upload/submit";
            const response3 = await fetch(url3, {
              method: "POST",
              headers: {
                Authorization: "Bearer " + userInfo.access_token,
              },
              body: formdata3,
            });
            if (response3.status === 200) {
              // 上传成功, 这里需要打tag和添加备注
              if (json.data.res_key && data.tags) {
                for (const tag of data.tags) {
                  await addTag(json.data.res_key, tag);
                }
              }
              json.data.res_key &&
                data.remark &&
                (await updateRemark(json.data.res_key, data.remark));
              !data.silence &&
                showToast({
                  state: "success",
                  title:
                    getTypeDescription(data.type) + " " + I18N.yiChengGongBaoCun,
                  duration: 1600,
                  toastId,
                });
              return true;
            } else {
              !data.silence && handleOtherError(toastId);
            }
          } else {
            !data.silence && handleOtherError(toastId);
          }
        } else {
          !data.silence && handleOtherError(toastId);
        }
      } else if (response.status === 406) {
        const json = await response.json();
        let title = I18N.baoCunShiBai;
        if (json.code == 5000) {
          title = I18N.suoXuanKongJianRong;
        } else if (json.code == 5003) {
          title = I18N.neiRongWeiGuiBao;
        } else if (json.code == 5400) {
          title = I18N.qiYeKongJianBu;
        } else if (json.code == 5401) {
          title = I18N.wuCaoZuoCiZi;
        }
        !data.silence &&
          showToast({
            state: "info",
            title,
            duration: 1600,
            toastId,
          });
      } else {
        !data.silence && handleOtherError(toastId);
      }
    } else {
      // 鉴权失败
      handleAuthCheckFail(toastId);
    }
  };

  const addTag = async (res_key, tag) => {
    if (!userInfo) {
      return;
    }
    const url = apiUrl + `/api/web/material_lib/material/tag`;
    const response = await fetch(url, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer " + userInfo.access_token,
      },
      body: JSON.stringify({
        team_id: selectedTeam?.id ?? 0,
        tag,
        material_key: res_key,
        material_type: "resource",
      }),
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        return true;
      }
    }
  };

  const updateRemark = async (res_key, remark) => {
    if (!userInfo) {
      return;
    }
    const url = apiUrl + `/api/netdisk/resource/${res_key}`;
    const response = await fetch(url, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer " + userInfo.access_token,
      },
      body: JSON.stringify({
        team_id: selectedTeam?.id ?? 0,
        description: remark,
      }),
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        return true;
      }
    }
  };

  const getCompanys = async () => {
    if (!userInfo) {
      return;
    }
    const url = apiUrl + "/api/user/ent/related?with_pending=false";
    const response = await fetch(url, {
      method: "GET",
      headers: {
        Authorization: "Bearer " + userInfo.access_token,
      },
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        const json = await response.json();
        updateStoragedCompanys(json.data?.joined);
      }
    } else {
      refreshToken();
    }
  };

  const resetToken = async (e_id) => {
    if (!userInfo) {
      return;
    }
    const url = apiUrl + "/api/user/user/reset_token";
    const response = await fetch(url, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer " + userInfo.refresh_token,
      },
      body: JSON.stringify({
        e_id,
      }),
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        const json = await response.json();
        if (json.data.user_info) {
          const userInfo = json.data.user_info;
          userInfo.refresh_token = json.data.refresh_token;
          userInfo.access_token = json.data.access_token;
          updateCookieUserInfo(userInfo);
        }
      }
    }
  };

  const getWebSpace = async () => {
    if (!userInfo) {
      return;
    }
    const url = apiUrl + "/api/web/space";
    const response = await fetch(url, {
      method: "GET",
      headers: {
        Authorization: "Bearer " + userInfo.access_token,
      },
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        const json = await response.json();
        updateStoragedWebSpace(json.data);
      } else {
        updateStoragedWebSpace();
      }
    }
  };

  const getTeams = async () => {
    if (!userInfo) {
      return;
    }
    const url = apiUrl + "/api/web/teams";
    const response = await fetch(url, {
      method: "GET",
      headers: {
        Authorization: "Bearer " + userInfo.access_token,
      },
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        const json = await response.json();
        updateStoragedTeams(json.data);
      } else {
        updateStoragedTeams();
      }
    }
  };

  const isTokenNotDenied = (response) => {
    if (response.status === 401) {
      return false;
    }
    return true;
  };

  function blobToBase64(blob) {
    return new Promise((resolve, _) => {
      const reader = new FileReader();
      reader.onloadend = () => resolve(reader.result);
      reader.readAsDataURL(blob);
    });
  }

  function dataURLtoFile(dataurl, filename) {
    var arr = dataurl.split(","),
      mime = arr[0].match(/:(.*?);/)[1],
      bstr = atob(arr[1]),
      n = bstr.length,
      u8arr = new Uint8Array(n);

    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }

    return new File([u8arr], filename, { type: mime });
  }

  const getThumbnail = (id, blob) => {
    return new Promise((resolve, reject) => {
      blobToBase64(blob).then((data) => {
        chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
          var activeTab = tabs[0];
          activeTab &&
            chrome.tabs.sendMessage(activeTab.id, {
              origin: "background",
              target: "content",
              payload: {
                value: "thumbnail",
                id,
                data,
                mime: blob.type,
              },
            });
        });
      });

      let count = 0;
      const runloop = () => {
        setTimeout(() => {
          count = count + 1;
          console.log("waiting for ", id);
          if (waitingMap[id]) {
            resolve(
              dataURLtoFile(
                waitingMap[id],
                I18N.wEBChaJian + "thumbnail_" + getDateTime()
              )
            );
          } else if (count > 10) {
            reject("timeout");
          } else {
            runloop();
          }
        }, 200);
      };
      runloop();
    });
  };

  const convertBase64ToBlob = (base64) => {
    const base64Arr = base64.split(",");
    let imageType = "";
    let base64String = "";
    if (base64Arr.length > 1) {
      //如果是图片base64，去掉头信息
      base64String = base64Arr[1];
      imageType = base64Arr[0].substring(
        base64Arr[0].indexOf(":") + 1,
        base64Arr[0].indexOf(";")
      );
    }
    // 将base64解码
    const bytes = atob(base64String);
    const bytesCode = new ArrayBuffer(bytes.length);
    // 转换为类型化数组
    const byteArray = new Uint8Array(bytesCode);
    // 将base64转换为ascii码
    for (let i = 0; i < bytes.length; i++) {
      byteArray[i] = bytes.charCodeAt(i);
    }
    // 生成Blob对象（文件对象）
    return new Blob([bytesCode], { type: imageType });
  };

  async function getBase64ImageSize(imageBase64) {
    return new Promise((resolve, reject) => {
      chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        const activeTab = tabs[0];
        chrome.tabs
          .sendMessage(activeTab.id, {
            origin: "background",
            target: "content",
            payload: {
              value: "getBase64ImageSize",
              data: imageBase64,
            },
          })
          .then((size) => {
            resolve(size);
          });
      });
    });
  }

  const convertData2File = async (data, toObj = false) => {
    let mime;
    let blob;
    let type;
    let thumbnail;
    let width;
    let height;
    switch (data.type) {
      case "link": {
        mime = "text/plain";
        blob = new Blob([data.content], { type: mime });
        type = 6;
        break;
      }
      case "text": {
        mime = "text/plain";
        blob = new Blob([data.content], { type: mime });
        type = 1;
        break;
      }
      case "image": {
        if (data.content.match(/^((http|https):\/\/)/)) {
          const response = await fetch(data.content);
          blob = await response.blob();
        } else {
          blob = convertBase64ToBlob(data.content);
        }
        mime = blob.type;
        type = 2;
        if (!toObj) {
          const id = new Date().getTime();
          thumbnail = await getThumbnail(id, blob);
          waitingMap[id] = undefined;
        }
        const imageBase64 = await blobToBase64(blob);
        const size = await getBase64ImageSize(imageBase64);
        width = size.width;
        height = size.height;
        break;
      }
      case "image/base64": {
        blob = dataURLtoFile(data.content, I18N.wEBChaJian + getDateTime());
        mime = blob.type;
        type = 2;
        if (!toObj) {
          const id = new Date().getTime();
          thumbnail = await getThumbnail(id, blob);
          waitingMap[id] = undefined;
        }
        const imageBase64 = await blobToBase64(blob);
        const size = await getBase64ImageSize(imageBase64);
        width = size.width;
        height = size.height;
        break;
      }
    }

    if (toObj) {
      return new File([blob], I18N.wEBChaJian + getDateTime());
    }

    let file_name =
      data.file_name?.length > 0
        ? data.file_name
        : I18N.wEBChaJian + getDateTime();
    if (file_name.length > 99) {
      file_name = file_name.slice(0, 99);
    }
    return {
      file_name,
      from: 2,
      md5: "",
      thumbnail,
      mime,
      size: blob.size,
      team_id: selectedTeam?.id ?? 0,
      type,
      width,
      height,
    };
  };

  function getDateTime() {
    const date = new Date();
    return `${date.getFullYear()}-${
    date.getMonth() + 1
  }-${date.getDate()} ${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`;
  }

  // 定时刷新令牌
  let lastRefreshTime = 0;
  const refreshToken = async () => {
    if (!userInfo) {
      return;
    }
    const url = apiUrl + "/api/user/user/refresh_token";
    const response = await fetch(url, {
      method: "PUT",
      headers: {
        "Content-Type": "application/json",
        Authorization: "Bearer " + userInfo.refresh_token,
      },
    });
    if (isTokenNotDenied(response)) {
      if (response.status === 200) {
        const json = await response.json();
        if (json.data?.access_token && json.data?.refresh_token) {
          userInfo.access_token = json.data?.access_token;
          userInfo.refresh_token = json.data?.refresh_token;
          updateCookieUserInfo(userInfo);
        }
      }
    } else {
      updateCookieUserInfo();
    }
    lastRefreshTime = new Date().getTime();
  };
  setInterval(() => {
    if (new Date().getTime() - lastRefreshTime > 5 * 60 * 1000) {
      refreshToken();
    }
  }, 5 * 1000);

  // 右键菜单
  function onContextMenuClick(info) {
    console.log(info);
    switch (info.menuItemId) {
      case "page": {
        chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
          var activeTab = tabs[0];
          uploadFile({
            type: "link",
            content: info.linkUrl ?? info.pageUrl,
            file_name: activeTab.title,
          });
        });
        break;
      }
      case "link": {
        uploadFile({
          type: "link",
          content: info.linkUrl ?? info.pageUrl,
          file_name: info.linkUrl ?? info.pageUrl,
        });
        break;
      }
      case "video":
      case "audio": {
        uploadFile({ type: "link", content: info.linkUrl ?? info.pageUrl });
        break;
      }
      case "image": {
        info.srcUrl && uploadFile({ type: "image", content: info.srcUrl });
        break;
      }
      default: {
        uploadFile({ type: "text", content: info.selectionText });
      }
    }
  }
  chrome.contextMenus.onClicked.addListener(onContextMenuClick);
  const reInitContextMenus = () => {
    chrome.contextMenus.removeAll();
    for (const context of ["page", "link", "video", "audio"]) {
      chrome.contextMenus.create({
        title: I18N?.caiJiLianJie,
        contexts: [context],
        id: context,
      });
    }
    for (const context of ["image"]) {
      chrome.contextMenus.create({
        title: I18N?.caiJiTuPian,
        contexts: [context],
        id: context,
      });
    }
    for (const context of ["selection"]) {
      chrome.contextMenus.create({
        title: I18N?.caiJiWenBen,
        contexts: [context],
        id: context,
      });
    }
  };

})();
