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 { mockRecordPrice, mockGetPriceHistory, mockCompareStores } = vi.hoisted(() => ({
  mockRecordPrice: vi.fn(),
  mockGetPriceHistory: vi.fn(),
  mockCompareStores: vi.fn(),
}));

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

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

vi.mock('@/lib/useApi', () => ({ useApi: mockUseApi }));
vi.mock('@/services/medicine-prices', () => ({
  recordPrice: mockRecordPrice,
  getPriceHistory: mockGetPriceHistory,
  compareStores: mockCompareStores,
}));
vi.mock('@/services/medicines', () => ({
  listMedicines: mockListMedicines,
  listMedicineProducts: mockListMedicineProducts,
}));
vi.mock('@/services/stores', () => ({ listStores: mockListStores }));
vi.mock('next/link', () => ({
  default: (props: { href?: string; children: React.ReactNode }) => props.children,
}));

import MedicinePricesPage from '../../../../src/app/(dashboard)/medicine-prices/page';

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

beforeEach(() => {
  vi.clearAllMocks();
  mockListMedicines.mockResolvedValue(emptyResponse);
  mockListStores.mockResolvedValue(emptyResponse);
  mockGetPriceHistory.mockResolvedValue(emptyResponse);
  mockCompareStores.mockResolvedValue({ data: [] });
});

