void (async () => {
  const core = await import(chrome.runtime.getURL("content-core.js"));
  const port = chrome.runtime.connect({ name: "murtco-pdf-drag" });
  const prepared = new WeakMap();
  const transfers = new Map();
  const tokenChips = new Map();
  let transferCounter = 0;
  let nativeAvailable = true;

  function log(event, state = "", reason = "") {
    if (!nativeAvailable) {
      return;
    }
    try {
      port.postMessage({
        type: "clientLog",
        event: String(event || "").slice(0, 64),
        state: String(state || "").slice(0, 32),
        reason: String(reason || "").slice(0, 160)
      });
    } catch {
      nativeAvailable = false;
    }
  }

  function findChip(target) {
    return target?.closest?.("[data-native-drag-id]") || null;
  }

  function updateBadge(chip, visualState) {
    const presentation = core.nativeDragStatusPresentation(visualState);
    if (!presentation) {
      return;
    }
    const side = core.nativeDragBadgeSide(
      chip.getBoundingClientRect(),
      window.innerWidth
    );
    core.updateNativeDragStatusBadge(chip, presentation, side);
  }

  function setState(chip, state, detail) {
    const current = prepared.get(chip) || {
      baseTitle: chip.title || "",
      state: "idle",
      token: ""
    };
    const next = core.transitionDragSession(current, state);
    prepared.set(chip, next);
    chip.dataset.nativeDragState = state;
    chip.title = [next.baseTitle, detail].filter(Boolean).join("\n");
    updateBadge(chip, state);
    log("state_changed", state, detail);
  }

  async function readBoundedPdf(response) {
    const contentType = String(response.headers?.get?.("content-type") || "")
      .split(";", 1)[0]
      .trim()
      .toLowerCase();
    if (contentType && contentType !== "application/pdf") {
      throw new Error("Attachment response is not a PDF.");
    }

    const declaredLength = Number(response.headers?.get?.("content-length"));
    if (Number.isFinite(declaredLength) && declaredLength > core.MAX_PDF_BYTES) {
      throw new Error("Attachment exceeds the 50 MiB proof limit.");
    }

    if (!response.body?.getReader) {
      const fallback = new Uint8Array(await response.arrayBuffer());
      if (fallback.byteLength > core.MAX_PDF_BYTES) {
        throw new Error("Attachment exceeds the 50 MiB proof limit.");
      }
      return fallback;
    }

    const reader = response.body.getReader();
    const chunks = [];
    let byteLength = 0;
    try {
      while (true) {
        const { done, value } = await reader.read();
        if (done) {
          break;
        }
        const chunk = value instanceof Uint8Array ? value : new Uint8Array(value);
        byteLength += chunk.byteLength;
        if (byteLength > core.MAX_PDF_BYTES) {
          await reader.cancel();
          throw new Error("Attachment exceeds the 50 MiB proof limit.");
        }
        chunks.push(chunk);
      }
    } finally {
      reader.releaseLock?.();
    }

    const bytes = new Uint8Array(byteLength);
    let offset = 0;
    for (const chunk of chunks) {
      bytes.set(chunk, offset);
      offset += chunk.byteLength;
    }
    return bytes;
  }

  function bytesToHex(bytes) {
    return Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("");
  }

  async function prepareChip(chip) {
    const existing = prepared.get(chip);
    if (existing?.state === "preparing" || existing?.token) {
      return;
    }

    const inspected = core.inspectDragDescriptor(chip, window.location.href);
    const descriptor = inspected.descriptor;
    if (!descriptor) {
      const existingReason = prepared.get(chip)?.descriptorReason;
      if (existingReason !== inspected.reason) {
        setState(chip, "error", `Native PDF drag unavailable (${inspected.reason})`);
        prepared.get(chip).descriptorReason = inspected.reason;
        log("descriptor_rejected", "error", inspected.reason);
      }
      return;
    }

    setState(chip, "preparing", "Preparing native PDF drag");
    try {
      const response = await fetch(descriptor.url, {
        credentials: "include",
        cache: "no-store"
      });
      if (!response.ok) {
        throw new Error(`Attachment request failed (${response.status}).`);
      }
      const bytes = await readBoundedPdf(response);
      if (!core.validatePdfBytes(bytes)) {
        throw new Error("Attachment bytes do not contain a PDF signature.");
      }

      const transferId = `${Date.now().toString(36)}-${(++transferCounter).toString(36)}`;
      transfers.set(transferId, chip);
      port.postMessage({
        type: "prepareBegin",
        transferId,
        attachmentId: descriptor.attachmentId,
        filename: descriptor.filename,
        mimeType: descriptor.mimeType,
        byteLength: bytes.byteLength
      });
      for (const chunk of core.encodeTransferChunks(bytes)) {
        port.postMessage({
          type: "prepareChunk",
          transferId,
          sequence: chunk.sequence,
          base64: chunk.base64
        });
      }
      const digest = await crypto.subtle.digest("SHA-256", bytes);
      port.postMessage({
        type: "prepareEnd",
        transferId,
        sha256: bytesToHex(new Uint8Array(digest))
      });
    } catch (error) {
      const reason = String(error?.message || "prepare_failed").slice(0, 120);
      setState(chip, "error", `Native PDF drag unavailable (${reason})`);
      log("prepare_failed", "error", reason);
    }
  }

  let preloadQueued = false;
  let lastScanSignature = "";

  function preloadActiveDetailPdf() {
    preloadQueued = false;
    const chips = core.findActiveDetailDragChips(document);
    const allCandidateCount = document.querySelectorAll(
      "[data-native-drag-id][data-native-drag-url]"
    ).length;
    const signature = `${allCandidateCount}:${chips.length}`;
    if (signature !== lastScanSignature) {
      lastScanSignature = signature;
      log("preload_scan", chips.length ? "candidate_found" : "no_candidate", signature);
    }
    for (const chip of chips) {
      void prepareChip(chip);
    }
  }

  function queueActiveDetailPreload() {
    if (preloadQueued) {
      return;
    }
    preloadQueued = true;
    queueMicrotask(preloadActiveDetailPdf);
  }

  function maybePrepare(event) {
    const chip = findChip(event.target);
    if (!chip) {
      return;
    }
    const state = prepared.get(chip);
    if (event.type === "pointerdown" &&
        event.button === 0 &&
        state?.state === "ready" &&
        state.token) {
      event.preventDefault();
      event.stopImmediatePropagation();
      state.state = "prepared";
      chip.dataset.nativeDragState = "canceled";
      chip.title = [
        state.baseTitle,
        "Native pickup missed; move away and return to retry"
      ].filter(Boolean).join("\n");
      updateBadge(chip, "canceled");
      log("web_press_missed_overlay", "prepared");
      port.postMessage({
        type: "disarmOverlay",
        token: state.token
      });
      return;
    }
    if (core.canArmOverlay(state)) {
      armChip(chip);
      return;
    }
    void prepareChip(chip);
  }

  function armChip(chip) {
    const state = prepared.get(chip);
    if (!nativeAvailable ||
        !core.canArmOverlay(state)) {
      return;
    }
    state.state = "arming";
    chip.dataset.nativeDragState = "starting";
    chip.title = [state.baseTitle, "Arming Windows drag surface"].filter(Boolean).join("\n");
    updateBadge(chip, "starting");
    port.postMessage({
      type: "armOverlay",
      token: state.token
    });
  }

  port.onMessage.addListener((message) => {
    if (message?.type === "prepared" && message.transferId) {
      const chip = transfers.get(message.transferId);
      transfers.delete(message.transferId);
      if (!chip || typeof message.token !== "string" || !message.token) {
        return;
      }
      const state = prepared.get(chip);
      if (!state) {
        return;
      }
      state.state = "prepared";
      state.token = message.token;
      tokenChips.set(message.token, chip);
      chip.draggable = false;
      chip.dataset.nativeDragState = "prepared";
      chip.title = [state.baseTitle, "PDF prepared; hover to arm Windows drag"].filter(Boolean).join("\n");
      updateBadge(chip, "prepared");
      log("pdf_prepared", "prepared");
      if (chip.matches?.(":hover")) {
        armChip(chip);
      }
      return;
    }

    if (message?.type === "overlayArmed" && message.token) {
      const chip = tokenChips.get(message.token);
      const state = chip && prepared.get(chip);
      if (!chip || !state) {
        return;
      }
      state.state = "ready";
      chip.dataset.nativeDragState = "ready";
      chip.title = [state.baseTitle, "Windows drag surface armed—press and drag"].filter(Boolean).join("\n");
      updateBadge(chip, "ready");
      log("overlay_armed", "ready");
      return;
    }

    if (message?.type === "dragEntered" && message.token) {
      const chip = tokenChips.get(message.token);
      const state = chip && prepared.get(chip);
      if (!chip || !state) {
        return;
      }
      state.state = "active";
      chip.dataset.nativeDragState = "active";
      chip.title = [state.baseTitle, "Windows OLE PDF drag entered"].filter(Boolean).join("\n");
      updateBadge(chip, "active");
      log("native_drag_entered", "active");
      return;
    }

    if (message?.type === "overlayExpired" && message.token) {
      const chip = tokenChips.get(message.token);
      const state = chip && prepared.get(chip);
      if (!chip || !state) {
        return;
      }
      state.state = "prepared";
      chip.dataset.nativeDragState = "canceled";
      chip.title = [state.baseTitle, `Windows drag surface disarmed (${message.reason || "expired"}); hover again`]
        .filter(Boolean)
        .join("\n");
      updateBadge(chip, "canceled");
      log("overlay_disarmed", "prepared", message.reason || "expired");
      return;
    }

    if (message?.type === "dragCompleted" && message.token) {
      const chip = tokenChips.get(message.token);
      const state = chip && prepared.get(chip);
      if (!chip || !state) {
        return;
      }
      state.state = "prepared";
      chip.dataset.nativeDragState = "completed";
      chip.title = [
        state.baseTitle,
        `PDF file drop completed (${message.effect || "copy"}); return to drag again`
      ].filter(Boolean).join("\n");
      updateBadge(chip, "completed");
      log("state_changed", "prepared", "PDF retained for another drag");
      return;
    }

    if (message?.type === "dragCanceled" && message.token) {
      const chip = tokenChips.get(message.token);
      const state = chip && prepared.get(chip);
      if (!chip || !state) {
        return;
      }
      state.state = "prepared";
      chip.dataset.nativeDragState = "canceled";
      chip.title = [
        state.baseTitle,
        `Windows file drag canceled (${message.reason || "no target"}); return to retry`
      ].filter(Boolean).join("\n");
      updateBadge(chip, "canceled");
      log("state_changed", "prepared", "PDF retained after canceled drag");
      return;
    }

    if (message?.type === "dragFailed" && message.token) {
      const chip = tokenChips.get(message.token);
      if (!chip) {
        return;
      }
      tokenChips.delete(message.token);
      setState(chip, "error", `Windows file drag failed: ${message.message || message.code || "unknown error"}`);
      return;
    }

    if (message?.type === "nativeReset") {
      const chips = new Set([
        ...transfers.values(),
        ...tokenChips.values()
      ]);
      transfers.clear();
      tokenChips.clear();
      nativeAvailable = true;
      for (const chip of chips) {
        const current = prepared.get(chip) || {
          baseTitle: chip.title || "",
          state: "idle",
          token: ""
        };
        const next = core.transitionDragSession(current, "reset");
        prepared.set(chip, next);
        chip.dataset.nativeDragState = "reset";
        chip.title = [next.baseTitle, "Fresh native drag will prepare on next hover"]
          .filter(Boolean)
          .join("\n");
        updateBadge(chip, "reset");
      }
      return;
    }

    if (message?.type === "error") {
      const chip = transfers.get(message.transferId);
      if (chip) {
        transfers.delete(message.transferId);
        setState(chip, "error", "Native PDF drag unavailable");
      }
      const tokenChip = tokenChips.get(message.token);
      if (tokenChip) {
        tokenChips.delete(message.token);
        setState(tokenChip, "error", "Native PDF drag unavailable");
      }
    }
  });

  port.onDisconnect.addListener(() => {
    nativeAvailable = false;
    for (const chip of transfers.values()) {
      setState(chip, "error", "Native PDF drag unavailable");
    }
    transfers.clear();
    for (const chip of tokenChips.values()) {
      setState(chip, "error", "Native PDF drag unavailable");
    }
    tokenChips.clear();
  });

  function onDragStart(event) {
    const chip = findChip(event.target);
    if (!chip || !core.readDragDescriptor(chip, window.location.href)) {
      return;
    }

    event.preventDefault();
    event.stopImmediatePropagation();
  }

  document.addEventListener("pointerenter", maybePrepare, true);
  document.addEventListener("pointermove", maybePrepare, true);
  document.addEventListener("pointerdown", maybePrepare, true);
  document.addEventListener("focusin", maybePrepare, true);
  document.addEventListener("dragstart", onDragStart, true);
  log(
    "extension_started",
    "connected",
    `version=${chrome.runtime.getManifest().version}; frame=${window === window.top ? "top" : "child"}`
  );
  new MutationObserver(queueActiveDetailPreload).observe(document.documentElement, {
    childList: true,
    subtree: true,
    attributes: true,
    attributeFilter: ["hidden"]
  });
  queueActiveDetailPreload();
  window.setInterval(queueActiveDetailPreload, 1000);
  window.addEventListener("blur", () => {
    for (const [token, chip] of tokenChips) {
      const state = prepared.get(chip);
      if (["arming", "ready"].includes(state?.state)) {
        port.postMessage({ type: "disarmOverlay", token });
      }
    }
  });
  window.addEventListener("pagehide", () => {
    for (const [token, chip] of tokenChips) {
      const state = prepared.get(chip);
      if (["arming", "ready"].includes(state?.state)) {
        port.postMessage({ type: "disarmOverlay", token });
      }
    }
  });
})();
