"""Regression tests for Hermes' Spectrum mixed text+attachment workaround."""
from __future__ import annotations

import subprocess
import textwrap
from pathlib import Path


_PATCHER = Path("plugins/platforms/photon/sidecar/patch-spectrum-mixed-attachments.mjs")


def test_sidecar_applies_spectrum_patch_before_importing_sdk() -> None:
    """Existing installs should self-heal at runtime, not only during npm postinstall."""
    index = Path("plugins/platforms/photon/sidecar/index.mjs").read_text(encoding="utf-8")
    assert "import { patchSpectrumTs }" in index
    assert "patchSpectrumTs();" in index
    assert index.index("patchSpectrumTs();") < index.index('await import("spectrum-ts")')


def test_spectrum_patch_preserves_text_when_single_attachment(tmp_path: Path) -> None:
    """The sidecar dependency patch must turn text+one attachment into group content."""
    dist = tmp_path / "node_modules" / "spectrum-ts" / "dist"
    dist.mkdir(parents=True)
    chunk = dist / "chunk-test.js"
    chunk.write_text(
        textwrap.dedent(
            """
            var rebuildFromAppleMessage = async (client, message, phone, chatGuidHint) => {
              const messageGuidStr = message.guid;
              const timestamp = message.dateCreated ?? /* @__PURE__ */ new Date();
              const base = buildMessageBase(message, chatGuidHint, timestamp, phone);
              const attachments = messageAttachments(message);
              if (attachments.length === 1) {
                const info = attachments[0];
                if (!info) {
                  throw new Error("Unreachable: attachments.length === 1 but no element");
                }
                return buildAttachmentMessage(client, base, info, messageGuidStr, 0);
              }
              if (attachments.length > 1) {
                const items = [];
                for (let i = 0; i < attachments.length; i++) {
                  const info = attachments[i];
                  if (!info) {
                    continue;
                  }
                  items.push(
                    await buildAttachmentMessage(
                      client,
                      base,
                      info,
                      formatChildId(i, messageGuidStr),
                      i,
                      messageGuidStr
                    )
                  );
                }
                return {
                  ...base,
                  id: messageGuidStr,
                  content: asProviderGroup(items)
                };
              }
              if (getBalloonBundleId(message) === URL_BALLOON_BUNDLE_ID) {
                return toRichlinkMessage(message, base, messageGuidStr);
              }
              const text2 = message.content.text;
              return {
                ...base,
                id: messageGuidStr,
                content: text2 ? asText(text2) : asCustom(message)
              };
            };
            var toInboundMessages = async (client, cache, event, phone) => {
              const base = buildMessageBase(
                event.message,
                event.chatGuid,
                event.occurredAt,
                phone
              );
              const messageGuidStr = event.message.guid;
              if (getBalloonBundleId(event.message) === URL_BALLOON_BUNDLE_ID) {
                const msg2 = toRichlinkMessage(event.message, base, messageGuidStr);
                cacheMessage(cache, msg2);
                return [msg2];
              }
              const attachments = messageAttachments(event.message);
              if (attachments.length === 1) {
                const info = attachments[0];
                if (!info) {
                  throw new Error("Unreachable: attachments.length === 1 but no element");
                }
                const msg2 = await buildAttachmentMessage(
                  client,
                  base,
                  info,
                  messageGuidStr,
                  0
                );
                cacheMessage(cache, msg2);
                return [msg2];
              }
              if (attachments.length > 1) {
                const items = [];
                for (let i = 0; i < attachments.length; i++) {
                  const info = attachments[i];
                  if (!info) {
                    continue;
                  }
                  items.push(
                    await buildAttachmentMessage(
                      client,
                      base,
                      info,
                      formatChildId(i, messageGuidStr),
                      i,
                      messageGuidStr
                    )
                  );
                }
                const parent = {
                  ...base,
                  id: messageGuidStr,
                  content: asProviderGroup(items)
                };
                cacheMessage(cache, parent);
                return [parent];
              }
              const text2 = event.message.content.text;
              const msg = {
                ...base,
                id: messageGuidStr,
                content: text2 ? asText(text2) : asCustom(event.message)
              };
              cacheMessage(cache, msg);
              return [msg];
            };
            """
        ),
        encoding="utf-8",
    )

    result = subprocess.run(
        ["node", str(_PATCHER), str(tmp_path)],
        cwd=Path.cwd(),
        text=True,
        capture_output=True,
        check=False,
    )

    assert result.returncode == 0, result.stderr
    patched = chunk.read_text(encoding="utf-8")
    assert "Preserve mixed text + attachment iMessage payloads" in patched
    assert "content: asProviderGroup([textMsg, msg2])" in patched
    assert "content: asProviderGroup(items)" in patched
    assert "formatChildId(text2 ? i + 1 : i, messageGuidStr)" in patched
