import { beforeEach, describe, expect, it, vi } from "vitest";
import type { PluginRuntime, SsrFPolicy } from "../runtime-api.js";
import { downloadMSTeamsAttachments } from "./attachments/download.js";
import { resolveRequestUrl } from "./attachments/shared.js";
import { setMSTeamsRuntime } from "./runtime.js";

const GRAPH_HOST = "graph.microsoft.com";
const _SHAREPOINT_HOST = "contoso.sharepoint.com";
const AZUREEDGE_HOST = "azureedge.net";
const TEST_HOST = "x";
const createUrlForHost = (host: string, pathSegment: string) => `https://${host}/${pathSegment}`;
const createTestUrl = (pathSegment: string) => createUrlForHost(TEST_HOST, pathSegment);
const SAVED_PNG_PATH = "/tmp/saved.png";
const SAVED_PDF_PATH = "/tmp/saved.pdf";
const TEST_URL_IMAGE = createTestUrl("img");
const _TEST_URL_IMAGE_PNG = createTestUrl("img.png");
const _TEST_URL_IMAGE_1_PNG = createTestUrl("1.png");
const _TEST_URL_IMAGE_2_JPG = createTestUrl("2.jpg");
const _TEST_URL_PDF = createTestUrl("x.pdf");
const _TEST_URL_PDF_1 = createTestUrl("1.pdf");
const _TEST_URL_PDF_2 = createTestUrl("2.pdf");
const _TEST_URL_HTML_A = createTestUrl("a.png");
const _TEST_URL_HTML_B = createTestUrl("b.png");
const TEST_URL_INLINE_IMAGE = createTestUrl("inline.png");
const TEST_URL_DOC_PDF = createTestUrl("doc.pdf");
const TEST_URL_FILE_DOWNLOAD = createTestUrl("dl");
const TEST_URL_OUTSIDE_ALLOWLIST = "https://evil.test/img";
const CONTENT_TYPE_IMAGE_PNG = "image/png";
const CONTENT_TYPE_APPLICATION_PDF = "application/pdf";
const CONTENT_TYPE_TEXT_HTML = "text/html";
const CONTENT_TYPE_TEAMS_FILE_DOWNLOAD_INFO = "application/vnd.microsoft.teams.file.download.info";
const REDIRECT_STATUS_CODES = new Set([301, 302, 303, 307, 308]);
const MAX_REDIRECT_HOPS = 5;
type RemoteMediaFetchParams = {
  url: string;
  maxBytes?: number;
  filePathHint?: string;
  ssrfPolicy?: SsrFPolicy;
  fetchImpl?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
};

const detectMimeMock = vi.fn(async () => CONTENT_TYPE_IMAGE_PNG);
const saveMediaBufferMock = vi.fn(async () => ({
  id: "saved.png",
  path: SAVED_PNG_PATH,
  size: Buffer.byteLength(PNG_BUFFER),
  contentType: CONTENT_TYPE_IMAGE_PNG,
}));
const readRemoteMediaResponse = async (
  res: Response,
  params: Pick<RemoteMediaFetchParams, "maxBytes" | "filePathHint">,
) => {
  if (!res.ok) {
    throw new Error(`HTTP ${res.status}`);
  }
  const buffer = Buffer.from(await res.arrayBuffer());
  if (typeof params.maxBytes === "number" && buffer.byteLength > params.maxBytes) {
    throw new Error(`payload exceeds maxBytes ${params.maxBytes}`);
  }
  return {
    buffer,
    contentType: res.headers.get("content-type") ?? undefined,
    fileName: params.filePathHint,
  };
};

function isHostnameAllowedByPattern(hostname: string, pattern: string): boolean {
  if (pattern.startsWith("*.")) {
    const suffix = pattern.slice(2);
    return suffix.length > 0 && hostname !== suffix && hostname.endsWith(`.${suffix}`);
  }
  return hostname === pattern;
}

function isUrlAllowedBySsrfPolicy(url: string, policy?: SsrFPolicy): boolean {
  if (!policy?.hostnameAllowlist || policy.hostnameAllowlist.length === 0) {
    return true;
  }
  const hostname = new URL(url).hostname.toLowerCase();
  return policy.hostnameAllowlist.some((pattern) =>
    isHostnameAllowedByPattern(hostname, pattern.toLowerCase()),
  );
}

