import React from "react";
import { render, fireEvent, act } from "@testing-library/react-native";
import { Alert, Linking } from "react-native";
import SupportScreen from "../../../src/features/home/SupportScreen";

jest.mock("expo-constants", () => ({ expoConfig: { version: "1.2.3" } }));
jest.mock("expo-mail-composer", () => ({
  isAvailableAsync: jest.fn(),
  composeAsync: jest.fn(),
}));
jest.mock("../../../src/core/crashLog", () => ({ sendTestCrashReport: jest.fn() }));

import * as MailComposer from "expo-mail-composer";
import { sendTestCrashReport } from "../../../src/core/crashLog";

const isAvailable = MailComposer.isAvailableAsync as jest.Mock;
const composeAsync = MailComposer.composeAsync as jest.Mock;
const sendTestCrash = sendTestCrashReport as jest.Mock;

const baseProps = {
  appDisplayName: "MissioFlow",
  instanceUrl: "https://api.test",
  tenantId: 42 as string | number | null,
  supportEmail: "support@test.fr" as string | null,
  supportPhone: "01 23 45 67 89" as string | null,
};

// Recupere les boutons du dernier Alert.alert et invoque celui qui porte `text`.
function pressAlertButton(text: string) {
  const calls = (Alert.alert as jest.Mock).mock.calls;
  const buttons = calls[calls.length - 1][2] as { text: string; onPress?: () => void }[];
  buttons.find((b) => b.text === text)?.onPress?.();
}

describe("SupportScreen", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    jest.spyOn(Alert, "alert").mockImplementation(() => {});
    jest.spyOn(Linking, "openURL").mockResolvedValue(true as never);
    isAvailable.mockResolvedValue(true);
    composeAsync.mockResolvedValue({ status: "sent" });
    sendTestCrash.mockResolvedValue(1);
  });

  it("affiche version, instance et identifiant client", () => {
    const { getByText, getByLabelText } = render(<SupportScreen {...baseProps} />);
    expect(getByLabelText("Application MissioFlow version 1.2.3")).toBeTruthy();
    expect(getByText("https://api.test")).toBeTruthy();
    expect(getByText("42")).toBeTruthy(); // identifiant client
  });

  it("sans tenant ni contact -> pas d'identifiant, paragraphe 'administrateur web'", () => {
    const { queryByText, getByText } = render(
      <SupportScreen
        appDisplayName="MissioFlow"
        instanceUrl={null}
        tenantId={null}
        supportEmail={null}
        supportPhone={null}
      />
    );
    expect(queryByText("Identifiant client")).toBeNull();
    expect(getByText("-")).toBeTruthy(); // instance absente
    expect(getByText(/administrateur de votre instance via l'application web/)).toBeTruthy();
  });

  it("onEmail: ouvre le composeur natif quand disponible (avec diagnostic)", async () => {
    const { getByLabelText } = render(<SupportScreen {...baseProps} />);
    await act(async () => {
      fireEvent.press(getByLabelText("Ecrire au support : support@test.fr"));
    });
    expect(composeAsync).toHaveBeenCalledWith({
      recipients: ["support@test.fr"],
      subject: "Support MissioFlow",
      body: expect.stringContaining("Informations techniques"),
    });
    expect(Linking.openURL).not.toHaveBeenCalled();
  });

  it("onEmail: repli mailto quand le composeur est indisponible", async () => {
    isAvailable.mockResolvedValue(false);
    const { getByLabelText } = render(<SupportScreen {...baseProps} />);
    await act(async () => {
      fireEvent.press(getByLabelText("Ecrire au support : support@test.fr"));
    });
    expect(composeAsync).not.toHaveBeenCalled();
    expect(Linking.openURL).toHaveBeenCalledWith(expect.stringContaining("mailto:support@test.fr"));
  });

  it("onEmail: repli mailto quand isAvailableAsync throw", async () => {
    isAvailable.mockRejectedValue(new Error("native ko"));
    const { getByLabelText } = render(<SupportScreen {...baseProps} />);
    await act(async () => {
      fireEvent.press(getByLabelText("Ecrire au support : support@test.fr"));
    });
    expect(Linking.openURL).toHaveBeenCalledWith(expect.stringContaining("mailto:support@test.fr"));
  });

  it("onCall: compose tel: avec un numero nettoye", async () => {
    const { getByLabelText } = render(<SupportScreen {...baseProps} />);
    await act(async () => {
      fireEvent.press(getByLabelText("Appeler le support : 01 23 45 67 89"));
    });
    expect(Linking.openURL).toHaveBeenCalledWith("tel:0123456789");
  });

  it("openUrl en echec -> Alert 'Action impossible'", async () => {
    (Linking.openURL as jest.Mock).mockRejectedValue(new Error("no app"));
    const { getByLabelText } = render(<SupportScreen {...baseProps} />);
    await act(async () => {
      fireEvent.press(getByLabelText("Appeler le support : 01 23 45 67 89"));
    });
    expect(Alert.alert).toHaveBeenCalledWith("Action impossible", expect.any(String));
  });

  it("diagnostic (__DEV__): appui long -> section visible, envoi test count>0 -> Alert succes", async () => {
    const { getByLabelText, getByText } = render(<SupportScreen {...baseProps} />);
    // Appui long sur la version -> revele les outils de diagnostic.
    fireEvent(getByLabelText("Application MissioFlow version 1.2.3"), "longPress");
    fireEvent.press(getByLabelText("Tester la remontee de crash vers le backend"));
    // L'Alert de confirmation est affiche : on confirme l'envoi.
    await act(async () => {
      pressAlertButton("Envoyer le test");
    });
    expect(sendTestCrash).toHaveBeenCalled();
    expect(Alert.alert).toHaveBeenCalledWith("Test envoye ✅", expect.any(String));
    // Le hint de diagnostic est rendu.
    expect(getByText(/Envoie un incident de TEST/)).toBeTruthy();
  });

  it("diagnostic: envoi test count=0 -> Alert 'Conserve en local'", async () => {
    sendTestCrash.mockResolvedValue(0);
    const { getByLabelText } = render(<SupportScreen {...baseProps} />);
    fireEvent(getByLabelText("Application MissioFlow version 1.2.3"), "longPress");
    fireEvent.press(getByLabelText("Tester la remontee de crash vers le backend"));
    await act(async () => {
      pressAlertButton("Envoyer le test");
    });
    expect(Alert.alert).toHaveBeenCalledWith("Conserve en local ⏳", expect.any(String));
  });
});
