import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import ShoppingListsPage from '../../../../src/app/(dashboard)/shopping-lists/page';
import * as useApiModule from '@/lib/useApi';
import * as ShoppingListsService from '@/services/shopping-lists';
import * as MealPlansService from '@/services/meal-plans';

vi.mock('@/lib/useApi');
vi.mock('@/services/shopping-lists');
vi.mock('@/services/meal-plans');

describe('ShoppingListsPage', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(useApiModule.useApi).mockReturnValue({
      householdId: 'hh1',
      isLoading: false,
      user: null,
      token: '123',
    });
    vi.mocked(ShoppingListsService.getShoppingLists).mockResolvedValue([
      { id: 'list1', name: 'Groceries', status: 'active', items: [], totalEstimatedCost: 10, createdAt: '2026-05-10' } as any,
    ]);
  });

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

  it('renders prompt if no household', () => {
    vi.mocked(useApiModule.useApi).mockReturnValue({ householdId: null, isLoading: false } as any);
    render(<ShoppingListsPage />);
    expect(screen.getByText('Please join a household.')).toBeInTheDocument();
  });

  it('loads and displays shopping lists', async () => {
    render(<ShoppingListsPage />);
    await waitFor(() => {
      expect(screen.getByText(/Groceries/i)).toBeInTheDocument();
    });
  });

  it('toggles create list modal and submits', async () => {
    vi.mocked(ShoppingListsService.createShoppingList).mockResolvedValue({
      id: 'list2', name: 'New List', status: 'active', items: [], createdAt: '2026-05-11'
    } as any);

    render(<ShoppingListsPage />);
    await waitFor(() => expect(screen.getByRole('button', { name: /New Shopping List/i })).toBeInTheDocument());

    fireEvent.click(screen.getByRole('button', { name: /New Shopping List/i }));
    
    // Wait for modal
    const input = await screen.findByPlaceholderText(/e.g., Weekly Costco Run/i);
    fireEvent.change(input, { target: { value: 'New List' } });

    fireEvent.click(screen.getByRole('button', { name: /^Create$/i }));

    await waitFor(() => {
      expect(ShoppingListsService.createShoppingList).toHaveBeenCalledWith('hh1', { name: 'New List', items: [] });
    });
  });

  it('opens generate from meal plan modal', async () => {
    vi.mocked(MealPlansService.listMealPlans).mockResolvedValue({
      data: [{ _id: 'mp1', status: 'active', weekStartDate: '2026-05-10', days: [] } as any],
      total: 1,
      page: 1,
      limit: 10,
    });
    vi.mocked(ShoppingListsService.generateFromMealPlan).mockResolvedValue({
      id: 'list3', name: 'Generated', status: 'active', items: [], createdAt: '2026-05-11'
    } as any);

    render(<ShoppingListsPage />);
    await waitFor(() => expect(screen.getByRole('button', { name: /Generate from Meal Plan/i })).toBeInTheDocument());

    fireEvent.click(screen.getByRole('button', { name: /Generate from Meal Plan/i }));

    await waitFor(() => {
      expect(MealPlansService.listMealPlans).toHaveBeenCalled();
    });

    const generateBtn = await screen.findByText(/Week of/i);
    fireEvent.click(generateBtn);

    await waitFor(() => {
      expect(ShoppingListsService.generateFromMealPlan).toHaveBeenCalledWith('hh1', 'mp1');
    });
  });

  it('handles fetch errors', async () => {
    vi.mocked(ShoppingListsService.getShoppingLists).mockRejectedValue(new Error('Failed to load'));
    render(<ShoppingListsPage />);
    await waitFor(() => {
      expect(screen.getByText('Failed to load')).toBeInTheDocument();
    });
  });

  it('can cancel/close creation and gap scanning modals', async () => {
    vi.mocked(MealPlansService.listMealPlans).mockResolvedValue({
      data: [],
      total: 0, page: 1, limit: 10
    });

    const { container } = render(<ShoppingListsPage />);

    // 1. Open Create Modal and close via overlay backdrop click
    fireEvent.click(screen.getByRole('button', { name: /New Shopping List/i }));
    const createHeading = screen.getByText('Create Shopping List');
    expect(createHeading).toBeInTheDocument();
    
    // The overlay is the grandparent of the heading. Let's click it!
    const overlay = createHeading.parentElement?.parentElement;
    if (overlay) fireEvent.click(overlay);
    expect(screen.queryByText('Create Shopping List')).not.toBeInTheDocument();

    // 2. Re-open Create Modal and close via Cancel button click
    fireEvent.click(screen.getByRole('button', { name: /New Shopping List/i }));
    fireEvent.click(screen.getByRole('button', { name: /Cancel/i }));
    expect(screen.queryByText('Create Shopping List')).not.toBeInTheDocument();

    // 3. Open Gap Modal and close via Close button
    fireEvent.click(screen.getByRole('button', { name: /Generate from Meal Plan/i }));
    await waitFor(() => {
      expect(screen.getByText('Scan Meal Plan Gaps')).toBeInTheDocument();
    });
    fireEvent.click(screen.getByRole('button', { name: /Close/i }));
    expect(screen.queryByText('Scan Meal Plan Gaps')).not.toBeInTheDocument();
  });

  it('renders empty state and opens create modal', async () => {
    vi.mocked(ShoppingListsService.getShoppingLists).mockResolvedValue([]);

    render(<ShoppingListsPage />);
    const createFirstBtn = await screen.findByRole('button', { name: /Create First List/i });
    fireEvent.click(createFirstBtn);
    expect(screen.getByText('Create Shopping List')).toBeInTheDocument();
  });

  it('renders completed lists', async () => {
    vi.mocked(ShoppingListsService.getShoppingLists).mockResolvedValue([
      { id: 'listc', name: 'Completed Groceries', status: 'completed', items: [], totalEstimatedCost: 25, createdAt: '2026-05-09' } as any
    ]);

    render(<ShoppingListsPage />);
    await screen.findByText('Completed Runs');
    expect(screen.getByText('Completed Groceries')).toBeInTheDocument();
  });

  it('handles generate from meal plan failure', async () => {
    vi.mocked(MealPlansService.listMealPlans).mockResolvedValue({
      data: [{ _id: 'mp1', status: 'active', weekStartDate: '2026-05-10', days: [] } as any],
      total: 1, page: 1, limit: 10
    });
    vi.mocked(ShoppingListsService.generateFromMealPlan).mockRejectedValue(new Error('Gen failed'));
    const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => {});

    render(<ShoppingListsPage />);
    fireEvent.click(screen.getByRole('button', { name: /Generate from Meal Plan/i }));
    
    const generateBtn = await screen.findByText(/Week of/i);
    fireEvent.click(generateBtn);

    await waitFor(() => {
      expect(alertSpy).toHaveBeenCalledWith('Gen failed');
    });
    alertSpy.mockRestore();
  });

  it('sorts lists by status and creation date', async () => {
    vi.mocked(ShoppingListsService.getShoppingLists).mockResolvedValue([
      { id: 'list1', name: 'List 1', status: 'completed', createdAt: '2026-05-10', items: [], totalEstimatedCost: 10 } as any,
      { id: 'list2', name: 'List 2', status: 'active', createdAt: '2026-05-12', items: [], totalEstimatedCost: 20 } as any,
    ]);

    render(<ShoppingListsPage />);
    await screen.findByText('List 2');
    expect(screen.getByText('List 1')).toBeInTheDocument();
  });

  it('handles create list failure', async () => {
    vi.mocked(ShoppingListsService.getShoppingLists).mockResolvedValue([]);
    vi.mocked(ShoppingListsService.createShoppingList).mockRejectedValue(new Error('Create failed'));
    const alertSpy = vi.spyOn(window, 'alert').mockImplementation(() => {});

    render(<ShoppingListsPage />);
    
    const openBtn = await screen.findByRole('button', { name: /Create First List/i });
    fireEvent.click(openBtn);

    const input = screen.getByPlaceholderText(/e.g., Weekly Costco Run/i);
    fireEvent.change(input, { target: { value: 'New List' } });

    const submitBtn = screen.getByRole('button', { name: /^Create$/ });
    fireEvent.click(submitBtn);

    await waitFor(() => {
      expect(alertSpy).toHaveBeenCalledWith('Create failed');
    });
    alertSpy.mockRestore();
  });

  it('handles open gap modal fetch failure', async () => {
    vi.mocked(ShoppingListsService.getShoppingLists).mockResolvedValue([]);
    vi.mocked(MealPlansService.listMealPlans).mockRejectedValue(new Error('Fetch plans failed'));
    const spy = vi.spyOn(console, 'error').mockImplementation(() => {});

    render(<ShoppingListsPage />);
    const btn = await screen.findByRole('button', { name: /Generate from Meal Plan/i });
    fireEvent.click(btn);

    await waitFor(() => {
      expect(spy).toHaveBeenCalled();
    });
    spy.mockRestore();
  });
});