async function fetchRemoteMediaWithRedirects(
  params: RemoteMediaFetchParams,
  requestInit?: RequestInit,
) {
  const fetchFn = params.fetchImpl ?? fetch;
  let currentUrl = params.url;
  for (let i = 0; i <= MAX_REDIRECT_HOPS; i += 1) {
    if (!isUrlAllowedBySsrfPolicy(currentUrl, params.ssrfPolicy)) {
      throw new Error(`Blocked hostname (not in allowlist): ${currentUrl}`);
    }
    const res = await fetchFn(currentUrl, { redirect: "manual", ...requestInit });
    if (REDIRECT_STATUS_CODES.has(res.status)) {
      const location = res.headers.get("location");
      if (!location) {
        throw new Error("redirect missing location");
      }
      currentUrl = new URL(location, currentUrl).toString();
      continue;
    }
    return readRemoteMediaResponse(res, params);
  }
  throw new Error("too many redirects");
}

const fetchRemoteMediaMock = vi.fn(async (params: RemoteMediaFetchParams) => {
  return await fetchRemoteMediaWithRedirects(params);
});

const runtimeStub = {
  media: {
    detectMime: detectMimeMock,
  },
  channel: {
    media: {
      fetchRemoteMedia: fetchRemoteMediaMock,
      saveMediaBuffer: saveMediaBufferMock,
    },
  },
} as unknown as PluginRuntime;

type DownloadAttachmentsParams = Parameters<typeof downloadMSTeamsAttachments>[0];
type DownloadedMedia = Awaited<ReturnType<typeof downloadMSTeamsAttachments>>;
type DownloadAttachmentsBuildOverrides = Partial<
  Omit<DownloadAttachmentsParams, "attachments" | "maxBytes" | "allowHosts">
> &
  Pick<DownloadAttachmentsParams, "allowHosts">;
type DownloadAttachmentsNoFetchOverrides = Partial<
  Omit<DownloadAttachmentsParams, "attachments" | "maxBytes" | "allowHosts" | "fetchFn">
> &
  Pick<DownloadAttachmentsParams, "allowHosts">;
type FetchFn = typeof fetch;
type MSTeamsAttachments = DownloadAttachmentsParams["attachments"];
type LabeledCase = { label: string };
type FetchCallExpectation = { expectFetchCalled?: boolean };
type DownloadedMediaExpectation = { path?: string; placeholder?: string };

const DEFAULT_MAX_BYTES = 1024 * 1024;
const DEFAULT_ALLOW_HOSTS = [TEST_HOST];
const MEDIA_PLACEHOLDER_IMAGE = "<media:image>";
const MEDIA_PLACEHOLDER_DOCUMENT = "<media:document>";
const _formatImagePlaceholder = (count: number) =>
  count > 1 ? `${MEDIA_PLACEHOLDER_IMAGE} (${count} images)` : MEDIA_PLACEHOLDER_IMAGE;
const formatDocumentPlaceholder = (count: number) =>
  count > 1 ? `${MEDIA_PLACEHOLDER_DOCUMENT} (${count} files)` : MEDIA_PLACEHOLDER_DOCUMENT;
const IMAGE_ATTACHMENT = { contentType: CONTENT_TYPE_IMAGE_PNG, contentUrl: TEST_URL_IMAGE };
const PNG_BUFFER = Buffer.from("png");
const PNG_BASE64 = PNG_BUFFER.toString("base64");
const PDF_BUFFER = Buffer.from("pdf");
const createTokenProvider = (
  tokenOrResolver: string | ((scope: string) => string | Promise<string>) = "token",
) => ({
  getAccessToken: vi.fn(async (scope: string) =>
    typeof tokenOrResolver === "function" ? await tokenOrResolver(scope) : tokenOrResolver,
  ),
});
const asSingleItemArray = <T>(value: T) => [value];
const withLabel = <T extends object>(label: string, fields: T): T & LabeledCase => ({
  label,
  ...fields,
});
const buildAttachment = <T extends Record<string, unknown>>(contentType: string, props: T) => ({
  contentType,
  ...props,
});
const createHtmlAttachment = (content: string) =>
  buildAttachment(CONTENT_TYPE_TEXT_HTML, { content });
const buildHtmlImageTag = (src: string) => `<img src="${src}" />`;
const createHtmlImageAttachments = (sources: string[], prefix = "") =>
  asSingleItemArray(createHtmlAttachment(`${prefix}${sources.map(buildHtmlImageTag).join("")}`));
