import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import type React from 'react';

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

const {
  mockGetRefillAlerts,
  mockListRefillLists,
  mockCreateRefillList,
  mockUpdateRefillList,
  mockUpdateRefillListItem,
  mockAddToCabinet,
} = vi.hoisted(() => ({
  mockGetRefillAlerts: vi.fn(),
  mockListRefillLists: vi.fn(),
  mockCreateRefillList: vi.fn(),
  mockUpdateRefillList: vi.fn(),
  mockUpdateRefillListItem: vi.fn(),
  mockAddToCabinet: vi.fn(),
}));

vi.mock('@/lib/useApi', () => ({ useApi: mockUseApi }));
vi.mock('@/services/refills', () => ({
  getRefillAlerts: mockGetRefillAlerts,
  listRefillLists: mockListRefillLists,
  createRefillList: mockCreateRefillList,
  updateRefillList: mockUpdateRefillList,
  updateRefillListItem: mockUpdateRefillListItem,
  addToCabinet: mockAddToCabinet,
}));
vi.mock('next/link', () => ({
  default: (props: { href?: string; children: React.ReactNode }) => props.children,
}));

import RefillsPage from '../../../../src/app/(dashboard)/refills/page';

const emptyAlerts = { data: [], pagination: { cursor: null, hasMore: false } };
const emptyLists = { data: [], pagination: { cursor: null, hasMore: false } };

beforeEach(() => {
  vi.clearAllMocks();
  mockGetRefillAlerts.mockResolvedValue(emptyAlerts);
  mockListRefillLists.mockResolvedValue(emptyLists);
});

