import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { SWRConfig } from 'swr';
import ShoppingListDetailPage from '../../../../../src/app/(dashboard)/shopping-lists/[id]/page';
import * as useApiModule from '@/lib/useApi';
import * as ShoppingListsService from '@/services/shopping-lists';
import * as ProductsService from '@/services/products';
import * as useShoppingListSyncModule from '@/lib/useShoppingListSync';

vi.mock('@/lib/useApi');
vi.mock('@/services/shopping-lists');
vi.mock('@/services/products');
vi.mock('@/lib/useShoppingListSync');

const { mockPush } = vi.hoisted(() => ({
  mockPush: vi.fn(),
}));

// Mock next/navigation params
vi.mock('next/navigation', () => ({
  useParams: () => ({ id: 'list1' }),
  useRouter: () => ({ push: mockPush, back: vi.fn() }),
}));

describe('ShoppingListDetailPage', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    
    // Mock browser API modals
    window.confirm = vi.fn().mockReturnValue(true);
    window.alert = vi.fn();

    vi.mocked(useApiModule.useApi).mockReturnValue({
      householdId: 'hh1',
      isLoading: false,
      user: null,
      token: '123',
    } as any);

    vi.mocked(ProductsService.listProducts).mockResolvedValue({
      data: [
        { _id: 'p1', name: 'Apple', category: 'Produce', unit: 'pcs' }
      ]
    } as any);

    vi.mocked(ShoppingListsService.getShoppingList).mockResolvedValue({
      id: 'list1',
      name: 'Test List',
      status: 'active',
      items: [
        { id: 'item1', productId: 'p1', quantity: 5, checked: false, unit: 'pcs' },
      ],
      createdAt: '2026-05-10',
    } as any);

    vi.mocked(useShoppingListSyncModule.useShoppingListSync).mockReturnValue({
      isConnected: true,
      error: null,
      toggleItemCheck: vi.fn(),
    });
  });

  const wrapper = ({ children }: { children: React.ReactNode }) => (
    <SWRConfig value={{ provider: () => new Map(), dedupingInterval: 0 }}>{children}</SWRConfig>
  );

  it('renders loading initially', () => {
    vi.mocked(useApiModule.useApi).mockReturnValue({ householdId: null, isLoading: true } as any);
    render(<ShoppingListDetailPage />, { wrapper });
    expect(screen.getByText(/Hydrating session checklist/i)).toBeInTheDocument();
  });

  it('loads and displays the list', async () => {
    render(<ShoppingListDetailPage />, { wrapper });
    await waitFor(() => {
      expect(screen.getByText('Test List')).toBeInTheDocument();
      expect(screen.getAllByText('Apple')[0]).toBeInTheDocument();
    });
  });

  it('toggles an item check', async () => {
    render(<ShoppingListDetailPage />, { wrapper });
    await waitFor(() => expect(screen.getAllByText('Apple')[0]).toBeInTheDocument());
    
    const checkbox = screen.getByRole('button', { name: /Toggle check for Apple/i });
    fireEvent.click(checkbox);
    
    expect(useShoppingListSyncModule.useShoppingListSync).toHaveBeenCalled();
  });

  it('adds a new item to the list', async () => {
    vi.mocked(ShoppingListsService.addShoppingItem).mockResolvedValue({
      id: 'list1',
      items: [
        { id: 'item1', productId: 'p1', quantity: 5, checked: false },
        { id: 'item2', customName: 'Banana', quantity: 2, checked: false },
      ]
    } as any);

    render(<ShoppingListDetailPage />, { wrapper });
    await waitFor(() => expect(screen.getAllByText('Apple')[0]).toBeInTheDocument());

    const customInput = screen.getByPlaceholderText(/e.g., Generic Flour/i);
    fireEvent.change(customInput, { target: { value: 'Banana' } });

    const submitBtn = screen.getByRole('button', { name: /Add to List/i });
    fireEvent.click(submitBtn);

    await waitFor(() => {
      expect(ShoppingListsService.addShoppingItem).toHaveBeenCalledWith(
        'hh1',
        'list1',
        expect.objectContaining({ customName: 'Banana' })
      );
    });
  });

  it('completes the trip and syncs to pantry', async () => {
    vi.mocked(ShoppingListsService.getShoppingList).mockResolvedValue({
      id: 'list1',
      name: 'Test List',
      status: 'active',
      items: [
        { id: 'item1', productId: 'p1', quantity: 5, checked: true, addedToPantry: false },
      ],
      createdAt: '2026-05-10',
    } as any);

    vi.mocked(ShoppingListsService.updateShoppingList).mockResolvedValue({ status: 'completed' } as any);
    vi.mocked(ShoppingListsService.syncToPantry).mockResolvedValue({ addedCount: 1 } as any);

    render(<ShoppingListDetailPage />, { wrapper });
    await waitFor(() => expect(screen.getAllByText('Apple')[0]).toBeInTheDocument());

    const syncBtn = screen.getByRole('button', { name: /Sync.*items to Pantry/i });
    fireEvent.click(syncBtn);

    await waitFor(() => {
      expect(ShoppingListsService.syncToPantry).toHaveBeenCalledWith('hh1', 'list1');
      expect(ShoppingListsService.updateShoppingList).toHaveBeenCalledWith('hh1', 'list1', { status: 'completed' });
    });
  });

  it('deletes an item from the list', async () => {
    vi.mocked(ShoppingListsService.removeShoppingItem).mockResolvedValue({
      id: 'list1',
      items: []
    } as any);

    render(<ShoppingListDetailPage />, { wrapper });
    await waitFor(() => expect(screen.getAllByText('Apple')[0]).toBeInTheDocument());

    const trashBtn = screen.getByRole('button', { name: /Delete Apple/i });
    fireEvent.click(trashBtn);

    await waitFor(() => {
      expect(ShoppingListsService.removeShoppingItem).toHaveBeenCalledWith('hh1', 'list1', 'item1');
    });
  });

  it('loads store comparisons when clicked', async () => {
    vi.mocked(ShoppingListsService.getBasketStoreComparison).mockResolvedValue({
      singleStoreOptions: [
        { storeId: 's1', storeName: 'Walmart', estimatedTotal: 45.00, itemsCovered: 1 }
      ]
    } as any);

    render(<ShoppingListDetailPage />, { wrapper });
    await waitFor(() => expect(screen.getAllByText('Apple')[0]).toBeInTheDocument());

    const checkBtn = screen.getByRole('button', { name: /Check Lowest Store Options/i });
    fireEvent.click(checkBtn);

    await waitFor(() => {
      expect(ShoppingListsService.getBasketStoreComparison).toHaveBeenCalledWith('hh1', 'list1');
      expect(screen.getByText('Walmart')).toBeInTheDocument();
      expect(screen.getByText('$45.00')).toBeInTheDocument();
    });
  });

  it('fills out the complete add item form and submits', async () => {
    vi.mocked(ShoppingListsService.addShoppingItem).mockResolvedValue({
      id: 'list1',
      items: [
        { id: 'item1', productId: 'p1', quantity: 10, checked: false, unit: 'piece', notes: 'Fresh' }
      ]
    } as any);

    render(<ShoppingListDetailPage />, { wrapper });
    await waitFor(() => expect(screen.getAllByText('Apple')[0]).toBeInTheDocument());

    const selectInputs = screen.getAllByRole('combobox');
    // First combobox is Link Product Catalog
    fireEvent.change(selectInputs[0], { target: { value: 'p1' } });

    const qtyInput = screen.getByRole('spinbutton');
    fireEvent.change(qtyInput, { target: { value: '10' } });

    // Second combobox is Unit
    fireEvent.change(selectInputs[1], { target: { value: 'piece' } });

    const notesInput = screen.getByPlaceholderText(/Brand preference/i);
    fireEvent.change(notesInput, { target: { value: 'Fresh' } });

    const submitBtn = screen.getByRole('button', { name: /Add to List/i });
    fireEvent.click(submitBtn);

    await waitFor(() => {
      expect(ShoppingListsService.addShoppingItem).toHaveBeenCalledWith(
        'hh1',
        'list1',
        expect.objectContaining({
          productId: 'p1',
          quantity: 10,
          unit: 'piece',
          notes: 'Fresh'
        })
      );
    });
  });

  it('navigates back to hub when button clicked', async () => {
    render(<ShoppingListDetailPage />, { wrapper });
    await screen.findAllByText('Apple');
    
    const backBtn = screen.getByRole('button', { name: /Back to Hub/i });
    fireEvent.click(backBtn);
    expect(mockPush).toHaveBeenCalledWith('/shopping-lists');
  });

  it('handles delete failure gracefully', async () => {
    vi.mocked(ShoppingListsService.removeShoppingItem).mockRejectedValue(new Error('Delete failed'));
    const spy = vi.spyOn(window, 'alert').mockImplementation(() => {});

    render(<ShoppingListDetailPage />, { wrapper });
    await screen.findAllByText('Apple');

    const trashBtn = screen.getByRole('button', { name: /Delete Apple/i });
    fireEvent.click(trashBtn);

    await waitFor(() => {
      expect(spy).toHaveBeenCalledWith('Delete failed');
    });
    spy.mockRestore();
  });

  it('cancels sync to pantry if confirm is rejected', async () => {
    vi.mocked(ShoppingListsService.getShoppingList).mockResolvedValue({
      id: 'list1',
      name: 'Test List',
      status: 'active',
      items: [
        { id: 'item1', productId: 'p1', checked: true, addedToPantry: false, quantity: 5, unit: 'pcs' }
      ]
    } as any);
    
    const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(false);

    render(<ShoppingListDetailPage />, { wrapper });
    const syncBtn = await screen.findByRole('button', { name: /Sync 1 items to Pantry/i });
    fireEvent.click(syncBtn);

    expect(confirmSpy).toHaveBeenCalled();
    expect(ShoppingListsService.syncToPantry).not.toHaveBeenCalled();
    confirmSpy.mockRestore();
  });
});
