import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { beforeEach, describe, expect, it, vi } from "vitest";

import CampaignsPage from "./CampaignsPage";
import { CampaignsAPI } from "@/apis/CampaignsAPI";

vi.mock("@/apis/CampaignsAPI", () => ({
  CampaignsAPI: {
    getAll: vi.fn(),
    create: vi.fn(),
    remove: vi.fn(),
    generateToday: vi.fn(),
    materialize: vi.fn(),
    getContacts: vi.fn(),
    getReport: vi.fn(),
  },
}));

vi.mock("@/hooks/store/useCompanyStore", () => ({
  useCompanyStore: () => ({ selectedCompany: { id: 1, name: "Empresa Test" } }),
}));

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

const mockedCampaignsAPI = CampaignsAPI as unknown as Record<string, ReturnType<typeof vi.fn>>;

const baseCampaign = {
  id: 1,
  idcompany: 1,
  name: "Cumpleaños del mes",
  type: "cumpleanos" as const,
  criteria: null,
  valid_from: null,
  valid_until: null,
  is_active: true,
  created_by: 1,
  created_at: "2026-08-19T00:00:00Z",
  updated_at: "2026-08-19T00:00:00Z",
};

describe("CampaignsPage", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockedCampaignsAPI.getAll.mockResolvedValue([baseCampaign]);
    mockedCampaignsAPI.getContacts.mockResolvedValue([]);
    mockedCampaignsAPI.getReport.mockResolvedValue({
      idcampaign: 1,
      campaign_name: baseCampaign.name,
      pending: 0,
      contacted: 0,
      skipped: 0,
      total: 0,
    });
  });

  it("carga y muestra la lista de campañas", async () => {
    render(<CampaignsPage />);

    expect(await screen.findByText("Cumpleaños del mes")).toBeInTheDocument();
    expect(mockedCampaignsAPI.getAll).toHaveBeenCalledWith(1);
  });

  it("muestra mensaje cuando no hay campañas", async () => {
    mockedCampaignsAPI.getAll.mockResolvedValue([]);
    render(<CampaignsPage />);

    expect(await screen.findByText(/Aún no hay campañas creadas/i)).toBeInTheDocument();
  });

  it("muestra error si falla la carga inicial", async () => {
    const { toast } = await import("sonner");
    mockedCampaignsAPI.getAll.mockRejectedValue(new Error("fail"));
    render(<CampaignsPage />);

    await waitFor(() => expect(toast.error).toHaveBeenCalled());
  });

  it("crea una campaña nueva desde el formulario", async () => {
    const user = userEvent.setup();
    mockedCampaignsAPI.create.mockResolvedValue({ ...baseCampaign, id: 2, name: "Nueva" });
    render(<CampaignsPage />);

    await screen.findByText("Cumpleaños del mes");

    await user.type(screen.getByPlaceholderText(/Ej: Cumpleaños del mes/i), "Nueva campaña");
    await user.click(screen.getByRole("button", { name: /Crear campaña/i }));

    await waitFor(() =>
      expect(mockedCampaignsAPI.create).toHaveBeenCalledWith({
        idcompany: 1,
        name: "Nueva campaña",
        type: "cumpleanos",
      })
    );
  });

  it("no crea la campaña si el nombre está vacío", async () => {
    const { toast } = await import("sonner");
    const user = userEvent.setup();
    render(<CampaignsPage />);

    await screen.findByText("Cumpleaños del mes");
    await user.click(screen.getByRole("button", { name: /Crear campaña/i }));

    expect(toast.error).toHaveBeenCalledWith("El nombre de la campaña es obligatorio");
    expect(mockedCampaignsAPI.create).not.toHaveBeenCalled();
  });

  it("genera contactos hoy para una campaña", async () => {
    const user = userEvent.setup();
    mockedCampaignsAPI.generateToday.mockResolvedValue([
      { id: 10, idcampaign: 1, idcustomer: 5, status: "pending", idticket: null, skip_reason: null, generated_at: "2026-08-19" },
    ]);
    render(<CampaignsPage />);

    await screen.findByText("Cumpleaños del mes");
    await user.click(screen.getByRole("button", { name: /Generar contactos hoy/i }));

    await waitFor(() => expect(mockedCampaignsAPI.generateToday).toHaveBeenCalledWith(1));
  });

  it("muestra aviso cuando no hay nuevos elegibles", async () => {
    const { toast } = await import("sonner");
    const user = userEvent.setup();
    mockedCampaignsAPI.generateToday.mockResolvedValue([]);
    render(<CampaignsPage />);

    await screen.findByText("Cumpleaños del mes");
    await user.click(screen.getByRole("button", { name: /Generar contactos hoy/i }));

    await waitFor(() =>
      expect(toast.success).toHaveBeenCalledWith("No hay nuevos elegibles para hoy")
    );
  });

  it("materializa contactos pendientes de una campaña", async () => {
    const user = userEvent.setup();
    mockedCampaignsAPI.materialize.mockResolvedValue([
      { id: 10, idcampaign: 1, idcustomer: 5, status: "materializado", idticket: 99, skip_reason: null, generated_at: "2026-08-19" },
    ]);
    render(<CampaignsPage />);

    await screen.findByText("Cumpleaños del mes");
    await user.click(screen.getByRole("button", { name: /Materializar/i }));

    await waitFor(() => expect(mockedCampaignsAPI.materialize).toHaveBeenCalledWith(1));
  });

  it("elimina una campaña", async () => {
    const user = userEvent.setup();
    mockedCampaignsAPI.remove.mockResolvedValue(undefined);
    mockedCampaignsAPI.getAll.mockResolvedValueOnce([baseCampaign]).mockResolvedValueOnce([]);
    render(<CampaignsPage />);

    await screen.findByText("Cumpleaños del mes");
    await user.click(screen.getByRole("button", { name: /Eliminar/i }));

    await waitFor(() => expect(mockedCampaignsAPI.remove).toHaveBeenCalledWith(1));
  });

  it("selecciona una campaña y muestra su detalle con contactos y reporte", async () => {
    const user = userEvent.setup();
    mockedCampaignsAPI.getContacts.mockResolvedValue([
      {
        id: 10,
        idcampaign: 1,
        idcustomer: 5,
        customer_name: "Ana Pérez",
        status: "materializado",
        idticket: 99,
        ticket_number: "#00000099",
        skip_reason: null,
        generated_at: "2026-08-19",
      },
    ]);
    mockedCampaignsAPI.getReport.mockResolvedValue({
      idcampaign: 1,
      campaign_name: baseCampaign.name,
      pending: 0,
      contacted: 1,
      skipped: 0,
      total: 1,
    });

    render(<CampaignsPage />);

    await user.click(await screen.findByText("Cumpleaños del mes"));

    expect(await screen.findByText("Ana Pérez")).toBeInTheDocument();
    expect(screen.getByText("#00000099")).toBeInTheDocument();
    expect(screen.getByText("Contactado")).toBeInTheDocument();
    expect(screen.getByText("Detalle de campaña")).toBeInTheDocument();
  });

  it("muestra mensaje cuando la campaña seleccionada aún no tiene contactos", async () => {
    const user = userEvent.setup();
    render(<CampaignsPage />);

    await user.click(await screen.findByText("Cumpleaños del mes"));

    expect(
      await screen.findByText(/Sin contactos generados todavía/i)
    ).toBeInTheDocument();
  });

  it("muestra error si falla la carga del detalle de campaña", async () => {
    const { toast } = await import("sonner");
    const user = userEvent.setup();
    mockedCampaignsAPI.getContacts.mockRejectedValue(new Error("fail"));

    render(<CampaignsPage />);
    await user.click(await screen.findByText("Cumpleaños del mes"));

    await waitFor(() => expect(toast.error).toHaveBeenCalledWith("No se pudo cargar el detalle de la campaña"));
  });

  it("muestra error si falla generar contactos", async () => {
    const { toast } = await import("sonner");
    const user = userEvent.setup();
    mockedCampaignsAPI.generateToday.mockRejectedValue(new Error("fail"));

    render(<CampaignsPage />);
    await screen.findByText("Cumpleaños del mes");
    await user.click(screen.getByRole("button", { name: /Generar contactos hoy/i }));

    await waitFor(() => expect(toast.error).toHaveBeenCalledWith("No se pudo generar la base de contactos"));
  });

  it("muestra error si falla materializar", async () => {
    const { toast } = await import("sonner");
    const user = userEvent.setup();
    mockedCampaignsAPI.materialize.mockRejectedValue(new Error("fail"));

    render(<CampaignsPage />);
    await screen.findByText("Cumpleaños del mes");
    await user.click(screen.getByRole("button", { name: /Materializar/i }));

    await waitFor(() => expect(toast.error).toHaveBeenCalledWith("No se pudo materializar la campaña"));
  });

  it("muestra error si falla eliminar", async () => {
    const { toast } = await import("sonner");
    const user = userEvent.setup();
    mockedCampaignsAPI.remove.mockRejectedValue(new Error("fail"));

    render(<CampaignsPage />);
    await screen.findByText("Cumpleaños del mes");
    await user.click(screen.getByRole("button", { name: /Eliminar/i }));

    await waitFor(() => expect(toast.error).toHaveBeenCalledWith("No se pudo eliminar la campaña"));
  });

  it("refresca el detalle al generar contactos de la campaña ya seleccionada", async () => {
    const user = userEvent.setup();
    mockedCampaignsAPI.generateToday.mockResolvedValue([]);
    render(<CampaignsPage />);

    await user.click(await screen.findByText("Cumpleaños del mes"));
    await screen.findByText("Detalle de campaña");
    mockedCampaignsAPI.getContacts.mockClear();

    await user.click(screen.getByRole("button", { name: /Generar contactos hoy/i }));

    await waitFor(() => expect(mockedCampaignsAPI.getContacts).toHaveBeenCalled());
  });

  it("refresca el detalle al materializar la campaña ya seleccionada", async () => {
    const user = userEvent.setup();
    mockedCampaignsAPI.materialize.mockResolvedValue([]);
    render(<CampaignsPage />);

    await user.click(await screen.findByText("Cumpleaños del mes"));
    await screen.findByText("Detalle de campaña");
    mockedCampaignsAPI.getContacts.mockClear();

    await user.click(screen.getByRole("button", { name: /Materializar/i }));

    await waitFor(() => expect(mockedCampaignsAPI.getContacts).toHaveBeenCalled());
  });

  it("limpia la selección al eliminar la campaña seleccionada", async () => {
    const user = userEvent.setup();
    mockedCampaignsAPI.remove.mockResolvedValue(undefined);
    mockedCampaignsAPI.getAll.mockResolvedValueOnce([baseCampaign]).mockResolvedValueOnce([]);
    render(<CampaignsPage />);

    await user.click(await screen.findByText("Cumpleaños del mes"));
    await screen.findByText("Detalle de campaña");

    await user.click(screen.getByRole("button", { name: /Eliminar/i }));

    await waitFor(() => expect(screen.queryByText("Detalle de campaña")).not.toBeInTheDocument());
  });

  it("permite cambiar el tipo de campaña en el formulario", async () => {
    const user = userEvent.setup();
    mockedCampaignsAPI.create.mockResolvedValue({ ...baseCampaign, id: 3, type: "evento" });
    render(<CampaignsPage />);

    await screen.findByText("Cumpleaños del mes");

    await user.click(screen.getByRole("combobox"));
    await user.click(await screen.findByRole("option", { name: "Evento" }));

    await user.type(screen.getByPlaceholderText(/Ej: Cumpleaños del mes/i), "Evento especial");
    await user.click(screen.getByRole("button", { name: /Crear campaña/i }));

    await waitFor(() =>
      expect(mockedCampaignsAPI.create).toHaveBeenCalledWith({
        idcompany: 1,
        name: "Evento especial",
        type: "evento",
      })
    );
  });

  it("muestra error si falla la creación de campaña", async () => {
    const { toast } = await import("sonner");
    const user = userEvent.setup();
    mockedCampaignsAPI.create.mockRejectedValue(new Error("fail"));

    render(<CampaignsPage />);
    await screen.findByText("Cumpleaños del mes");
    await user.type(screen.getByPlaceholderText(/Ej: Cumpleaños del mes/i), "X");
    await user.click(screen.getByRole("button", { name: /Crear campaña/i }));

    await waitFor(() => expect(toast.error).toHaveBeenCalledWith("No se pudo crear la campaña"));
  });
});