const createContentUrlAttachments = (contentType: string, ...contentUrls: string[]) =>
  contentUrls.map((contentUrl) => buildAttachment(contentType, { contentUrl }));
const createImageAttachments = (...contentUrls: string[]) =>
  createContentUrlAttachments(CONTENT_TYPE_IMAGE_PNG, ...contentUrls);
const createPdfAttachments = (...contentUrls: string[]) =>
  createContentUrlAttachments(CONTENT_TYPE_APPLICATION_PDF, ...contentUrls);
const createTeamsFileDownloadInfoAttachments = (
  downloadUrl = TEST_URL_FILE_DOWNLOAD,
  fileType = "png",
) =>
  asSingleItemArray(
    buildAttachment(CONTENT_TYPE_TEAMS_FILE_DOWNLOAD_INFO, {
      content: { downloadUrl, fileType },
    }),
  );
const createHostedContentsWithType = (contentType: string, ...ids: string[]) =>
  ids.map((id) => ({ id, contentType, contentBytes: PNG_BASE64 }));
const _createHostedImageContents = (...ids: string[]) =>
  createHostedContentsWithType(CONTENT_TYPE_IMAGE_PNG, ...ids);
type BinaryPayload = Uint8Array | string;
const _createPdfResponse = (payload: BinaryPayload = PDF_BUFFER) => {
  return createBufferResponse(payload, CONTENT_TYPE_APPLICATION_PDF);
};
const createBufferResponse = (payload: BinaryPayload, contentType: string, status = 200) => {
  const raw = typeof payload === "string" ? Buffer.from(payload) : payload;
  return new Response(new Uint8Array(raw), {
    status,
    headers: { "content-type": contentType },
  });
};
const createJsonResponse = (payload: unknown, status = 200) =>
  new Response(JSON.stringify(payload), { status });
const createTextResponse = (body: string, status = 200) => new Response(body, { status });
const _createGraphCollectionResponse = (value: unknown[]) => createJsonResponse({ value });
const createNotFoundResponse = () => new Response("not found", { status: 404 });
const createRedirectResponse = (location: string, status = 302) =>
  new Response(null, { status, headers: { location } });
const publicResolve = async () => ({ address: "13.107.136.10" });

const createOkFetchMock = (contentType: string, payload = "png") =>
  vi.fn(async (_input: RequestInfo | URL, _init?: RequestInit) =>
    createBufferResponse(payload, contentType),
  );
const asFetchFn = (fetchFn: unknown): FetchFn => fetchFn as FetchFn;

const buildDownloadParams = (
  attachments: MSTeamsAttachments,
  overrides: DownloadAttachmentsBuildOverrides = {},
): DownloadAttachmentsParams => {
  return {
    attachments,
    maxBytes: DEFAULT_MAX_BYTES,
    allowHosts: DEFAULT_ALLOW_HOSTS,
    resolveFn: publicResolve,
    ...overrides,
  };
};

const downloadAttachmentsWithFetch = async (
  attachments: MSTeamsAttachments,
  fetchFn: unknown,
  overrides: DownloadAttachmentsNoFetchOverrides = {},
  options: FetchCallExpectation = {},
) => {
  const media = await downloadMSTeamsAttachments(
    buildDownloadParams(attachments, {
      ...overrides,
      fetchFn: asFetchFn(fetchFn),
    }),
  );
  expectMockCallState(fetchFn, options.expectFetchCalled ?? true);
  return media;
};

const createAuthAwareImageFetchMock = (params: { unauthStatus: number; unauthBody: string }) =>
  vi.fn(async (_url: string, opts?: RequestInit) => {
    const headers = new Headers(opts?.headers);
    const hasAuth = Boolean(headers.get("Authorization"));
    if (!hasAuth) {
      return createTextResponse(params.unauthBody, params.unauthStatus);
    }
    return createBufferResponse(PNG_BUFFER, CONTENT_TYPE_IMAGE_PNG);
  });
const expectMockCallState = (mockFn: unknown, shouldCall: boolean) => {
  if (shouldCall) {
    expect(mockFn).toHaveBeenCalled();
  } else {
    expect(mockFn).not.toHaveBeenCalled();
  }
};