describe('MedicinePricesPage', () => {
  it('shows loading skeleton when session loading', () => {
    mockUseApi.mockReturnValue({ householdId: null, isLoading: true });
    render(<MedicinePricesPage />);
    expect(screen.getByText('Medicine Prices')).toBeInTheDocument();
    expect(screen.queryByText('Record Price')).not.toBeInTheDocument();
  });

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

  it('renders Medicine Prices heading when householdId exists', () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    render(<MedicinePricesPage />);
    expect(screen.getByText('Medicine Prices')).toBeInTheDocument();
    expect(screen.getByText('Record Price')).toBeInTheDocument();
  });

  it('shows empty prompt to select a medicine', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    render(<MedicinePricesPage />);
    await waitFor(() =>
      expect(screen.getByText('Select a medicine to view price history.')).toBeInTheDocument(),
    );
  });

  it('loads price history when medicine selected', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListMedicines.mockResolvedValue({
      data: [
        { _id: 'med-1', name: 'Metformin', strength: 500, strengthUnit: 'mg', form: 'tablet' },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockGetPriceHistory.mockResolvedValue({
      data: [
        {
          _id: 'pr-1',
          medicineId: 'med-1',
          medicineName: 'Metformin',
          storeId: 'st-1',
          storeName: 'Walgreens',
          price: 15.99,
          pricePerUnit: 0.18,
          currency: 'USD',
          quantity: 90,
          unit: 'tablet',
          date: '2026-01-01T00:00:00.000Z',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });

    render(<MedicinePricesPage />);

    await waitFor(() => screen.getByText('Select a medicine'));
    fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: 'med-1' } });

    await waitFor(() =>
      expect(mockGetPriceHistory).toHaveBeenCalledWith('hh1', 'med-1', expect.any(Object)),
    );
    // Wait for price records to render
    await waitFor(() => expect(screen.getByText('Walgreens')).toBeInTheDocument());
  });

  it('shows validation error in form when required fields missing', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });

    render(<MedicinePricesPage />);

    await userEvent.click(screen.getByText('Record Price'));
    await waitFor(() => screen.getByRole('button', { name: 'Record Price', hidden: false }));

    // The submit button inside the form also has text 'Record Price'
    const submitBtn = screen
      .getAllByRole('button', { name: 'Record Price' })
      .find((b) => b.getAttribute('type') === 'submit');
    if (submitBtn) {
      fireEvent.submit(submitBtn.closest('form')!);
      await waitFor(() =>
        expect(
          screen.getByText('Please select a medicine, a product, and a store.'),
        ).toBeInTheDocument(),
      );
    }
  });

  it('shows error when price history fails', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListMedicines.mockResolvedValue({
      data: [
        { _id: 'med-1', name: 'Metformin', strength: 500, strengthUnit: 'mg', form: 'tablet' },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockGetPriceHistory.mockRejectedValue(new Error('History load failed'));

    render(<MedicinePricesPage />);

    await waitFor(() => screen.getByText('Select a medicine'));
    fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: 'med-1' } });

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

  it('records a price successfully', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListMedicines.mockResolvedValue({
      data: [
        { _id: 'med-1', name: 'Metformin', strength: 500, strengthUnit: 'mg', form: 'tablet' },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockListStores.mockResolvedValue({
      data: [
        {
          _id: 'st-1',
          name: 'Walgreens',
          tags: [],
          isActive: true,
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockListMedicineProducts.mockResolvedValue({
      data: [{ _id: 'prod-1', brand: 'Generic', packageSize: 90, packageUnit: 'tablet' }],
      pagination: { cursor: null, hasMore: false },
    });
    mockRecordPrice.mockResolvedValue({});

    render(<MedicinePricesPage />);

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

    await waitFor(() => screen.getByText('Record Price', { selector: 'h2' }));

    // Select store
    fireEvent.change(screen.getByDisplayValue('Select store'), { target: { value: 'st-1' } });
    // Select medicine
    fireEvent.change(screen.getByDisplayValue('Select medicine'), { target: { value: 'med-1' } });

    // Wait for products to load and select product
    await waitFor(() => screen.getByText(/Generic.*90/));
    fireEvent.change(screen.getByDisplayValue('Select product'), { target: { value: 'prod-1' } });

    // Submit form
    fireEvent.submit(
      screen.getByRole('button', { name: 'Record Price', hidden: true }).closest('form')!,
    );

    await waitFor(() =>
      expect(mockRecordPrice).toHaveBeenCalledWith(
        'hh1',
        expect.objectContaining({ medicineId: 'med-1' }),
      ),
    );
    // Form should close after success
    await waitFor(() =>
      expect(screen.queryByText('Record Price', { selector: 'h2' })).not.toBeInTheDocument(),
    );
  });

  it('shows Load more button in price history', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListMedicines.mockResolvedValue({
      data: [
        { _id: 'med-1', name: 'Metformin', strength: 500, strengthUnit: 'mg', form: 'tablet' },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockGetPriceHistory.mockResolvedValue({
      data: [
        {
          _id: 'pr-1',
          medicineId: 'med-1',
          medicineName: 'Metformin',
          storeId: 'st-1',
          storeName: 'Walgreens',
          price: 15.99,
          pricePerUnit: 0.18,
          currency: 'USD',
          quantity: 90,
          unit: 'tablet',
          date: '2026-01-01T00:00:00.000Z',
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: 'cur1', hasMore: true },
    });

    render(<MedicinePricesPage />);

    await waitFor(() => screen.getByText('Select a medicine'));
    fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: 'med-1' } });

    await waitFor(() => expect(screen.getByText('Load more')).toBeInTheDocument());
    await userEvent.click(screen.getByText('Load more'));

    expect(mockGetPriceHistory).toHaveBeenCalledTimes(2);
  });

  it('shows store filter when medicine selected and changes it', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListMedicines.mockResolvedValue({
      data: [
        { _id: 'med-1', name: 'Metformin', strength: 500, strengthUnit: 'mg', form: 'tablet' },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockListStores.mockResolvedValue({
      data: [
        {
          _id: 'st-1',
          name: 'Walgreens',
          tags: [],
          isActive: true,
          createdAt: '2026-01-01T00:00:00.000Z',
        },
      ],
      pagination: { cursor: null, hasMore: false },
    });

    render(<MedicinePricesPage />);

    await waitFor(() => screen.getByText('Select a medicine'));
    fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: 'med-1' } });

    await waitFor(() => screen.getByDisplayValue('All stores'));
    fireEvent.change(screen.getByDisplayValue('All stores'), { target: { value: 'st-1' } });

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

  it('dismisses price history error', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListMedicines.mockResolvedValue({
      data: [
        { _id: 'med-1', name: 'Metformin', strength: 500, strengthUnit: 'mg', form: 'tablet' },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockGetPriceHistory.mockRejectedValue(new Error('History load failed'));

    render(<MedicinePricesPage />);

    await waitFor(() => screen.getByText('Select a medicine'));
    fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: 'med-1' } });

    await waitFor(() => screen.getByText('History load failed'));
    await userEvent.click(screen.getByText('Dismiss'));
    expect(screen.queryByText('History load failed')).not.toBeInTheDocument();
  });

  it('shows store comparison table', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    mockListMedicines.mockResolvedValue({
      data: [
        { _id: 'med-1', name: 'Metformin', strength: 500, strengthUnit: 'mg', form: 'tablet' },
      ],
      pagination: { cursor: null, hasMore: false },
    });
    mockCompareStores.mockResolvedValue({
      data: [
        {
          storeId: 'st-1',
          storeName: 'Walgreens',
          latestPrice: 12.99,
          latestPricePerUnit: 0.14,
          currency: 'USD',
          date: '2026-01-01T00:00:00.000Z',
          isInsurancePrice: false,
        },
        {
          storeId: 'st-2',
          storeName: 'CVS',
          latestPrice: 14.99,
          latestPricePerUnit: 0.17,
          currency: 'USD',
          date: '2026-01-01T00:00:00.000Z',
          isInsurancePrice: false,
        },
      ],
    });

    render(<MedicinePricesPage />);

    await waitFor(() => screen.getByText('Select a medicine'));
    fireEvent.change(screen.getAllByRole('combobox')[0]!, { target: { value: 'med-1' } });

    await waitFor(() => expect(screen.getByText('Store comparison')).toBeInTheDocument());
    expect(screen.getByText('Walgreens')).toBeInTheDocument();
    // First row (cheapest) should be highlighted green
    expect(screen.getByText('cheapest')).toBeInTheDocument();
  });

  it('changes price, currency, quantity, and unit fields in form', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });

    render(<MedicinePricesPage />);

    await userEvent.click(screen.getByText('Record Price'));
    await waitFor(() => screen.getByPlaceholderText('9.99'));

    fireEvent.change(screen.getByPlaceholderText('9.99'), { target: { value: '15.99' } });
    fireEvent.change(screen.getByPlaceholderText('USD'), { target: { value: 'EUR' } });
    fireEvent.change(screen.getByPlaceholderText('90'), { target: { value: '30' } });
    fireEvent.change(screen.getByDisplayValue('tablet'), { target: { value: 'capsule' } });

    expect(screen.getByPlaceholderText('9.99')).toBeInTheDocument();
  });

  it('changes medicine search, notes and insurance price fields in form', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });

    render(<MedicinePricesPage />);

    await userEvent.click(screen.getByText('Record Price'));
    await waitFor(() => screen.getByPlaceholderText('Search medicines...'));

    fireEvent.change(screen.getByPlaceholderText('Search medicines...'), {
      target: { value: 'Met' },
    });

    // Notes field (no placeholder, but maxLength 1000)
    const notesInput = document.querySelector('input[maxLength="1000"]') as HTMLElement;
    if (notesInput) {
      fireEvent.change(notesInput, { target: { value: 'Some notes' } });
    }

    // Insurance price checkbox
    fireEvent.click(screen.getByLabelText('Insurance price'));
    expect(screen.getByLabelText('Insurance price')).toBeChecked();
  });

  it('toggles Record Price form', async () => {
    mockUseApi.mockReturnValue({ householdId: 'hh1', isLoading: false });
    render(<MedicinePricesPage />);
    await userEvent.click(screen.getByText('Record Price'));
    // Both the header toggle and the form show "Cancel" when open
    expect(screen.getAllByRole('button', { name: 'Cancel' }).length).toBeGreaterThan(0);
    await userEvent.click(screen.getAllByRole('button', { name: 'Cancel' })[0]);
    expect(screen.queryByRole('button', { name: 'Cancel' })).not.toBeInTheDocument();
  });
});
