(function installBackendConnectivity(root) {
  const MESSAGE = "服务器正在更新维护中，请稍后使用";
  const EVENT_TYPE = "MMW_BACKEND_CONNECTIVITY";
  const STORAGE_KEY = "mmwBackendConnectivity";
  const HOST_ID = "mmw-backend-maintenance-layer";
  let publicationState;
  let publicationQueue = Promise.resolve();

  function isNoResponseError(error) {
    const name = String(error?.name || "");
    const message = String(error?.message || error || "");
    return name === "AbortError"
      || name === "TimeoutError"
      || /failed to fetch|network\s*error|network request failed|load failed|err_(?:connection|network|failed)|connection (?:refused|reset|closed)|dns|timed?\s*out|timeout|接口连接超时|连接超时|连接失败|无法连接.+接口/i.test(message);
  }

  function createUnavailableError() {
    const error = new Error(MESSAGE);
    error.name = "BackendUnavailableError";
    error.code = "BACKEND_UNAVAILABLE";
    error.backendUnavailable = true;
    return error;
  }

  function hideCard() {
    root.document?.getElementById(HOST_ID)?.remove();
  }

  function showCard() {
    if (!root.document || root.document.getElementById(HOST_ID)) return;
    const host = root.document.createElement("div");
    host.id = HOST_ID;
    host.className = "mmw-backend-maintenance-layer";
    host.innerHTML = `
      <section class="mmw-backend-maintenance-card" role="alertdialog" aria-modal="true" aria-labelledby="mmw-backend-maintenance-title">
        <button class="mmw-backend-maintenance-close" type="button" aria-label="关闭维护提示">×</button>
        <span class="mmw-backend-maintenance-icon" aria-hidden="true">
          <svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="8" x="3" y="3" rx="2"/><rect width="18" height="8" x="3" y="13" rx="2"/><path d="M7 7h.01M7 17h.01M11 7h6M11 17h6"/></svg>
        </span>
        <h2 id="mmw-backend-maintenance-title">服务器维护中</h2>
        <p>${MESSAGE}</p>
        <button class="mmw-backend-maintenance-confirm" type="button">我知道了</button>
      </section>`;
    host.querySelectorAll("button").forEach((button) => button.addEventListener("click", hideCard));
    (root.document.body || root.document.documentElement).append(host);
  }

  function broadcast(state) {
    const message = { type: EVENT_TYPE, ...state };
    root.chrome?.runtime?.sendMessage?.(message).catch(() => {});
    root.chrome?.tabs?.query?.({ active: true, lastFocusedWindow: true })
      .then((tabs) => Promise.all((tabs || []).map((tab) => (
        tab?.id ? root.chrome.tabs.sendMessage(tab.id, message).catch(() => {}) : null
      ))))
      .catch(() => {});
  }

  function publish(unavailable) {
    if (!root.chrome?.storage?.local) return Promise.resolve();
    publicationQueue = publicationQueue.then(async () => {
      if (publicationState === undefined) {
        const stored = await root.chrome.storage.local.get([STORAGE_KEY]).catch(() => ({}));
        publicationState = stored?.[STORAGE_KEY]?.unavailable === true;
      }
      if (publicationState === unavailable) return;
      publicationState = unavailable;
      const state = { unavailable, changedAt: Date.now(), message: MESSAGE };
      await root.chrome.storage.local.set({ [STORAGE_KEY]: state }).catch(() => {});
      broadcast(state);
    });
    return publicationQueue;
  }

  function installUiListener() {
    if (!root.document || !root.chrome?.runtime?.onMessage) return;
    root.chrome.runtime.onMessage.addListener((message) => {
      if (message?.type !== EVENT_TYPE) return;
      if (message.unavailable) showCard();
      else hideCard();
    });
    if (/^(chrome|edge)-extension:$/.test(root.location?.protocol || "")) {
      root.chrome.storage.local.get([STORAGE_KEY]).then((stored) => {
        if (stored?.[STORAGE_KEY]?.unavailable) showCard();
      }).catch(() => {});
    }
  }

  root.MMWBackendConnectivity = {
    MESSAGE,
    EVENT_TYPE,
    STORAGE_KEY,
    isNoResponseError,
    createUnavailableError,
    publish,
    showCard,
    hideCard
  };
  installUiListener();
})(globalThis);
