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

vi.mock("@/apis/GoalsAPI", () => ({
  GoalsAPI: {
    listTemplates: vi.fn(),
    createTemplate: vi.fn(),
    instantiate: vi.fn(),
    upsertFactor: vi.fn(),
    compliance: vi.fn(),
  },
}));

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

describe("GoalsSection", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    GoalsAPI.listTemplates.mockResolvedValue([]);
    GoalsAPI.compliance.mockResolvedValue([]);
    GoalsAPI.createTemplate.mockResolvedValue({ id: 1 });
    GoalsAPI.instantiate.mockResolvedValue();
    GoalsAPI.upsertFactor.mockResolvedValue();
  });

  it("no renderiza sin companyId", () => {
    const { container } = render(<GoalsSection companyId={undefined} year={2026} />);
    expect(container.firstChild).toBeNull();
  });

  it("muestra loader de plantillas", () => {
    GoalsAPI.listTemplates.mockReturnValue(new Promise(() => {}));
    render(<GoalsSection companyId={1} year={2026} />);
    expect(document.querySelector(".animate-spin")).toBeTruthy();
  });

  it("crea plantilla, guarda factor y genera el mes", async () => {
    const user = userEvent.setup();
    GoalsAPI.listTemplates.mockResolvedValue([
      { id: 9, name: "Meta ventas agosto", metric_type: "sales", default_target: 100000 },
    ]);
    GoalsAPI.compliance.mockResolvedValue([
      { id: 1, template_name: "Meta ventas agosto", metric_type: "sales", target_value: 100000, actual_value: 50000, compliance_percent: 50 },
    ]);

    render(<GoalsSection companyId={3} year={2026} />);
    expect(await screen.findByText("Plantillas de metas")).toBeInTheDocument();
    expect((await screen.findAllByText(/Meta ventas agosto/)).length).toBeGreaterThan(0);
    expect(screen.getByText("50")).toBeInTheDocument();

    await user.click(screen.getByRole("button", { name: /Crear plantilla/i }));
    await waitFor(() => expect(GoalsAPI.createTemplate).toHaveBeenCalled());
    expect(toast.success).toHaveBeenCalled();

    await user.click(screen.getByRole("button", { name: /Guardar factor/i }));
    await waitFor(() => expect(GoalsAPI.upsertFactor).toHaveBeenCalled());

    await user.click(screen.getByRole("button", { name: /Generar mes/i }));
    await waitFor(() => expect(GoalsAPI.instantiate).toHaveBeenCalledWith(9, 2026, expect.anything()));
  });

  it("muestra errores si fallan las acciones", async () => {
    const user = userEvent.setup();
    GoalsAPI.createTemplate.mockRejectedValue(new Error("x"));
    GoalsAPI.upsertFactor.mockRejectedValue(new Error("x"));
    GoalsAPI.instantiate.mockRejectedValue(new Error("x"));
    GoalsAPI.listTemplates.mockResolvedValue([
      { id: 2, name: "Llamados", metric_type: "calls", default_target: 10 },
    ]);

    render(<GoalsSection companyId={1} year={2026} />);
    await screen.findByText(/Llamados/);
    await user.click(screen.getByRole("button", { name: /Crear plantilla/i }));
    await user.click(screen.getByRole("button", { name: /Guardar factor/i }));
    await user.click(screen.getByRole("button", { name: /Generar mes/i }));
    await waitFor(() => expect(toast.error).toHaveBeenCalled());
  });

  it("cambia nombre, meta, mes y factor y muestra vacío de cumplimiento", async () => {
    const user = userEvent.setup();
    render(<GoalsSection companyId={1} year={2026} />);
    await screen.findByText("Plantillas de metas");
    expect(screen.getByText("No hay metas generadas para este mes")).toBeInTheDocument();

    const nameInput = screen.getByDisplayValue("Meta mensual");
    await user.clear(nameInput);
    await user.type(nameInput, "Meta Q3");

    const targetInput = screen.getByDisplayValue("100000");
    await user.clear(targetInput);
    await user.type(targetInput, "25000");

    const monthInput = screen.getByDisplayValue(String(new Date().getMonth() + 1));
    await user.clear(monthInput);
    await user.type(monthInput, "8");

    const factorInput = screen.getByDisplayValue("1");
    await user.clear(factorInput);
    await user.type(factorInput, "1.5");

    await user.click(screen.getByRole("button", { name: /Crear plantilla/i }));
    await waitFor(() =>
      expect(GoalsAPI.createTemplate).toHaveBeenCalledWith(
        expect.objectContaining({ name: "Meta Q3", default_target: 25000 }),
      ),
    );

    await user.click(screen.getByRole("button", { name: /Guardar factor/i }));
    await waitFor(() =>
      expect(GoalsAPI.upsertFactor).toHaveBeenCalledWith(1, 8, 1.5),
    );
  });

  it("muestra el código de métrica si no hay etiqueta", async () => {
    GoalsAPI.listTemplates.mockResolvedValue([
      { id: 3, name: "Otra", metric_type: "custom", default_target: 1 },
    ]);
    render(<GoalsSection companyId={1} year={2026} />);
    expect(await screen.findByText(/Otra \(custom\)/)).toBeInTheDocument();
  });

  it("usa métrica y guion cuando el cumplimiento no trae nombre ni porcentaje", async () => {
    GoalsAPI.listTemplates.mockResolvedValue(undefined);
    GoalsAPI.compliance.mockResolvedValue([
      { id: 4, template_name: null, metric_type: "sales", target_value: 10, actual_value: 2, compliance_percent: null },
    ]);
    render(<GoalsSection companyId={1} year={2026} />);
    expect(await screen.findByText("sales")).toBeInTheDocument();
    expect(screen.getByText("—")).toBeInTheDocument();
  });

  it("cambia la métrica a llamados", async () => {
    const user = userEvent.setup();
    render(<GoalsSection companyId={1} year={2026} />);
    await screen.findByText("Plantillas de metas");
    await user.click(screen.getByRole("combobox"));
    await user.click(await screen.findByRole("option", { name: "Llamados" }));
    await user.click(screen.getByRole("button", { name: /Crear plantilla/i }));
    await waitFor(() =>
      expect(GoalsAPI.createTemplate).toHaveBeenCalledWith(expect.objectContaining({ metric_type: "calls", dimension_type: "seller" })),
    );
  });
});
