(function () {
  const state = {
    qtBridge: null,
    initPromise: null,
  };

  function safeParse(payload, fallback = {}) {
    if (!payload && payload !== "") return fallback;
    if (typeof payload === "object") return payload;
    try {
      return JSON.parse(payload);
    } catch (error) {
      return fallback;
    }
  }

  function isQtEnvironment() {
    return !!(window.qt && window.qt.webChannelTransport);
  }

  async function ensureQtBridge() {
    if (state.qtBridge) return state.qtBridge;
    if (!isQtEnvironment()) return null;
    if (state.initPromise) return state.initPromise;

    state.initPromise = new Promise((resolve) => {
      const finish = () => {
        if (!window.QWebChannel) {
          resolve(null);
          return;
        }
        new window.QWebChannel(window.qt.webChannelTransport, (channel) => {
          state.qtBridge = channel.objects.desktopBridge || null;
          window.qtBridge = state.qtBridge;
          resolve(state.qtBridge);
        });
      };

      if (window.QWebChannel) {
        finish();
        return;
      }

      const script = document.createElement("script");
      script.src = "qrc:///qtwebchannel/qwebchannel.js";
      script.onload = finish;
      script.onerror = () => resolve(null);
      document.head.appendChild(script);
    });

    return state.initPromise;
  }

  function arrayBufferToBase64(buffer) {
    let binary = "";
    const bytes = new Uint8Array(buffer);
    const chunkSize = 0x8000;
    for (let offset = 0; offset < bytes.length; offset += chunkSize) {
      const chunk = bytes.subarray(offset, offset + chunkSize);
      binary += String.fromCharCode.apply(null, chunk);
    }
    return btoa(binary);
  }

  function base64ToUint8Array(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
  }

  async function fileToBase64(blob) {
    return arrayBufferToBase64(await blob.arrayBuffer());
  }

  async function invoke(method, ...args) {
    const qtBridge = await ensureQtBridge();
    if (!qtBridge || typeof qtBridge[method] !== "function") {
      return { ok: false, error: "desktop bridge unavailable" };
    }
    const result = await qtBridge[method](...args);
    return safeParse(result, { ok: false, error: "invalid desktop bridge response" });
  }

  const desktopBridge = {
    isQt: isQtEnvironment,
    ensureQtBridge,
    toBase64: fileToBase64,
    base64ToUint8Array,
    window: {
      async minimize() {
        return invoke("windowMinimize");
      },
      async maximize() {
        return invoke("windowMaximize");
      },
      async close() {
        return invoke("windowClose");
      },
    },
    files: {
      async openProject(path = "") {
        return invoke("openProject", path);
      },
      async saveProject(payload) {
        return invoke("saveProject", JSON.stringify(payload));
      },
      async savePrintPdf(payload) {
        return invoke("savePrintPdf", JSON.stringify(payload));
      },
      async pickSpreadsheet() {
        return invoke("pickSpreadsheet");
      },
      async readSpreadsheet(path) {
        return invoke("readSpreadsheet", path || "");
      },
    },
    print: {
      async listPrinters() {
        return invoke("listPrinters");
      },
      async submitJob(payload) {
        return invoke("submitPrintJob", JSON.stringify(payload));
      },
    },
    fonts: {
      async listLocalFonts() {
        return invoke("listLocalFonts");
      },
    },
  };

  window.desktopBridge = desktopBridge;
})();