const expectAttachmentMediaLength = (media: DownloadedMedia, expectedLength: number) => {
  expect(media).toHaveLength(expectedLength);
};
const expectSingleMedia = (media: DownloadedMedia, expected: DownloadedMediaExpectation = {}) => {
  expectAttachmentMediaLength(media, 1);
  expectFirstMedia(media, expected);
};
const expectMediaBufferSaved = () => {
  expect(saveMediaBufferMock).toHaveBeenCalled();
};
const expectFirstMedia = (media: DownloadedMedia, expected: DownloadedMediaExpectation) => {
  const first = media[0];
  if (expected.path !== undefined) {
    expect(first?.path).toBe(expected.path);
  }
  if (expected.placeholder !== undefined) {
    expect(first?.placeholder).toBe(expected.placeholder);
  }
};
type AttachmentDownloadSuccessCase = LabeledCase & {
  attachments: MSTeamsAttachments;
  buildFetchFn?: () => unknown;
  beforeDownload?: () => void;
  assert?: (media: DownloadedMedia) => void;
};
type AttachmentAuthRetryScenario = {
  attachmentUrl: string;
  unauthStatus: number;
  unauthBody: string;
  overrides?: Omit<DownloadAttachmentsNoFetchOverrides, "tokenProvider">;
};
type AttachmentAuthRetryCase = LabeledCase & {
  scenario: AttachmentAuthRetryScenario;
  expectedMediaLength: number;
  expectTokenFetch: boolean;
};
const ATTACHMENT_DOWNLOAD_SUCCESS_CASES: AttachmentDownloadSuccessCase[] = [
  withLabel("downloads and stores image contentUrl attachments", {
    attachments: asSingleItemArray(IMAGE_ATTACHMENT),
    assert: (media) => {
      expectFirstMedia(media, { path: SAVED_PNG_PATH });
      expectMediaBufferSaved();
    },
  }),
  withLabel("supports Teams file.download.info downloadUrl attachments", {
    attachments: createTeamsFileDownloadInfoAttachments(),
  }),
  withLabel("downloads inline image URLs from html attachments", {
    attachments: createHtmlImageAttachments([TEST_URL_INLINE_IMAGE]),
  }),
  withLabel("downloads non-image file attachments (PDF)", {
    attachments: createPdfAttachments(TEST_URL_DOC_PDF),
    buildFetchFn: () => createOkFetchMock(CONTENT_TYPE_APPLICATION_PDF, "pdf"),
    beforeDownload: () => {
      detectMimeMock.mockResolvedValueOnce(CONTENT_TYPE_APPLICATION_PDF);
      saveMediaBufferMock.mockResolvedValueOnce({
        id: "saved.pdf",
        path: SAVED_PDF_PATH,
        size: Buffer.byteLength(PDF_BUFFER),
        contentType: CONTENT_TYPE_APPLICATION_PDF,
      });
    },
    assert: (media) => {
      expectSingleMedia(media, {
        path: SAVED_PDF_PATH,
        placeholder: formatDocumentPlaceholder(1),
      });
    },
  }),
];
const ATTACHMENT_AUTH_RETRY_CASES: AttachmentAuthRetryCase[] = [
  withLabel("retries with auth when the first request is unauthorized", {
    scenario: {
      attachmentUrl: IMAGE_ATTACHMENT.contentUrl,
      unauthStatus: 401,
      unauthBody: "unauthorized",
      overrides: { authAllowHosts: [TEST_HOST] },
    },
    expectedMediaLength: 1,
    expectTokenFetch: true,
  }),
  withLabel("skips auth retries when the host is not in auth allowlist", {
    scenario: {
      attachmentUrl: createUrlForHost(AZUREEDGE_HOST, "img"),
      unauthStatus: 403,
      unauthBody: "forbidden",
      overrides: {
        allowHosts: [AZUREEDGE_HOST],
        authAllowHosts: [GRAPH_HOST],
      },
    },
    expectedMediaLength: 0,
    expectTokenFetch: false,
  }),
];
const runAttachmentDownloadSuccessCase = async ({
  attachments,
  buildFetchFn,
  beforeDownload,
  assert,
}: AttachmentDownloadSuccessCase) => {
  const fetchFn = (buildFetchFn ?? (() => createOkFetchMock(CONTENT_TYPE_IMAGE_PNG)))();
  beforeDownload?.();
  const media = await downloadAttachmentsWithFetch(attachments, fetchFn);
  expectSingleMedia(media);
  assert?.(media);
};
const runAttachmentAuthRetryCase = async ({
  scenario,
  expectedMediaLength,
  expectTokenFetch,
}: AttachmentAuthRetryCase) => {
  const tokenProvider = createTokenProvider();
  const fetchMock = createAuthAwareImageFetchMock({
    unauthStatus: scenario.unauthStatus,
    unauthBody: scenario.unauthBody,
  });
  const media = await downloadAttachmentsWithFetch(
    createImageAttachments(scenario.attachmentUrl),
    fetchMock,
    { tokenProvider, ...scenario.overrides },
  );
  expectAttachmentMediaLength(media, expectedMediaLength);
  expectMockCallState(tokenProvider.getAccessToken, expectTokenFetch);
};