describe('RefillsPage', () => {
  it('shows loading skeleton when session loading', () => {
    mockUseApi.mockReturnValue({ householdId: null, isLoading: true });
    render(<RefillsPage />);
    expect(screen.getByText('Refills')).toBeInTheDocument();
    expect(screen.queryByText('New Refill List')).not.toBeInTheDocument();
  });

  it('shows household prompt when no householdId', () => {
    mockUseApi.mockReturnValue({ householdId: null, isLoading: false });
    render(<RefillsPage />);
    expect(screen.getByText(/create or join a household/)).toBeInTheDocument();
  });

  it('renders Refills heading when householdId exists', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    render(<RefillsPage />);
    expect(screen.getByText('Refills')).toBeInTheDocument();
  });

  it('shows empty state for alerts', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    render(<RefillsPage />);
    await waitFor(() => expect(screen.getByText(/No medicines running low/)).toBeInTheDocument());
  });

  it('shows empty state for refill lists', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    render(<RefillsPage />);
    await waitFor(() => expect(screen.getByText(/No refill lists yet/)).toBeInTheDocument());
  });

  it('shows error when alerts fail', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockGetRefillAlerts.mockRejectedValue(new Error('Alert error'));
    render(<RefillsPage />);
    await waitFor(() => expect(screen.getByText('Alert error')).toBeInTheDocument());
  });

  it('shows New List button and toggles form', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    render(<RefillsPage />);

    await waitFor(() => screen.getByText('New List'));
    await userEvent.click(screen.getByText('New List'));
    expect(screen.getByPlaceholderText('List name')).toBeInTheDocument();
  });

  it('creates a new refill list', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockCreateRefillList.mockResolvedValue({
      _id: 'rl-1',
      name: 'Test List',
      status: 'active',
      items: [],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    });

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('New List'));
    await userEvent.click(screen.getByText('New List'));

    await waitFor(() => screen.getByPlaceholderText('List name'));
    fireEvent.change(screen.getByPlaceholderText('List name'), { target: { value: 'Test List' } });
    fireEvent.submit(screen.getByPlaceholderText('List name').closest('form')!);

    await waitFor(() =>
      expect(mockCreateRefillList).toHaveBeenCalledWith(
        'hh1',
        expect.objectContaining({ name: 'Test List' }),
      ),
    );
  });

  it('shows refill list and can select it', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListRefillLists.mockResolvedValue({
      data: [
        {
          _id: 'rl-1',
          name: 'Weekly List',
          status: 'active',
          items: [
            {
              _id: 'item-1',
              medicineId: 'med-1',
              medicineName: 'Aspirin',
              quantity: 30,
              unit: 'tablet',
              checked: false,
              addedToCabinet: false,
            },
          ],
          householdId: 'hh1',
          createdBy: 'u1',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => expect(screen.getByText('Aspirin')).toBeInTheDocument());
  });

  it('updates a refill list status', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    const refillList = {
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'active',
      items: [],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    };
    mockListRefillLists.mockResolvedValue({
      data: [refillList],
      pagination: { cursor: null, hasMore: false },
    });
    mockUpdateRefillList.mockResolvedValue({});

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByText('Start shopping'));
    await userEvent.click(screen.getByText('Start shopping'));

    expect(mockUpdateRefillList).toHaveBeenCalledWith('hh1', 'rl-1', { status: 'shopping' });
  });

  it('shows error when create list fails', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockCreateRefillList.mockRejectedValue(new Error('Create failed'));

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('New List'));
    await userEvent.click(screen.getByText('New List'));

    await waitFor(() => screen.getByPlaceholderText('List name'));
    fireEvent.change(screen.getByPlaceholderText('List name'), { target: { value: 'Test' } });
    fireEvent.submit(screen.getByPlaceholderText('List name').closest('form')!);

    await waitFor(() => expect(screen.getByText('Create failed')).toBeInTheDocument());
  });

  it('adds checked items to cabinet', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    const refillList = {
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'active',
      items: [
        {
          _id: 'item-1',
          medicineId: 'med-1',
          medicineName: 'Aspirin',
          quantity: 30,
          unit: 'tablet',
          checked: true,
          addedToCabinet: false,
        },
      ],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    };
    mockListRefillLists.mockResolvedValue({
      data: [refillList],
      pagination: { cursor: null, hasMore: false },
    });
    mockAddToCabinet.mockResolvedValue({ addedCount: 1 });
    vi.spyOn(window, 'confirm').mockReturnValue(true);
    vi.spyOn(window, 'alert').mockImplementation(() => {});

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByText(/Add .* to Cabinet/));
    await userEvent.click(screen.getByText(/Add .* to Cabinet/));

    expect(mockAddToCabinet).toHaveBeenCalledWith('hh1', 'rl-1');
  });

  it('generates a refill list from alerts', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockGetRefillAlerts.mockResolvedValue({
      data: [
        {
          medicineId: 'm1',
          medicineName: 'Aspirin',
          medicineStrength: 500,
          medicineStrengthUnit: 'mg',
          currentStock: 5,
          dailyConsumption: 1.5,
          daysUntilEmpty: 3,
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockCreateRefillList.mockResolvedValue({
      _id: 'rl-gen',
      name: 'Auto Refills',
      status: 'active',
      items: [],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    });

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Aspirin'));
    await userEvent.click(screen.getByText('Generate Refill List'));

    await waitFor(() => screen.getByPlaceholderText('List name, e.g. Weekly refills'));
    fireEvent.change(screen.getByPlaceholderText('List name, e.g. Weekly refills'), {
      target: { value: 'Auto Refills' },
    });
    fireEvent.submit(
      screen.getByPlaceholderText('List name, e.g. Weekly refills').closest('form')!,
    );

    await waitFor(() =>
      expect(mockCreateRefillList).toHaveBeenCalledWith(
        'hh1',
        expect.objectContaining({ name: 'Auto Refills', fromAlerts: true }),
      ),
    );
  });

  it('cancels create list form', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('New List'));
    await userEvent.click(screen.getByText('New List'));

    await waitFor(() => screen.getByPlaceholderText('List name'));
    await userEvent.click(
      screen.getAllByRole('button', { name: 'Cancel' })[
        screen.getAllByRole('button', { name: 'Cancel' }).length - 1
      ]!,
    );

    expect(screen.queryByPlaceholderText('List name')).not.toBeInTheDocument();
  });

  it('closes the refill list detail panel', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListRefillLists.mockResolvedValue({
      data: [
        {
          _id: 'rl-1',
          name: 'Weekly List',
          status: 'active',
          items: [],
          householdId: 'hh1',
          createdBy: 'u1',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByTitle('Close'));
    await userEvent.click(screen.getByTitle('Close'));

    // After close, detail panel should not be visible (no Close button)
    expect(screen.queryByTitle('Close')).not.toBeInTheDocument();
  });

  it('archives a refill list', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    const refillList = {
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'active',
      items: [],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    };
    mockListRefillLists.mockResolvedValue({
      data: [refillList],
      pagination: { cursor: null, hasMore: false },
    });
    mockUpdateRefillList.mockResolvedValue({});

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByText('Archive'));
    await userEvent.click(screen.getByText('Archive'));

    expect(mockUpdateRefillList).toHaveBeenCalledWith('hh1', 'rl-1', { status: 'archived' });
  });

  it('shows error when generate list fails', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockGetRefillAlerts.mockResolvedValue({
      data: [
        {
          medicineId: 'm1',
          medicineName: 'Aspirin',
          medicineStrength: 500,
          medicineStrengthUnit: 'mg',
          currentStock: 5,
          dailyConsumption: 1.5,
          daysUntilEmpty: 3,
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockCreateRefillList.mockRejectedValue(new Error('Generate failed'));

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Aspirin'));
    await userEvent.click(screen.getByText('Generate Refill List'));

    await waitFor(() => screen.getByPlaceholderText('List name, e.g. Weekly refills'));
    fireEvent.change(screen.getByPlaceholderText('List name, e.g. Weekly refills'), {
      target: { value: 'Auto Refills' },
    });
    fireEvent.submit(
      screen.getByPlaceholderText('List name, e.g. Weekly refills').closest('form')!,
    );

    await waitFor(() => expect(screen.getByText('Generate failed')).toBeInTheDocument());
  });

  it('dismisses refill list panel error', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListRefillLists.mockRejectedValue(new Error('List error'));

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('List error'));
    await userEvent.click(screen.getByText('Dismiss'));
    expect(screen.queryByText('List error')).not.toBeInTheDocument();
  });

  it('toggles item checked state', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    const refillList = {
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'active',
      items: [
        {
          _id: 'item-1',
          medicineId: 'med-1',
          medicineName: 'Aspirin',
          quantity: 30,
          unit: 'tablet',
          checked: false,
          addedToCabinet: false,
        },
      ],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    };
    mockListRefillLists.mockResolvedValue({
      data: [refillList],
      pagination: { cursor: null, hasMore: false },
    });
    mockUpdateRefillListItem.mockResolvedValue({
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'active',
      items: [
        {
          _id: 'item-1',
          medicineId: 'med-1',
          medicineName: 'Aspirin',
          quantity: 30,
          unit: 'tablet',
          checked: true,
          addedToCabinet: false,
        },
      ],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    });

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByText('Aspirin'));
    await userEvent.click(screen.getByRole('checkbox'));

    expect(mockUpdateRefillListItem).toHaveBeenCalledWith('hh1', 'rl-1', 'item-1', {
      checked: true,
    });
  });

  it('marks a shopping list as complete', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    const refillList = {
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'shopping',
      items: [],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    };
    mockListRefillLists.mockResolvedValue({
      data: [refillList],
      pagination: { cursor: null, hasMore: false },
    });
    mockUpdateRefillList.mockResolvedValue({});

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByText('Mark complete'));
    await userEvent.click(screen.getByText('Mark complete'));

    expect(mockUpdateRefillList).toHaveBeenCalledWith('hh1', 'rl-1', { status: 'completed' });
  });

  it('filters refill lists by status', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListRefillLists.mockResolvedValue({
      data: [
        {
          _id: 'rl-1',
          name: 'Active List',
          status: 'active',
          items: [],
          householdId: 'hh1',
          createdBy: 'u1',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Active List'));
    fireEvent.change(screen.getByDisplayValue('All statuses'), { target: { value: 'active' } });

    await waitFor(() => expect(mockListRefillLists).toHaveBeenCalledTimes(2));
  });

  it('shows actual price input after checking item and allows price entry', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    const refillList = {
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'active',
      items: [
        {
          _id: 'item-1',
          medicineId: 'med-1',
          medicineName: 'Aspirin',
          quantity: 30,
          unit: 'tablet',
          checked: true,
          addedToCabinet: false,
        },
      ],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    };
    mockListRefillLists.mockResolvedValue({
      data: [refillList],
      pagination: { cursor: null, hasMore: false },
    });

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByPlaceholderText('Actual price'));
    fireEvent.change(screen.getByPlaceholderText('Actual price'), { target: { value: '5.99' } });

    expect(screen.getByPlaceholderText('Actual price')).toBeInTheDocument();
  });

  it('dismisses panel-level error', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    const refillList = {
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'active',
      items: [
        {
          _id: 'item-1',
          medicineId: 'med-1',
          medicineName: 'Aspirin',
          quantity: 30,
          unit: 'tablet',
          checked: true,
          addedToCabinet: false,
        },
      ],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    };
    mockListRefillLists.mockResolvedValue({
      data: [refillList],
      pagination: { cursor: null, hasMore: false },
    });
    mockAddToCabinet.mockRejectedValue(new Error('Cabinet error'));
    vi.spyOn(window, 'confirm').mockReturnValue(true);

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByText(/Add .* to Cabinet/));
    await userEvent.click(screen.getByText(/Add .* to Cabinet/));

    await waitFor(() => screen.getByText('Cabinet error'));
    // Dismiss via inline button in the panel
    const dismissBtns = screen.getAllByText('Dismiss');
    await userEvent.click(dismissBtns[dismissBtns.length - 1]!);

    expect(screen.queryByText('Cabinet error')).not.toBeInTheDocument();
  });

  it('shows fallback error when non-Error thrown on update item', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    const refillList = {
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'active',
      items: [
        {
          _id: 'item-1',
          medicineId: 'med-1',
          medicineName: 'Aspirin',
          quantity: 30,
          unit: 'tablet',
          checked: false,
          addedToCabinet: false,
        },
      ],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    };
    mockListRefillLists.mockResolvedValue({
      data: [refillList],
      pagination: { cursor: null, hasMore: false },
    });
    mockUpdateRefillListItem.mockRejectedValue('update failed');

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByText('Aspirin'));
    await userEvent.click(screen.getAllByRole('checkbox')[0]!);

    await waitFor(() => expect(screen.getByText('Failed to update item')).toBeInTheDocument());
  });

  it('shows fallback error when non-Error thrown on update status', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    const refillList = {
      _id: 'rl-1',
      name: 'Weekly List',
      status: 'active',
      items: [],
      householdId: 'hh1',
      createdBy: 'u1',
      createdAt: '2026-01-01T00:00:00.000Z',
    };
    mockListRefillLists.mockResolvedValue({
      data: [refillList],
      pagination: { cursor: null, hasMore: false },
    });
    mockUpdateRefillList.mockRejectedValue('status failed');

    render(<RefillsPage />);

    await waitFor(() => screen.getByText('Weekly List'));
    await userEvent.click(screen.getByText('Weekly List'));

    await waitFor(() => screen.getByText('Start shopping'));
    await userEvent.click(screen.getByText('Start shopping'));

    await waitFor(() => expect(screen.getByText('Failed to update status')).toBeInTheDocument());
  });

  it('shows refill alert when present', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockGetRefillAlerts.mockResolvedValue({
      data: [
        {
          medicineId: 'm1',
          medicineName: 'Aspirin',
          medicineStrength: 500,
          medicineStrengthUnit: 'mg',
          currentStock: 5,
          dailyConsumption: 1.5,
          daysUntilEmpty: 3,
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    render(<RefillsPage />);

    await waitFor(() => expect(screen.getByText('Aspirin')).toBeInTheDocument());
  });
});
