import React from "react";
import { render, fireEvent, waitFor, act } from "@testing-library/react-native";
import { Alert } from "react-native";
import BottlesScreen from "../../../src/features/home/BottlesScreen";
import type { MobileBottle } from "../../../src/types/bottle";

jest.mock("../../../src/services/bottlesApi", () => ({
  getMyBottles: jest.fn(),
  postBottleAction: jest.fn(),
}));

import { getMyBottles, postBottleAction } from "../../../src/services/bottlesApi";

const getBottles = getMyBottles as jest.Mock;
const postAction = postBottleAction as jest.Mock;

const makeBottle = (over: Partial<MobileBottle> = {}): MobileBottle => ({
  id: 1,
  code_interne: "B-001",
  type: "gaz",
  statut: "en_service",
  ...over,
});

const ready = (bottles: MobileBottle[], count = bottles.length, quota = 0) => ({
  available: true,
  payload: { bottles, count, quota, technicien_id: 1 },
});

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("BottlesScreen", () => {
  beforeEach(() => {
    jest.clearAllMocks();
    jest.spyOn(Alert, "alert").mockImplementation(() => {});
    getBottles.mockResolvedValue(ready([makeBottle()]));
    postAction.mockResolvedValue(undefined);
  });

  it("charge et affiche la liste (resume + carte)", async () => {
    getBottles.mockResolvedValue(ready([makeBottle({ code_interne: "B-XYZ" })], 1, 5));
    const { findByText, getByText } = render(<BottlesScreen brandColor="#1E56A8" />);
    expect(await findByText("B-XYZ")).toBeTruthy();
    expect(getByText(/1 bouteille attribuee \/ 5 max/)).toBeTruthy(); // singulier + quota
    expect(getByText("Gaz frigorigene")).toBeTruthy();
  });

  it("pluriel du resume pour plusieurs bouteilles", async () => {
    getBottles.mockResolvedValue(ready([makeBottle({ id: 1 }), makeBottle({ id: 2 })], 2));
    const { findByText } = render(<BottlesScreen brandColor="#1E56A8" />);
    expect(await findByText(/2 bouteilles attribuees/)).toBeTruthy();
  });

  it("available=false -> notice 'Bientot disponible'", async () => {
    getBottles.mockResolvedValue({ available: false });
    const { findByText } = render(<BottlesScreen brandColor="#1E56A8" />);
    expect(await findByText("Bientot disponible")).toBeTruthy();
  });

  it("erreur de chargement -> message + Reessayer relance load", async () => {
    getBottles.mockRejectedValueOnce(new Error("boom")).mockResolvedValue(ready([makeBottle()]));
    const { findByText, getByText } = render(<BottlesScreen brandColor="#1E56A8" />);
    expect(await findByText("boom")).toBeTruthy();
    await act(async () => {
      fireEvent.press(getByText("Reessayer"));
    });
    expect(await findByText("B-001")).toBeTruthy();
    expect(getBottles).toHaveBeenCalledTimes(2);
  });

  it("liste vide -> message dedie", async () => {
    getBottles.mockResolvedValue(ready([], 0));
    const { findByText } = render(<BottlesScreen brandColor="#1E56A8" />);
    expect(await findByText("Aucune bouteille active ne vous est attribuee.")).toBeTruthy();
  });

  it("carte: statut et type inconnus -> fallback, date_commande affichee", async () => {
    getBottles.mockResolvedValue(
      ready([
        makeBottle({
          statut: "zzz_inconnu",
          type: "exotique",
          code_interne: "",
          date_commande: "01/06/2026",
        }),
      ])
    );
    const { findByText, getByText } = render(<BottlesScreen brandColor="#1E56A8" />);
    // code_interne vide -> repli #id
    expect(await findByText("#1")).toBeTruthy();
    expect(getByText("zzz_inconnu")).toBeTruthy(); // statutStyle fallback label
    expect(getByText("exotique")).toBeTruthy(); // type brut
    expect(getByText("Commandee le 01/06/2026")).toBeTruthy();
  });

  it("action simple (request_return): confirmation -> postBottleAction + reload + message", async () => {
    const { findByText, getByText } = render(<BottlesScreen brandColor="#1E56A8" />);
    fireEvent.press(await findByText("Demander le retour"));
    expect(Alert.alert).toHaveBeenCalled();
    await act(async () => {
      pressAlertButton("Confirmer");
    });
    expect(postAction).toHaveBeenCalledWith("request_return", 1);
    expect(await findByText("Action effectuee.")).toBeTruthy();
    expect(getBottles).toHaveBeenCalledTimes(2); // load initial + reload
  });

  it("action simple en echec -> Alert 'Action impossible'", async () => {
    postAction.mockRejectedValue(new Error("transition refusee"));
    const { findByText } = render(<BottlesScreen brandColor="#1E56A8" />);
    fireEvent.press(await findByText("Demander le retour"));
    await act(async () => {
      pressAlertButton("Confirmer");
    });
    expect(Alert.alert).toHaveBeenLastCalledWith("Action impossible", "transition refusee");
  });

  it("echange: validations (serie puis type) puis succes", async () => {
    getBottles.mockResolvedValue(ready([makeBottle({ statut: "retour_demande" })]));
    const { findByText, getByText, getByPlaceholderText, queryByText } = render(
      <BottlesScreen brandColor="#1E56A8" />
    );
    // Ouvre la modale d'echange.
    fireEvent.press(await findByText("Echanger chez le fournisseur"));

    // Soumission sans numero de serie -> erreur.
    await act(async () => {
      fireEvent.press(getByText("Valider l'echange"));
    });
    expect(getByText("Le numero de serie de la nouvelle bouteille est requis.")).toBeTruthy();
    expect(postAction).not.toHaveBeenCalled();

    // Avec serie mais sans type -> erreur.
    fireEvent.changeText(getByPlaceholderText("Ex: FR-2024-00123"), "FR-NEW-9");
    await act(async () => {
      fireEvent.press(getByText("Valider l'echange"));
    });
    expect(getByText("Choisissez le type de la nouvelle bouteille.")).toBeTruthy();

    // Type choisi + commentaire -> succes.
    fireEvent.changeText(getByPlaceholderText("Note libre"), "RAS");
    fireEvent.press(getByText("Transfert"));
    await act(async () => {
      fireEvent.press(getByText("Valider l'echange"));
    });
    expect(postAction).toHaveBeenCalledWith("exchange", 1, {
      numero_serie_nouvelle: "FR-NEW-9",
      type_nouvelle: "transf",
      commentaire: "RAS",
    });
    expect(await findByText("Echange effectue.")).toBeTruthy();
    // Modale fermee -> le sous-titre n'est plus rendu.
    expect(queryByText(/Renseignez la nouvelle bouteille/)).toBeNull();
  });

  it("echange en echec -> message d'erreur dans la modale", async () => {
    getBottles.mockResolvedValue(ready([makeBottle({ statut: "retour_demande" })]));
    postAction.mockRejectedValue(new Error("quota depasse"));
    const { findByText, getByText, getByPlaceholderText } = render(
      <BottlesScreen brandColor="#1E56A8" />
    );
    fireEvent.press(await findByText("Echanger chez le fournisseur"));
    fireEvent.changeText(getByPlaceholderText("Ex: FR-2024-00123"), "FR-NEW-1");
    fireEvent.press(getByText("Transfert"));
    await act(async () => {
      fireEvent.press(getByText("Valider l'echange"));
    });
    expect(getByText("quota depasse")).toBeTruthy();
  });
});
