import React from "react";
import { render, screen, waitFor, fireEvent } from "@testing-library/react";
import { ProductAttributesManager } from "@/components/dashboard/catalog/ProductAttributesManager";
import { describe, it, expect, vi, beforeEach } from "vitest";
import * as catalogActions from "@/actions/catalog";

// Mock the actions
vi.mock("@/actions/catalog", () => ({
  getProductAttributes: vi.fn(),
  getAttributes: vi.fn(),
  getAttributeValueOptions: vi.fn(),
  linkAttributeToProduct: vi.fn(),
  unlinkAttributeFromProduct: vi.fn(),
}));

// Mock UniversalCombobox to track prop changes
const mockUniversalCombobox = vi.fn(({ label, options }) => (
  <div data-testid={`combobox-${label}`}>
    {options.map((o: any) => (
      <span key={o.value}>{o.label}</span>
    ))}
  </div>
));

vi.mock("@/components/dashboard/UniversalCombobox", () => ({
  UniversalCombobox: (props: any) => mockUniversalCombobox(props),
}));

describe("ProductAttributesManager", () => {
  const mockProductAttributes = [
    { attribute_id: 1, attribute_name: "Color", value_name: "Red", is_variant: true },
  ];
  const mockAttributes = { documents: [{ id: 10, name: "Size" }], total: 1 };
  const mockValues = [{ id: 100, name: "XL" }];

  beforeEach(() => {
    vi.clearAllMocks();
    (catalogActions.getProductAttributes as any).mockResolvedValue({ success: true, data: mockProductAttributes });
    (catalogActions.getAttributes as any).mockResolvedValue({ success: true, data: mockAttributes });
    (catalogActions.getAttributeValueOptions as any).mockResolvedValue({ success: true, data: mockValues });
    (catalogActions.unlinkAttributeFromProduct as any).mockResolvedValue({ success: true, data: { success: true } });
  });

  it("renders correctly and loads data", async () => {
    render(<ProductAttributesManager productId={123} />);

    await waitFor(() => {
      expect(catalogActions.getProductAttributes).toHaveBeenCalledWith(123);
      expect(catalogActions.getAttributes).toHaveBeenCalled();
      expect(screen.getByText("Color")).toBeInTheDocument();
      expect(screen.getByText("Red")).toBeInTheDocument();
      expect(screen.getByText("Ja")).toBeInTheDocument();
    });
  });

  it("provides stable options references to UniversalCombobox", async () => {
    const { rerender } = render(<ProductAttributesManager productId={123} />);

    // Wait until the combobox is called with the loaded options
    let firstOptions: any;
    await waitFor(() => {
      const call = mockUniversalCombobox.mock.calls.find(
        (call) => call[0].label === "Attribut wählen" && call[0].options.length > 0
      );
      expect(call).toBeDefined();
      firstOptions = call![0].options;
    });

    // Trigger a re-render with the same props
    rerender(<ProductAttributesManager productId={123} />);

    // Get the options from a subsequent call
    const subsequentCall = mockUniversalCombobox.mock.calls
      .filter((call) => call[0].label === "Attribut wählen")
      .pop();
    const subsequentOptions = subsequentCall![0].options;

    // Verify referential equality
    expect(firstOptions).toBe(subsequentOptions);
  });

  it("has correct aria-label on delete button", async () => {
    render(<ProductAttributesManager productId={123} />);

    await waitFor(() => {
      expect(screen.getByLabelText("Attribut entfernen")).toBeInTheDocument();
    });
  });

  it("shows loading spinner during deletion", async () => {
    // Delay the removal action to test loading state
    (catalogActions.unlinkAttributeFromProduct as any).mockImplementation(
      () => new Promise((resolve) => setTimeout(() => resolve({ success: true, data: { success: true } }), 100))
    );

    render(<ProductAttributesManager productId={123} />);

    await waitFor(() => {
      expect(screen.getByLabelText("Attribut entfernen")).toBeInTheDocument();
    });

    const deleteButton = screen.getByLabelText("Attribut entfernen");
    fireEvent.click(deleteButton);

    // Button should be disabled and show spinner (Loader2)
    expect(deleteButton).toBeDisabled();

    const spinner = deleteButton.querySelector(".animate-spin");
    expect(spinner).toBeInTheDocument();

    await waitFor(() => {
      expect(catalogActions.unlinkAttributeFromProduct).toHaveBeenCalledWith({
        product_id: 123,
        attribute_id: 1,
      });
      expect(deleteButton).not.toBeDisabled();
    });
  });
});
