import React, { memo } from 'react';
import { render, screen, fireEvent, act } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import UsersOverviewClient from '@/app/(private)/dashboard/users/overview/UsersOverviewClient';
import OrdersOverviewClient from '@/app/(private)/dashboard/orders/overview/OrdersOverviewClient';
import ShipmentsPage from '@/app/(private)/dashboard/shipment-tracking/overview/page';

// Track render counts
const renderCounts = {
  DataTable: 0,
  OrdersTable: 0,
  ShipmentsDataTable: 0
};

// Mock getUsers
vi.mock('@/actions/user', () => ({
  getUsers: vi.fn().mockResolvedValue({
    data: {
      documents: [
        { id: "1", firstName: 'John', lastName: 'Doe', email: 'john@example.com', role: 'user' },
      ],
      total: 1,
    },
    message: "Success"
  }),
}));

// Mock getShipments - use a factory that returns a spyable function
const mockGetShipments = vi.fn();
vi.mock('@/actions/sales', () => ({
  getShipments: (...args: any[]) => mockGetShipments(...args),
}));

// Mock getOrders
vi.mock('@/actions/orders', () => ({
  getOrders: vi.fn().mockResolvedValue({
    documents: [
      { id: '1', amount_total: 100, order_status: 'open' },
    ],
    total: 1,
  }),
}));

// Mock DataTable module - define component inside factory to avoid hoisting issues
vi.mock('@/components/ui/data-table', () => {
  const DataTableMock = memo(function DataTableMock({ onRowClick, data, testIdPrefix = 'row' }: any) {
    renderCounts.DataTable++;
    const safeData = Array.isArray(data) ? data : [];
    return (
      <div data-testid="data-table">
        {safeData.map((item: any) => (
          <button
            key={item.id}
            data-testid={`${testIdPrefix}-${item.id}`}
            onClick={() => onRowClick(item)}
          >
            {item.firstName || item.tracking_number || item.id}
          </button>
        ))}
      </div>
    );
  });
  
  return {
    DataTable: (props: any) => <DataTableMock {...props} />,
  };
});

// Mock EntityDetailPanel and EntityDetailSheet
vi.mock('@/components/dashboard/data-view/EntityDetailSheet', () => ({
  EntityDetailPanel: vi.fn(() => <div data-testid="entity-detail-panel" />),
  EntityDetailSheet: vi.fn(() => <div data-testid="entity-detail-sheet" />),
}));

// Mock PaginationWithLinks
vi.mock('@/components/ui/pagination-with-links', () => ({
  default: vi.fn(() => <div data-testid="pagination" />),
}));

// Mock next/navigation
vi.mock('next/navigation', () => ({
  useRouter: () => ({
    push: vi.fn(),
    refresh: vi.fn(),
  }),
  usePathname: () => '/dashboard/users/overview',
  useSearchParams: () => new URLSearchParams(),
}));

// Mock OrdersTable - define inside factory
vi.mock('@/app/(private)/dashboard/orders/overview/OrdersTable', () => {
  const OrdersTableMockInner = function OrdersTableMock({ onRowClick, data }: any) {
    renderCounts.OrdersTable++;
    const safeData = Array.isArray(data) ? data : [];
    return (
      <div data-testid="orders-table">
        {safeData.map((item: any) => (
          <button
            key={item.id}
            data-testid={`order-row-${item.id}`}
            onClick={() => onRowClick(item)}
          >
            {item.id}
          </button>
        ))}
      </div>
    );
  };
  const OrdersTableMock = memo(OrdersTableMockInner);
  
  return {
    OrdersTable: (props: any) => <OrdersTableMock {...props} />,
  };
});

describe('Dashboard Referential Stability & Memoization', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    renderCounts.DataTable = 0;
    renderCounts.OrdersTable = 0;
  });

  it('should skip re-renders of DataTable in UsersOverviewClient when parent state changes', async () => {
    const initialData = {
      documents: [{ id: "1", firstName: 'John', lastName: 'Doe', email: 'john@example.com', role: 'user' }],
      total: 1,
    };

    await act(async () => {
      render(<UsersOverviewClient initialData={initialData as any} activePage={1} pageSize={10} />);
    });

    // Capture render count after initial load
    const countAfterLoad = renderCounts.DataTable;
    expect(countAfterLoad).toBeGreaterThan(0);

    // Click a row to trigger state change (setSelectedUser) in parent
    const row1 = screen.getByTestId('row-1');
    await act(async () => {
      fireEvent.click(row1);
    });

    // If useCallback and React.memo work, renderCounts.DataTable should NOT increase
    expect(renderCounts.DataTable).toBe(countAfterLoad);
  });

  it('should skip re-renders of OrdersTable in OrdersOverviewClient', async () => {
    const initialData = {
      documents: [{ id: '1', amount_total: 100, order_status: 'open' }],
      total: 1,
    };

    await act(async () => {
      render(<OrdersOverviewClient initialData={initialData as any} />);
    });

    const countAfterLoad = renderCounts.OrdersTable;
    expect(countAfterLoad).toBeGreaterThan(0);

    const row1 = screen.getByTestId('order-row-1');
    await act(async () => {
      fireEvent.click(row1);
    });

    // Note: React 18 might render twice in dev or certain test envs, 
    // but the goal is that state updates in parent don't trigger UNNECESSARY re-renders
    expect(renderCounts.OrdersTable).toBe(countAfterLoad);
  });

  it('should skip re-renders of DataTable in ShipmentsPage', async () => {
    // Configure mock for this test
    mockGetShipments.mockResolvedValue({
      data: {
        documents: [{ id: 'ship-1', tracking_number: 'TRK123' }],
        total: 1,
      },
      message: 'Success',
    });

    const searchParams = Promise.resolve({ page: '1', pageSize: '10' });
    let resolvedPage: any;
    await act(async () => {
      resolvedPage = await ShipmentsPage({ searchParams });
    });

    render(resolvedPage);

    const countAfterLoad = renderCounts.DataTable;
    expect(countAfterLoad).toBeGreaterThan(0);

    const row1 = screen.getByTestId('row-ship-1');
    await act(async () => {
      fireEvent.click(row1);
    });

    expect(renderCounts.DataTable).toBe(countAfterLoad);
  });
});
