import { render, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import type { WorkSession } from '@solelog/shared';
import Sessions from './Sessions';
import { apiFetch } from '../lib/api';

vi.mock('../lib/api', () => ({
  apiFetch: vi.fn(),
}));

const mockApiFetch = vi.mocked(apiFetch);

function makeSession(over: Partial<WorkSession>): WorkSession {
  return {
    id: 1,
    user_id: 'u1',
    activity_id: 10,
    activity_name: 'Frezen',
    user_name: 'Jan',
    insole_type: 'Kurk',
    pair_count: 4,
    start_time: new Date('2026-06-17T08:00:00Z').toISOString(),
    end_time: new Date('2026-06-17T09:00:00Z').toISOString(),
    duration_seconds: 3000,
    paused_seconds: 600,
    paused_at: null,
    status: 'completed',
    source: 'manual',
    notes: null,
    created_at: new Date('2026-06-17T09:00:00Z').toISOString(),
    ...over,
  };
}

const SESSIONS: WorkSession[] = [
  makeSession({
    id: 1,
    user_name: 'Jan',
    activity_name: 'Frezen',
    status: 'completed',
    duration_seconds: 3000,
    paused_seconds: 600,
    end_time: new Date('2026-06-17T09:00:00Z').toISOString(),
  }),
  makeSession({
    id: 2,
    user_name: 'Piet',
    activity_name: 'Lijmen',
    status: 'active',
    duration_seconds: null,
    paused_seconds: 0,
    end_time: null,
    start_time: new Date(Date.now() - 120_000).toISOString(),
  }),
  makeSession({
    id: 3,
    user_name: 'Klaas',
    activity_name: 'Snijden',
    status: 'discarded',
    duration_seconds: null,
    paused_seconds: 0,
    end_time: new Date('2026-06-17T07:00:00Z').toISOString(),
  }),
];

// Route any non-sessions call (users roster, activities) to an empty list.
function mockEndpoints(sessions: WorkSession[]) {
  mockApiFetch.mockImplementation((path: string) => {
    if (path === '/api/admin/sessions') return Promise.resolve(sessions as never);
    return Promise.resolve([] as never);
  });
}

function renderSessions() {
  const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  return render(
    <QueryClientProvider client={queryClient}>
      <Sessions />
    </QueryClientProvider>,
  );
}

describe('Sessions', () => {
  beforeEach(() => {
    mockApiFetch.mockReset();
  });

  afterEach(() => {
    vi.clearAllMocks();
  });

  it('renders a row per session with worker, activity and worked time', async () => {
    mockEndpoints(SESSIONS);
    renderSessions();

    expect(await screen.findByText('Sessies')).toBeInTheDocument();
    expect(await screen.findByText('Jan')).toBeInTheDocument();
    expect(screen.getByText('Frezen')).toBeInTheDocument();
    expect(screen.getByText('Piet')).toBeInTheDocument();
    expect(screen.getByText('Klaas')).toBeInTheDocument();
    // worked for the completed manual session = 3000s = 00:50:00
    expect(screen.getByText('00:50:00')).toBeInTheDocument();
    // pause shown when paused_seconds > 0 (600s = 00:10:00)
    expect(screen.getByText(/Pauze 00:10:00/)).toBeInTheDocument();
  });

  it('narrows the list when the status filter changes', async () => {
    mockEndpoints(SESSIONS);
    renderSessions();

    expect(await screen.findByText('Jan')).toBeInTheDocument();

    await userEvent.selectOptions(screen.getByLabelText('Status'), 'active');

    expect(screen.getByText('Piet')).toBeInTheDocument();
    expect(screen.queryByText('Jan')).not.toBeInTheDocument();
    expect(screen.queryByText('Klaas')).not.toBeInTheDocument();
  });

  it('shows Stop/Annuleer only on active rows and ✎ on all rows', async () => {
    mockEndpoints(SESSIONS);
    renderSessions();

    expect(await screen.findByText('Jan')).toBeInTheDocument();

    // ✎ edit on every row.
    expect(screen.getAllByRole('button', { name: /Bewerk/ })).toHaveLength(3);
    // Stop/Annuleer only on the single active row.
    expect(screen.getAllByRole('button', { name: 'Stop' })).toHaveLength(1);
    expect(screen.getAllByRole('button', { name: 'Annuleer' })).toHaveLength(1);
  });

  it('Stop on an active row calls the stop endpoint', async () => {
    mockEndpoints(SESSIONS);
    renderSessions();

    expect(await screen.findByText('Piet')).toBeInTheDocument();
    await userEvent.click(screen.getByRole('button', { name: 'Stop' }));

    await waitFor(() =>
      expect(mockApiFetch).toHaveBeenCalledWith(
        '/api/admin/sessions/2/stop',
        expect.objectContaining({ method: 'POST' }),
      ),
    );
  });

  it('Annuleer on an active row calls the discard endpoint', async () => {
    mockEndpoints(SESSIONS);
    renderSessions();

    expect(await screen.findByText('Piet')).toBeInTheDocument();
    await userEvent.click(screen.getByRole('button', { name: 'Annuleer' }));

    await waitFor(() =>
      expect(mockApiFetch).toHaveBeenCalledWith(
        '/api/admin/sessions/2/discard',
        expect.objectContaining({ method: 'POST' }),
      ),
    );
  });

  it('+ Nieuwe registratie opens the create form (worker picker visible)', async () => {
    mockEndpoints(SESSIONS);
    renderSessions();

    expect(await screen.findByText('Jan')).toBeInTheDocument();
    await userEvent.click(screen.getByRole('button', { name: '+ Nieuwe registratie' }));

    // Task 4 fills the form; for now we assert the create panel/region opens.
    expect(await screen.findByTestId('session-form')).toBeInTheDocument();
  });

  it('shows the empty state when there are no sessions', async () => {
    mockEndpoints([]);
    renderSessions();

    expect(await screen.findByText('Nog geen sessies.')).toBeInTheDocument();
  });

  it('keeps the discarded row hidden when filtering to voltooid', async () => {
    mockEndpoints(SESSIONS);
    renderSessions();

    expect(await screen.findByText('Jan')).toBeInTheDocument();
    await userEvent.selectOptions(screen.getByLabelText('Status'), 'completed');

    const list = screen.getByRole('list', { name: 'Sessies' });
    expect(within(list).getByText('Jan')).toBeInTheDocument();
    expect(within(list).queryByText('Piet')).not.toBeInTheDocument();
    expect(within(list).queryByText('Klaas')).not.toBeInTheDocument();
  });
});
