import React from 'react';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ThemeProvider } from 'styled-components';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { I18nextProvider } from 'react-i18next';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import '@testing-library/jest-dom';
import SearchPage from '@/features/search/pages/SearchPage';
import * as searchService from '@/features/search/services/searchService';
import * as ticketService from '@/features/tickets/services/ticketService';
import * as departmentSupportService from '@/features/tickets/services/departmentSupportService';
import type { SearchTicketsResult } from '@/features/search/types/search.types';
import type { TicketSummary } from '@/features/tickets/types/ticket.types';
import type { PaginatedResponse } from '@/shared/types/pagination.types';
import i18n from '@/locales/i18n';
import { LocaleProvider } from '@/app/providers/LocaleProvider';
import { lightTheme } from '@/styles/theme';
import { useAuthStore } from '@/features/auth/store/authStore';

vi.mock('@/features/search/services/searchService');
vi.mock('@/features/tickets/services/ticketService');
vi.mock('@/features/tickets/services/departmentSupportService');

vi.mock('@/shared/components/layout/AppLayout', () => ({
  AppLayout: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));

const mockTicket: TicketSummary = {
  id: 'ticket-search-1',
  ticket_number: 'TKT-900',
  subject: 'Email not syncing',
  description: 'Outlook issue',
  department: { id: 'dept-1', name: 'IT', code: 'IT' },
  requester: { id: 'u1', name: 'Jane', email: 'jane@test.com' },
  assignee: null,
  status: {
    id: 'st-1',
    name_en: 'Open',
    name_ar: 'مفتوح',
    color: '#f00',
    is_closed: false,
  },
  priority: {
    id: 'pr-1',
    name_en: 'High',
    name_ar: 'عالي',
    color: '#f00',
    sort_order: 1,
  },
  first_response_at: null,
  resolved_at: null,
  closed_at: null,
  created_at: '2026-05-18T10:00:00Z',
  updated_at: '2026-05-18T12:00:00Z',
  age_in_days: 1,
  is_assigned: false,
  is_open: true,
};

const mockSearchResult: SearchTicketsResult = {
  tickets: [mockTicket],
  total_count: 1,
  search_time_ms: 12,
  filters_applied: {},
  pagination: {
    current_page: 1,
    per_page: 25,
    total_pages: 1,
    total_items: 1,
  },
};

const emptyList: PaginatedResponse<TicketSummary> = {
  data: [],
  meta: { current_page: 1, last_page: 1, per_page: 100, total: 0 },
};

function renderSearchPage(initialEntry = '/search') {
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  return render(
    <QueryClientProvider client={client}>
      <ThemeProvider theme={lightTheme}>
        <I18nextProvider i18n={i18n}>
          <LocaleProvider>
            <MemoryRouter initialEntries={[initialEntry]}>
              <Routes>
                <Route path="/search" element={<SearchPage />} />
              </Routes>
            </MemoryRouter>
          </LocaleProvider>
        </I18nextProvider>
      </ThemeProvider>
    </QueryClientProvider>,
  );
}

describe('SearchPage', () => {
  beforeEach(() => {
    vi.clearAllMocks();

    useAuthStore.setState({
      user: {
        id: 'u1',
        name: 'Jane',
        email: 'jane@test.com',
        locale: 'en',
        company_id: 'co-1',
        company: {
          id: 'co-1',
          name: 'ACME',
          domain: 'acme.test',
          is_active: true,
          created_at: '',
          updated_at: '',
        },
        departments: [
          {
            id: 'dept-1',
            company_id: 'co-1',
            name: 'IT',
            is_active: true,
            created_at: '',
            updated_at: '',
          },
        ],
        roles: [],
        permissions: [],
        is_active: true,
        created_at: '',
        updated_at: '',
      },
      token: 'token',
      isAuthenticated: true,
      isLoading: false,
    });

    vi.mocked(ticketService.fetchTickets).mockResolvedValue(emptyList);
    vi.mocked(departmentSupportService.fetchTicketFormOptions).mockResolvedValue({
      categories: [],
      priorities: [],
    });
    vi.mocked(searchService.fetchSearchSuggestions).mockResolvedValue([
      { search_query: 'printer', results_count: 3 },
    ]);
    vi.mocked(searchService.fetchSavedFilters).mockResolvedValue({
      personal: [
        {
          id: 'filter-1',
          name: 'Open high',
          filter_data: { status_ids: ['st-1'] },
        },
      ],
      shared: [],
      quick: [{ id: 'unassigned', name: 'Unassigned', filter_data: { is_unassigned: true } }],
    });
  });

  it('shows loading state while searching', () => {
    vi.mocked(searchService.searchTickets).mockImplementation(
      () => new Promise(() => undefined),
    );

    renderSearchPage();

    expect(screen.getByText(/searching tickets/i)).toBeInTheDocument();
  });

  it('renders search results on success', async () => {
    vi.mocked(searchService.searchTickets).mockResolvedValue(mockSearchResult);

    renderSearchPage();

    await waitFor(() => {
      expect(screen.getByText('Email not syncing')).toBeInTheDocument();
    });

    expect(screen.getByText(/printer/i)).toBeInTheDocument();
    expect(screen.getByText('Open high')).toBeInTheDocument();
  });

  it('shows error state when search fails', async () => {
    vi.mocked(searchService.searchTickets).mockRejectedValue(new Error('Network error'));

    renderSearchPage();

    await waitFor(() => {
      expect(screen.getByText(/search could not complete/i)).toBeInTheDocument();
    });
  });

  it('applies saved filter and renders results', async () => {
    vi.mocked(searchService.searchTickets).mockResolvedValue({
      ...mockSearchResult,
      tickets: [],
      total_count: 0,
    });
    vi.mocked(searchService.applySavedFilter).mockResolvedValue(mockSearchResult);

    renderSearchPage();

    await waitFor(() => {
      expect(screen.getByText('Open high')).toBeInTheDocument();
    });

    fireEvent.click(screen.getByText('Open high'));

    await waitFor(() => {
      expect(searchService.applySavedFilter).toHaveBeenCalledWith('filter-1', 25, 1);
      expect(screen.getByText('Email not syncing')).toBeInTheDocument();
    });
  });

  it('renders search suggestions', async () => {
    vi.mocked(searchService.searchTickets).mockResolvedValue(mockSearchResult);

    renderSearchPage();

    await waitFor(() => {
      expect(screen.getByText(/recent searches/i)).toBeInTheDocument();
      expect(screen.getByRole('button', { name: 'printer' })).toBeInTheDocument();
    });
  });

  it('sends sort params from URL to search API', async () => {
    vi.mocked(searchService.searchTickets).mockResolvedValue(mockSearchResult);

    renderSearchPage('/search?sort=priority_desc');

    await waitFor(() => {
      expect(searchService.searchTickets).toHaveBeenCalledWith(
        expect.objectContaining({
          sort_by: 'priority',
          sort_direction: 'desc',
        }),
      );
    });
  });

  it('updates sort query when dropdown changes', async () => {
    vi.mocked(searchService.searchTickets).mockResolvedValue(mockSearchResult);

    renderSearchPage();

    await waitFor(() => {
      expect(screen.getByText('Email not syncing')).toBeInTheDocument();
    });

    const sortSelect = await screen.findByTestId('search-sort');
    fireEvent.change(sortSelect, { target: { value: 'updated_at_asc' } });

    await waitFor(() => {
      expect(searchService.searchTickets).toHaveBeenCalledWith(
        expect.objectContaining({
          sort_by: 'updated_at',
          sort_direction: 'asc',
        }),
      );
    });
  });

  it('keeps sort in query when paginating', async () => {
    vi.mocked(searchService.searchTickets).mockResolvedValue({
      ...mockSearchResult,
      pagination: {
        current_page: 1,
        per_page: 25,
        total_pages: 3,
        total_items: 60,
      },
    });

    renderSearchPage('/search?sort=priority_desc');

    await waitFor(() => {
      expect(screen.getByText('Email not syncing')).toBeInTheDocument();
    });

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

    await waitFor(() => {
      expect(searchService.searchTickets).toHaveBeenCalledWith(
        expect.objectContaining({
          page: 2,
          sort_by: 'priority',
          sort_direction: 'desc',
        }),
      );
    });
  });
});
