import { screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, expect, it, vi, beforeEach } from "vitest";
import CampaignRoiReport from "./CampaignRoiReport";
import { renderWithQueryClient as render } from "./test-utils";
import { ReportsAPI } from "@/apis/ReportsAPI";
import { toast } from "sonner";

vi.mock("@/apis/ReportsAPI", () => ({
  ReportsAPI: { getCampaignRoi: vi.fn(), runCampaignRoiSnapshot: vi.fn() },
}));

vi.mock("sonner", () => ({
  toast: { success: vi.fn(), error: vi.fn() },
}));

describe("CampaignRoiReport", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("muestra el loader mientras carga", () => {
    ReportsAPI.getCampaignRoi.mockReturnValue(new Promise(() => {}));
    render(<CampaignRoiReport companyId={1} onDataReady={vi.fn()} />);
    expect(document.querySelector(".animate-spin")).toBeTruthy();
  });

  it("muestra mensaje vacío cuando no hay atribuciones para calcular ROI", async () => {
    ReportsAPI.getCampaignRoi.mockResolvedValue([]);
    render(<CampaignRoiReport companyId={1} onDataReady={vi.fn()} />);
    expect(await screen.findByText(/No hay atribuciones para calcular ROI/i)).toBeInTheDocument();
  });

  it("renderiza la curva de decaimiento por campaña y notifica los datos exportables", async () => {
    const onDataReady = vi.fn();
    ReportsAPI.getCampaignRoi.mockResolvedValue([
      {
        idcampaign: 5,
        total_attributed_amount: 100000,
        curve: [
          { offset_months: 0, label: "M0", sales_count: 4, amount: 60000, decay_rate: null },
          { offset_months: 1, label: "M+1", sales_count: 2, amount: 40000, decay_rate: 33.33 },
        ],
      },
    ]);

    render(<CampaignRoiReport companyId={1} onDataReady={onDataReady} />);

    expect(await screen.findByText("Campaña #5")).toBeInTheDocument();
    expect(screen.getByText("$100,000")).toBeInTheDocument();
    expect(screen.getByText("33.3%")).toBeInTheDocument();

    await waitFor(() => expect(onDataReady).toHaveBeenCalled());
    expect(onDataReady.mock.calls[0][0].fileName).toBe("roi-campanas");
  });

  it("ejecuta la corrida de ROI al hacer click en el botón", async () => {
    const user = userEvent.setup();
    ReportsAPI.getCampaignRoi.mockResolvedValue([]);
    ReportsAPI.runCampaignRoiSnapshot.mockResolvedValue({ id: 1 });

    render(<CampaignRoiReport companyId={1} onDataReady={vi.fn()} />);
    await user.click(await screen.findByRole("button", { name: /Generar corrida de ROI/i }));

    await waitFor(() => expect(ReportsAPI.runCampaignRoiSnapshot).toHaveBeenCalledWith(1));
    expect(toast.success).toHaveBeenCalled();
  });

  it("no ejecuta la corrida cuando no hay companyId (guard interno)", async () => {
    const user = userEvent.setup();
    render(<CampaignRoiReport companyId={undefined} onDataReady={vi.fn()} />);
    await user.click(await screen.findByRole("button", { name: /Generar corrida de ROI/i }));
    expect(ReportsAPI.runCampaignRoiSnapshot).not.toHaveBeenCalled();
  });

  it("muestra error si la corrida de ROI falla", async () => {
    const user = userEvent.setup();
    ReportsAPI.getCampaignRoi.mockResolvedValue([]);
    ReportsAPI.runCampaignRoiSnapshot.mockRejectedValue(new Error("fail"));

    render(<CampaignRoiReport companyId={1} onDataReady={vi.fn()} />);
    await user.click(await screen.findByRole("button", { name: /Generar corrida de ROI/i }));
    await waitFor(() => expect(toast.error).toHaveBeenCalled());
  });
});
