jest.mock("../../src/services/apiClient", () => ({
  __esModule: true,
  default: { get: jest.fn() },
  buildApiUrl: jest.fn(),
  getApiAccessToken: jest.fn(),
}));

jest.mock("../../src/core/fileStorage", () => ({
  saveDevisPdfToCacheFile: jest.fn(),
}));

jest.mock("expo-sharing", () => ({
  isAvailableAsync: jest.fn(),
  shareAsync: jest.fn(),
}));

import { buildApiUrl, getApiAccessToken } from "../../src/services/apiClient";
import { saveDevisPdfToCacheFile } from "../../src/core/fileStorage";
import * as Sharing from "expo-sharing";
import { downloadDevisPdf, openDevisPdf } from "../../src/services/devisApi";

const buildUrl = buildApiUrl as jest.Mock;
const getToken = getApiAccessToken as jest.Mock;
const saveToCache = saveDevisPdfToCacheFile as jest.Mock;
const isAvailable = Sharing.isAvailableAsync as jest.Mock;
const share = Sharing.shareAsync as jest.Mock;

const fetchMock = jest.fn();
(global as unknown as { fetch: jest.Mock }).fetch = fetchMock;

function okPdfResponse(): { ok: boolean; status: number; arrayBuffer: () => Promise<ArrayBuffer> } {
  return {
    ok: true,
    status: 200,
    arrayBuffer: async () => new Uint8Array([37, 80, 68, 70]).buffer,
  };
}

describe("devisApi.downloadDevisPdf", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    buildUrl.mockResolvedValue("https://tenant.example.fr/api/devis_pdf.php?id=42");
    getToken.mockResolvedValue("JWT-TOKEN");
    saveToCache.mockReturnValue("file:///doc/devis-cache/devis_42.pdf");
  });

  it("telecharge en binaire avec le Bearer en HEADER (jamais en URL) et renvoie l'URI cache", async () => {
    fetchMock.mockResolvedValue(okPdfResponse());

    const uri = await downloadDevisPdf(42);

    expect(buildUrl).toHaveBeenCalledWith("/devis_pdf.php?id=42");
    expect(fetchMock).toHaveBeenCalledWith(
      "https://tenant.example.fr/api/devis_pdf.php?id=42",
      { headers: { Authorization: "Bearer JWT-TOKEN" } }
    );
    // Le token ne doit JAMAIS finir en query string.
    expect(buildUrl.mock.calls[0][0]).not.toContain("JWT-TOKEN");
    expect(saveToCache).toHaveBeenCalledWith(42, expect.any(Uint8Array));
    expect(uri).toBe("file:///doc/devis-cache/devis_42.pdf");
  });

  it("sans token : n'envoie pas d'en-tete Authorization", async () => {
    getToken.mockResolvedValue(null);
    fetchMock.mockResolvedValue(okPdfResponse());

    await downloadDevisPdf(42);

    expect(fetchMock).toHaveBeenCalledWith(expect.any(String), { headers: {} });
  });

  it.each([
    [403, "Ce devis n'est pas accessible (hors de votre périmètre)."],
    [404, "Devis introuvable ou fichier PDF absent."],
    [422, "Référence de devis invalide."],
    [500, "Impossible de récupérer le devis. Réessayez plus tard."],
  ])("statut %i -> message clair, pas d'ecriture cache", async (status, message) => {
    fetchMock.mockResolvedValue({ ok: false, status, arrayBuffer: jest.fn() });

    await expect(downloadDevisPdf(42)).rejects.toThrow(message);
    expect(saveToCache).not.toHaveBeenCalled();
  });

  it("id invalide -> rejette en 422 sans appel reseau", async () => {
    await expect(downloadDevisPdf(0)).rejects.toThrow("Référence de devis invalide.");
    expect(fetchMock).not.toHaveBeenCalled();
  });
});

describe("devisApi.openDevisPdf", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    buildUrl.mockResolvedValue("https://tenant.example.fr/api/devis_pdf.php?id=42");
    getToken.mockResolvedValue("JWT-TOKEN");
    saveToCache.mockReturnValue("file:///doc/devis-cache/devis_42.pdf");
    fetchMock.mockResolvedValue(okPdfResponse());
  });

  it("telecharge puis partage le PDF local en application/pdf", async () => {
    isAvailable.mockResolvedValue(true);
    share.mockResolvedValue(undefined);

    await openDevisPdf(42);

    expect(share).toHaveBeenCalledWith(
      "file:///doc/devis-cache/devis_42.pdf",
      expect.objectContaining({ mimeType: "application/pdf" })
    );
  });

  it("partage indisponible -> erreur explicite, pas de partage", async () => {
    isAvailable.mockResolvedValue(false);

    await expect(openDevisPdf(42)).rejects.toThrow(
      "Le partage de fichiers n'est pas disponible sur cet appareil."
    );
    expect(share).not.toHaveBeenCalled();
  });
});
