import { act, render, screen, fireEvent, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { vi, it, expect, beforeEach } from 'vitest';
import { PayrollClient } from '../payroll-client';
const create = vi.fn(); const list = vi.fn(); const download = vi.fn();
vi.mock('@/lib/api', () => ({ api: { timekeeping: { payroll: { create: (...a: unknown[]) => create(...a), list: (...a: unknown[]) => list(...a), download: (...a: unknown[]) => download(...a) } }, scheduling: { settings: async () => ({ data: { timezone: 'America/Los_Angeles', work_week_start_day: 'mon', work_week_start_time: '00:00' } }) } } }));
vi.mock('@/lib/stores/store-context', async (importOriginal) => ({ ...(await importOriginal<typeof import('@/lib/stores/store-context')>()), useSelectedMembership: () => ({ store_id: 's1', capabilities: ['payroll.export'] }), requireSelectedStore: (s: string) => s }));
const auth = vi.hoisted(() => ({ commandGeneration: 1, token: 'token', user: { id: 'u1' } as { id: string } | null, selectedStoreId: 's1', memberships: [{ store_id: 's1' }], isLoading: false }));
vi.mock('@/stores/auth', () => ({ useAuthStore: { getState: () => auth } }));
function show() { render(<QueryClientProvider client={new QueryClient({ defaultOptions: { queries: { retry: false } } })}><PayrollClient /></QueryClientProvider>); }
beforeEach(() => { vi.resetAllMocks(); auth.user = { id: 'u1' }; auth.selectedStoreId = 's1'; list.mockResolvedValue({ data: [] }); });
it('discards a CSV that arrives after logout', async () => {
  list.mockResolvedValue({ data: [{ id: 'e1', filename: 'payroll.csv', week_starts: ['2026-09-07'], total_minutes: 480, total_pay_cents: 18500 }] });
  let resolve!: (blob: Blob) => void;
  download.mockReturnValue(new Promise(r => { resolve = r; }));
  const click = vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => {});
  show();
  fireEvent.click(await screen.findByRole('button', { name: 'Download payroll.csv' }));
  await waitFor(() => expect(download).toHaveBeenCalledOnce());
  auth.user = null;
  Object.defineProperty(URL, 'createObjectURL', { configurable: true, value: vi.fn(() => 'blob:private') });
  Object.defineProperty(URL, 'revokeObjectURL', { configurable: true, value: vi.fn() });
  await act(async () => { resolve(new Blob(['private payroll'])); });
  expect(click).not.toHaveBeenCalled(); click.mockRestore();
});
it('requires explicit oversized acknowledgement as a new payload/key, then retries it unchanged', async () => {
  create.mockRejectedValueOnce({ message: 'Review oversized sessions', errors: { oversized_sessions: ['Acknowledge sessions over 16 hours.'] } }).mockRejectedValueOnce(new TypeError('Connection lost')).mockResolvedValueOnce({ data: { id: 'e1' } });
  show();
  fireEvent.change(await screen.findByLabelText('First week start'), { target: { value: '2026-09-07' } });
  fireEvent.change(screen.getByLabelText('Last week start'), { target: { value: '2026-09-07' } });
  fireEvent.click(screen.getByRole('button', { name: 'Create CSV export' }));
  fireEvent.click(await screen.findByRole('checkbox', { name: /reviewed.*oversized/i }));
  fireEvent.click(screen.getByRole('button', { name: 'Acknowledge and export' }));
  await waitFor(() => expect(create).toHaveBeenCalledTimes(2));
  expect(create.mock.calls[1][1].acknowledge_oversized_sessions).toBe(true);
  expect(create.mock.calls[1][2]).not.toBe(create.mock.calls[0][2]);
  fireEvent.click(await screen.findByRole('button', { name: 'Retry same export' }));
  await waitFor(() => expect(create).toHaveBeenCalledTimes(3));
  expect(create.mock.calls[2]).toEqual(create.mock.calls[1]);
});
it('downloads historical CSV and revokes its object URL without creating an export', async () => {
  list.mockResolvedValue({ data: [{ id: 'e1', filename: 'payroll.csv', week_starts: ['2026-09-07'], total_minutes: 480, total_pay_cents: 18500 }] });
  download.mockResolvedValue(new Blob(['csv']));
  const objectUrl = vi.fn(() => 'blob:payroll'); const revoke = vi.fn();
  Object.defineProperty(URL, 'createObjectURL', { configurable: true, value: objectUrl });
  Object.defineProperty(URL, 'revokeObjectURL', { configurable: true, value: revoke });
  const click = vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => {});
  show();
  fireEvent.click(await screen.findByRole('button', { name: 'Download payroll.csv' }));
  await waitFor(() => expect(revoke).toHaveBeenCalledWith('blob:payroll'));
  expect(download).toHaveBeenCalledWith('s1', 'e1');
  expect(create).not.toHaveBeenCalled(); expect(click).toHaveBeenCalledOnce();
  click.mockRestore();
});
it('rejects more than 26 work weeks before dispatch', async () => {
  show();
  fireEvent.change(await screen.findByLabelText('First week start'), { target: { value: '2026-01-05' } });
  fireEvent.change(screen.getByLabelText('Last week start'), { target: { value: '2026-07-06' } });
  fireEvent.click(screen.getByRole('button', { name: 'Create CSV export' }));
  expect(await screen.findByRole('alert')).toHaveTextContent('26 weeks');
  expect(create).not.toHaveBeenCalled();
});
it('does not turn missing pay into zero dollars in history', async () => {
  list.mockResolvedValue({ data: [{ id: 'e1', filename: 'payroll.csv', week_starts: ['2026-09-07'], total_minutes: 480, total_pay_cents: null }] });
  show();
  expect(await screen.findByText('Unavailable — missing pay rate')).toBeInTheDocument();
  expect(screen.queryByText('$0.00')).not.toBeInTheDocument();
});
it('retains the export key across a failed request retry', async () => {
  create.mockRejectedValueOnce(new TypeError('Connection lost')).mockResolvedValueOnce({ data: { id: 'e1' } });
  show();
  fireEvent.change(await screen.findByLabelText('First week start'), { target: { value: '2026-09-07' } });
  fireEvent.change(screen.getByLabelText('Last week start'), { target: { value: '2026-09-14' } });
  fireEvent.click(screen.getByRole('button', { name: 'Create CSV export' }));
  expect(await screen.findByRole('alert')).toHaveTextContent('Connection lost');
  fireEvent.click(screen.getByRole('button', { name: 'Retry same export' }));
  await waitFor(() => expect(create).toHaveBeenCalledTimes(2));
  expect(create.mock.calls[0]).toEqual(create.mock.calls[1]);
  expect(create.mock.calls[0][1]).toEqual({ week_starts: ['2026-09-07', '2026-09-14'], acknowledge_oversized_sessions: false });
});
