// Regression tests for the trip listing's query state — added after
// manual testing found that the recommendations panel persisted across
// a new search, visually masking the fresh results.
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import TripListing from '../pages/TripListing';
import { AuthProvider } from '../context/AuthContext';
import { Trip } from '../models/trip';

jest.mock('../api/trip-api', () => ({
  searchTrips: jest.fn(),
  getRecommendations: jest.fn()
}));
import { searchTrips, getRecommendations } from '../api/trip-api';

const mockSearch = searchTrips as jest.Mock;
const mockRecs = getRecommendations as jest.Mock;

function trip(code: string, name: string): Trip {
  return {
    _id: code, code, name,
    length: '4 nights / 5 days',
    start: '2027-01-01T08:00:00.000Z',
    resort: 'Testing Bay, 3 stars',
    perPerson: '500.00',
    image: 'reef1.jpg',
    description: `${name} description`
  };
}

function page(trips: Trip[], total = trips.length) {
  return { results: trips, totalMatches: total, nextCursor: null, sortBy: 'name', order: 'asc' };
}

function renderListing() {
  return render(
    <AuthProvider>
      <MemoryRouter>
        <TripListing />
      </MemoryRouter>
    </AuthProvider>
  );
}

beforeEach(() => {
  localStorage.clear();
  mockSearch.mockReset();
  mockRecs.mockReset();
});

test('submitting a search replaces the listed trips', async () => {
  mockSearch
    .mockResolvedValueOnce(page([trip('A1', 'Alpha Reef'), trip('B1', 'Beta Bay')]))
    .mockResolvedValueOnce(page([trip('A1', 'Alpha Reef')], 1));

  renderListing();
  await screen.findAllByText('Alpha Reef');

  fireEvent.change(screen.getByLabelText('Search trips'), { target: { value: 'alpha' } });
  fireEvent.click(screen.getByRole('button', { name: 'Search' }));

  await waitFor(() => expect(screen.queryAllByText('Beta Bay')).toHaveLength(0));
  expect(screen.getAllByText('Alpha Reef').length).toBeGreaterThan(0);
  expect(mockSearch).toHaveBeenLastCalledWith(
    expect.objectContaining({ search: 'alpha' })
  );
});

test('recommendations panel clears when a new search is submitted', async () => {
  mockSearch.mockResolvedValue(page([trip('A1', 'Alpha Reef'), trip('B1', 'Beta Bay')]));
  mockRecs.mockResolvedValue([{ ...trip('B1', 'Beta Bay'), similarity: 2.5 }]);

  renderListing();
  await screen.findAllByText('Alpha Reef');

  // Open the recommendations panel.
  fireEvent.change(screen.getByLabelText('Similar to'), { target: { value: 'A1' } });
  await screen.findByText(/Trips similar to Alpha Reef/);

  // A new search must dismiss the now-stale panel.
  fireEvent.change(screen.getByLabelText('Search trips'), { target: { value: 'beta' } });
  fireEvent.click(screen.getByRole('button', { name: 'Search' }));

  await waitFor(() =>
    expect(screen.queryByText(/Trips similar to/)).not.toBeInTheDocument()
  );
});

test('recommendations panel has a Clear button that dismisses it', async () => {
  mockSearch.mockResolvedValue(page([trip('A1', 'Alpha Reef')]));
  mockRecs.mockResolvedValue([]);

  renderListing();
  await screen.findAllByText('Alpha Reef');

  fireEvent.change(screen.getByLabelText('Similar to'), { target: { value: 'A1' } });
  await screen.findByText(/Trips similar to Alpha Reef/);

  fireEvent.click(screen.getByRole('button', { name: 'Clear' }));
  await waitFor(() =>
    expect(screen.queryByText(/Trips similar to/)).not.toBeInTheDocument()
  );
});