describe("msteams attachments", () => {
  beforeEach(() => {
    detectMimeMock.mockClear();
    saveMediaBufferMock.mockClear();
    fetchRemoteMediaMock.mockClear();
    setMSTeamsRuntime(runtimeStub);
  });

  describe("downloadMSTeamsAttachments", () => {
    it.each<AttachmentDownloadSuccessCase>(ATTACHMENT_DOWNLOAD_SUCCESS_CASES)(
      "$label",
      runAttachmentDownloadSuccessCase,
    );

    it("stores inline data:image base64 payloads", async () => {
      const media = await downloadMSTeamsAttachments(
        buildDownloadParams([
          ...createHtmlImageAttachments([`data:image/png;base64,${PNG_BASE64}`]),
        ]),
      );

      expectSingleMedia(media);
      expectMediaBufferSaved();
    });

    it.each<AttachmentAuthRetryCase>(ATTACHMENT_AUTH_RETRY_CASES)(
      "$label",
      runAttachmentAuthRetryCase,
    );

    it("preserves auth fallback when dispatcher-mode fetch returns a redirect", async () => {
      const redirectedUrl = createTestUrl("redirected.png");
      const tokenProvider = createTokenProvider();
      const fetchMock = vi.fn(async (url: string, opts?: RequestInit) => {
        const hasAuth = Boolean(new Headers(opts?.headers).get("Authorization"));
        if (url === TEST_URL_IMAGE) {
          return hasAuth
            ? createRedirectResponse(redirectedUrl)
            : createTextResponse("unauthorized", 401);
        }
        if (url === redirectedUrl) {
          return createBufferResponse(PNG_BUFFER, CONTENT_TYPE_IMAGE_PNG);
        }
        return createNotFoundResponse();
      });

      fetchRemoteMediaMock.mockImplementationOnce(async (params) => {
        return await fetchRemoteMediaWithRedirects(params, {
          dispatcher: {},
        } as RequestInit);
      });

      const media = await downloadAttachmentsWithFetch(
        createImageAttachments(TEST_URL_IMAGE),
        fetchMock,
        { tokenProvider, authAllowHosts: [TEST_HOST] },
      );

      expectAttachmentMediaLength(media, 1);
      expect(tokenProvider.getAccessToken).toHaveBeenCalledOnce();
      expect(fetchMock.mock.calls.map(([calledUrl]) => calledUrl)).toContain(redirectedUrl);
    });

    it("continues scope fallback after non-auth failure and succeeds on later scope", async () => {
      let authAttempt = 0;
      const tokenProvider = createTokenProvider((scope) => `token:${scope}`);
      const fetchMock = vi.fn(async (_url: string, opts?: RequestInit) => {
        const auth = new Headers(opts?.headers).get("Authorization");
        if (!auth) {
          return createTextResponse("unauthorized", 401);
        }
        authAttempt += 1;
        if (authAttempt === 1) {
          return createTextResponse("upstream transient", 500);
        }
        return createBufferResponse(PNG_BUFFER, CONTENT_TYPE_IMAGE_PNG);
      });

      const media = await downloadAttachmentsWithFetch(
        createImageAttachments(TEST_URL_IMAGE),
        fetchMock,
        { tokenProvider, authAllowHosts: [TEST_HOST] },
      );

      expectAttachmentMediaLength(media, 1);
      expect(tokenProvider.getAccessToken).toHaveBeenCalledTimes(2);
    });

    it("does not forward Authorization to redirects outside auth allowlist", async () => {
      const tokenProvider = createTokenProvider("top-secret-token");
      const graphFileUrl = createUrlForHost(GRAPH_HOST, "file");
      const seen: Array<{ url: string; auth: string }> = [];
      const fetchMock = vi.fn(async (url: string, opts?: RequestInit) => {
        const auth = new Headers(opts?.headers).get("Authorization") ?? "";
        seen.push({ url, auth });
        if (url === graphFileUrl && !auth) {
          return new Response("unauthorized", { status: 401 });
        }
        if (url === graphFileUrl && auth) {
          return new Response("", {
            status: 302,
            headers: { location: "https://attacker.azureedge.net/collect" },
          });
        }
        if (url === "https://attacker.azureedge.net/collect") {
          return new Response(Buffer.from("png"), {
            status: 200,
            headers: { "content-type": CONTENT_TYPE_IMAGE_PNG },
          });
        }
        return createNotFoundResponse();
      });

      const media = await downloadMSTeamsAttachments(
        buildDownloadParams([{ contentType: CONTENT_TYPE_IMAGE_PNG, contentUrl: graphFileUrl }], {
          tokenProvider,
          allowHosts: [GRAPH_HOST, AZUREEDGE_HOST],
          authAllowHosts: [GRAPH_HOST],
          fetchFn: asFetchFn(fetchMock),
        }),
      );

      expectSingleMedia(media);
      const redirected = seen.find(
        (entry) => entry.url === "https://attacker.azureedge.net/collect",
      );
      expect(redirected).toBeDefined();
      expect(redirected?.auth).toBe("");
    });

    it("skips urls outside the allowlist", async () => {
      const fetchMock = vi.fn();
      const media = await downloadAttachmentsWithFetch(
        createImageAttachments(TEST_URL_OUTSIDE_ALLOWLIST),
        fetchMock,
        {
          allowHosts: [GRAPH_HOST],
        },
        { expectFetchCalled: false },
      );

      expectAttachmentMediaLength(media, 0);
    });

    it("blocks redirects to non-https URLs", async () => {
      const insecureUrl = "http://x/insecure.png";
      const fetchMock = vi.fn(async (input: RequestInfo | URL) => {
        const url = resolveRequestUrl(input);
        if (url === TEST_URL_IMAGE) {
          return createRedirectResponse(insecureUrl);
        }
        if (url === insecureUrl) {
          return createBufferResponse("insecure", CONTENT_TYPE_IMAGE_PNG);
        }
        return createNotFoundResponse();
      });

      const media = await downloadAttachmentsWithFetch(
        createImageAttachments(TEST_URL_IMAGE),
        fetchMock,
        {
          allowHosts: [TEST_HOST],
        },
      );

      expectAttachmentMediaLength(media, 0);
      expect(fetchMock).toHaveBeenCalledTimes(1);
    });

    describe("OneDrive/SharePoint shared links", () => {
      const GRAPH_SHARES_URL_PREFIX = `https://${GRAPH_HOST}/v1.0/shares/`;
      const DEFAULT_GRAPH_ALLOW_HOSTS = [GRAPH_HOST];
      const PDF_PAYLOAD = Buffer.from("pdf-bytes");

      const createGraphSharesFetchMock = () =>
        vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
          const url = resolveRequestUrl(input);
          const auth = new Headers(init?.headers).get("Authorization");
          if (url.startsWith(GRAPH_SHARES_URL_PREFIX)) {
            if (!auth) {
              return createTextResponse("unauthorized", 401);
            }
            return createBufferResponse(PDF_PAYLOAD, CONTENT_TYPE_APPLICATION_PDF);
          }
          return createNotFoundResponse();
        });

      it.each([
        {
          label: "SharePoint URL",
          contentUrl: "https://contoso.sharepoint.com/personal/user/Documents/report.pdf",
        },
        {
          label: "OneDrive 1drv.ms URL",
          contentUrl: "https://1drv.ms/b/s!AkxYabcdefg",
        },
        {
          label: "OneDrive onedrive.live.com URL",
          contentUrl: "https://onedrive.live.com/share/file",
        },
      ])("routes $label through Graph shares endpoint", async ({ contentUrl }) => {
        const tokenProvider = createTokenProvider();
        const fetchMock = createGraphSharesFetchMock();
        detectMimeMock.mockResolvedValueOnce(CONTENT_TYPE_APPLICATION_PDF);
        saveMediaBufferMock.mockResolvedValueOnce({
          id: "saved.pdf",
          path: SAVED_PDF_PATH,
          size: Buffer.byteLength(PDF_PAYLOAD),
          contentType: CONTENT_TYPE_APPLICATION_PDF,
        });

        const media = await downloadMSTeamsAttachments(
          buildDownloadParams(
            [
              {
                contentType: "reference",
                contentUrl,
                name: "report.pdf",
              },
            ],
            {
              tokenProvider,
              allowHosts: DEFAULT_GRAPH_ALLOW_HOSTS,
              authAllowHosts: DEFAULT_GRAPH_ALLOW_HOSTS,
              fetchFn: asFetchFn(fetchMock),
            },
          ),
        );

        expectAttachmentMediaLength(media, 1);
        expect(media[0]?.path).toBe(SAVED_PDF_PATH);
        // The only host that should be fetched is graph.microsoft.com.
        const calledUrls = (fetchMock.mock.calls as Array<[RequestInfo | URL, RequestInit?]>).map(
          ([input]) => resolveRequestUrl(input),
        );
        expect(calledUrls.length).toBeGreaterThan(0);
        for (const url of calledUrls) {
          expect(url.startsWith(GRAPH_SHARES_URL_PREFIX)).toBe(true);
        }
        // Graph scope token was acquired for the shares fetch.
        expect(tokenProvider.getAccessToken).toHaveBeenCalled();
      });

      it("falls through to direct fetch for non-shared-link URLs", async () => {
        const directUrl = createTestUrl("direct.pdf");
        const fetchMock = createOkFetchMock(CONTENT_TYPE_APPLICATION_PDF, "pdf");
        detectMimeMock.mockResolvedValueOnce(CONTENT_TYPE_APPLICATION_PDF);
        saveMediaBufferMock.mockResolvedValueOnce({
          id: "saved.pdf",
          path: SAVED_PDF_PATH,
          size: Buffer.byteLength(PDF_BUFFER),
          contentType: CONTENT_TYPE_APPLICATION_PDF,
        });

        const media = await downloadAttachmentsWithFetch(
          createPdfAttachments(directUrl),
          fetchMock,
        );

        expectAttachmentMediaLength(media, 1);
        const calledUrls = (fetchMock.mock.calls as unknown[]).map((call) => {
          const input = (call as [RequestInfo | URL])[0];
          return resolveRequestUrl(input);
        });
        // Should have hit the original host, NOT graph shares.
        expect(calledUrls.some((url) => url === directUrl)).toBe(true);
        expect(calledUrls.some((url) => url.startsWith(GRAPH_SHARES_URL_PREFIX))).toBe(false);
      });
    });

    describe("error logging (issue #63396)", () => {
      // Before this fix, fetch failures were swallowed by empty `catch {}`
      // blocks, leaving operators with no signal that SharePoint downloads
      // were silently failing on Node 24+. These tests pin the logger contract
      // so the regression cannot return.
      it("invokes logger.warn when a remote media download fails", async () => {
        const logger = { warn: vi.fn(), error: vi.fn() };
        const fetchMock = vi.fn(async () => createTextResponse("server error", 500));

        const media = await downloadMSTeamsAttachments(
          buildDownloadParams(createImageAttachments(TEST_URL_IMAGE), {
            fetchFn: asFetchFn(fetchMock),
            logger,
          }),
        );

        expectAttachmentMediaLength(media, 0);
        expect(logger.warn).toHaveBeenCalledWith(
          "msteams attachment download failed",
          expect.objectContaining({
            error: expect.stringContaining("HTTP 500"),
            host: expect.any(String),
          }),
        );
      });

      it("does not log when downloads succeed", async () => {
        const logger = { warn: vi.fn(), error: vi.fn() };
        const fetchMock = createOkFetchMock(CONTENT_TYPE_IMAGE_PNG);

        const media = await downloadMSTeamsAttachments(
          buildDownloadParams(createImageAttachments(TEST_URL_IMAGE), {
            fetchFn: asFetchFn(fetchMock),
            logger,
          }),
        );

        expectAttachmentMediaLength(media, 1);
        expect(logger.warn).not.toHaveBeenCalled();
        expect(logger.error).not.toHaveBeenCalled();
      });
    });
  });
});
