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

vi.mock("@/apis/configs/axiosConfigs", () => ({
  API: { get: vi.fn() },
}));

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

const dateRange = { startDate: "2026-08-01", endDate: "2026-08-31" };

const sale = (overrides = {}) => ({
  id: 1,
  sale_number: "V-0001",
  sale_date: "2026-08-15T12:00:00Z",
  customer_id: 10,
  customer_name: "Cliente Uno",
  subtotal: 1000,
  tax_amount: 190,
  discount_amount: 0,
  total: 1190,
  status: "completed",
  items: [{ product_id: 1, sku: "SKU-1", name: "Perfume A" }],
  payments: [{ payment_method_id: 1, payment_method_name: "Efectivo", amount: 1190 }],
  ...overrides,
});

function mockApiGet({ sales = [], members = [], paymentMethods = [] } = {}) {
  API.get.mockImplementation((url) => {
    if (url === "/sales") return Promise.resolve({ data: { data: { detail: sales } } });
    if (url === "/network-member") return Promise.resolve({ data: { data: { detail: members } } });
    if (url === "/payments") return Promise.resolve({ data: { data: { detail: paymentMethods } } });
    return Promise.resolve({ data: { data: { detail: [] } } });
  });
}

describe("SalesReport", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    ReportsAPI.getSalesByProduct.mockResolvedValue([]);
  });

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

  it("muestra mensaje cuando no hay ventas en el período", async () => {
    mockApiGet({ sales: [] });
    render(<SalesReport companyId={1} dateRange={dateRange} onDataReady={vi.fn()} />);

    expect(await screen.findByText(/No hay ventas en este período/i)).toBeInTheDocument();
  });

  it("filtra ventas fuera de rango y calcula totales", async () => {
    mockApiGet({
      sales: [
        sale({ id: 1, sale_date: "2026-08-15T12:00:00Z", total: 1000 }),
        sale({ id: 2, sale_date: "2026-09-05T12:00:00Z", total: 5000, sale_number: "V-0002" }),
      ],
    });

    render(<SalesReport companyId={1} dateRange={dateRange} onDataReady={vi.fn()} />);

    expect(await screen.findByText("V-0001")).toBeInTheDocument();
    expect(screen.queryByText("V-0002")).not.toBeInTheDocument();
    expect(screen.getAllByText("$1,000").length).toBeGreaterThan(0);
  });

  it("filtra por miembro y método de pago", async () => {
    const user = userEvent.setup();
    mockApiGet({
      sales: [
        sale({ id: 1, customer_id: 10, sale_number: "V-A" }),
        sale({ id: 2, customer_id: 20, sale_number: "V-B" }),
      ],
      members: [{ id: 10, full_name: "Cliente Diez" }, { id: 20, full_name: "Cliente Veinte" }],
      paymentMethods: [{ id: 1, name: "Efectivo" }],
    });

    render(<SalesReport companyId={1} dateRange={dateRange} onDataReady={vi.fn()} />);
    await screen.findByText("V-A");

    const [memberSelect] = screen.getAllByRole("combobox");
    await user.click(memberSelect);
    await user.click(await screen.findByRole("option", { name: "Cliente Diez" }));

    expect(screen.getByText("V-A")).toBeInTheDocument();
    expect(screen.queryByText("V-B")).not.toBeInTheDocument();
  });

  it("filtra por método de pago", async () => {
    const user = userEvent.setup();
    mockApiGet({
      sales: [
        sale({ id: 1, sale_number: "V-A", payments: [{ payment_method_id: 1, payment_method_name: "Efectivo", amount: 1190 }] }),
        sale({ id: 2, sale_number: "V-B", payments: [{ payment_method_id: 2, payment_method_name: "Tarjeta", amount: 1190 }] }),
      ],
      paymentMethods: [{ id: 1, name: "Efectivo" }, { id: 2, name: "Tarjeta" }],
    });

    render(<SalesReport companyId={1} dateRange={dateRange} onDataReady={vi.fn()} />);
    await screen.findByText("V-A");

    const [, paymentSelect] = screen.getAllByRole("combobox");
    await user.click(paymentSelect);
    await user.click(await screen.findByRole("option", { name: "Tarjeta" }));

    expect(screen.getByText("V-B")).toBeInTheDocument();
    expect(screen.queryByText("V-A")).not.toBeInTheDocument();
  });

  it("no consulta las APIs cuando no hay companyId", () => {
    render(<SalesReport companyId={undefined} dateRange={dateRange} onDataReady={vi.fn()} />);
    expect(API.get).not.toHaveBeenCalled();
  });

  it("muestra la tabla de ventas por producto cuando hay datos", async () => {
    mockApiGet({ sales: [sale()] });
    ReportsAPI.getSalesByProduct.mockResolvedValue([
      { idproduct: 1, product_name: "Perfume A", product_sku: "SKU-1", quantity_sold: 3, total_amount: 3000 },
    ]);

    render(<SalesReport companyId={1} dateRange={dateRange} onDataReady={vi.fn()} />);

    expect(await screen.findByText("Perfume A")).toBeInTheDocument();
    expect(screen.getByText("SKU-1")).toBeInTheDocument();
    expect(ReportsAPI.getSalesByProduct).toHaveBeenCalledWith(1, "2026-08-01", "2026-08-31");
  });

  it("muestra mensaje cuando no hay ventas por producto", async () => {
    mockApiGet({ sales: [sale()] });
    ReportsAPI.getSalesByProduct.mockResolvedValue([]);

    render(<SalesReport companyId={1} dateRange={dateRange} onDataReady={vi.fn()} />);

    expect(await screen.findByText(/No hay ventas por producto en este período/i)).toBeInTheDocument();
  });

  it("aplica fallbacks cuando faltan cliente, items, pagos o estado no es completed", async () => {
    mockApiGet({
      sales: [
        {
          id: 5,
          sale_number: "V-SIN-DATOS",
          sale_date: "2026-08-10T12:00:00Z",
          customer_id: null,
          customer_name: null,
          subtotal: 0,
          tax_amount: 0,
          discount_amount: 0,
          total: 0,
          status: "pending",
          items: undefined,
          payments: undefined,
        },
      ],
    });

    render(<SalesReport companyId={1} dateRange={dateRange} onDataReady={vi.fn()} />);

    expect(await screen.findByText("V-SIN-DATOS")).toBeInTheDocument();
    expect(screen.getAllByText("Cliente General").length).toBeGreaterThan(0);
    expect(screen.getByText("pending")).toBeInTheDocument();
  });

  it("agrupa por método 'Desconocido' cuando el pago no trae nombre de método", async () => {
    mockApiGet({
      sales: [sale({ payments: [{ payment_method_id: 9, amount: 500 }] })],
    });

    render(<SalesReport companyId={1} dateRange={dateRange} onDataReady={vi.fn()} />);

    expect(await screen.findByText("Desconocido")).toBeInTheDocument();
  });

  it("soporta respuesta con payload.data plano (array directo)", async () => {
    API.get.mockImplementation((url) => {
      if (url === "/sales") return Promise.resolve({ data: [sale({ sale_number: "V-PLANO" })] });
      return Promise.resolve({ data: { data: { detail: [] } } });
    });

    render(<SalesReport companyId={1} dateRange={dateRange} onDataReady={vi.fn()} />);

    expect(await screen.findByText("V-PLANO")).toBeInTheDocument();
  });

  it("llama onDataReady con el resumen exportable", async () => {
    const onDataReady = vi.fn();
    mockApiGet({ sales: [sale()] });

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

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