import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import LoginPage from './page';
import { AuthProvider } from '../_lib/auth-context';
import { SuperAdminProvider } from '../_lib/super-admin-context';

const pushMock = jest.fn();
const refreshMock = jest.fn();

jest.mock('next/navigation', () => ({
  useRouter: () => ({ push: pushMock, refresh: refreshMock, replace: jest.fn() }),
  useSearchParams: () => new URLSearchParams(),
}));

function authResponse(body: unknown, status = 200) {
  return new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' } });
}

const UNAUTHORIZED = () => Promise.resolve(new Response('Unauthorized', { status: 401 }));

function mockFetch(responses: Record<string, (url: string, init?: RequestInit) => Response | Promise<Response>>) {
  fetchSpy.mockImplementation((url: string, init?: RequestInit) => {
    const handler = responses[url];
    if (handler) return handler(url, init);
    return responses['default']?.(url, init) ?? UNAUTHORIZED();
  });
}

beforeEach(() => {
  jest.clearAllMocks();
  fetchSpy = jest.spyOn(global, 'fetch');
  mockFetch({ default: UNAUTHORIZED });
});

afterEach(() => {
  jest.restoreAllMocks();
});

function renderWithProvider(ui: React.ReactElement) {
  return render(
    <AuthProvider>
      <SuperAdminProvider>{ui}</SuperAdminProvider>
    </AuthProvider>,
  );
}

describe('LoginPage', () => {
  it('renders email and password fields and a submit button', async () => {
    renderWithProvider(<LoginPage />);
    expect(await screen.findByLabelText(/email/i)).toBeInTheDocument();
    expect(await screen.findByLabelText(/password/i)).toBeInTheDocument();
    expect(await screen.findByRole('button', { name: /sign in/i })).toBeInTheDocument();
  });

  it('posts credentials to /api/auth/login and redirects on success', async () => {
    mockFetch({
      '/api/auth/login': () => authResponse({ admin: { id: 'a-1', email: 'me@x.com' } }),
      '/api/auth/me': () => authResponse({ admin: { id: 'a-1', email: 'me@x.com', role: 'OWNER', tenantId: 't1', tenantSlug: 'default', tenantName: 'Default' } }),
    });
    renderWithProvider(<LoginPage />);
    await screen.findByLabelText(/email/i);
    fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'me@x.com' } });
    fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'secret' } });
    fireEvent.click(screen.getByRole('button', { name: /sign in/i }));
    await waitFor(() =>
      expect(fetchSpy).toHaveBeenCalledWith(
        '/api/auth/login',
        expect.objectContaining({
          method: 'POST',
          body: JSON.stringify({ email: 'me@x.com', password: 'secret' }),
        }),
      ),
    );
    expect(pushMock).toHaveBeenCalledWith('/');
  });

  it('shows an error message on 401', async () => {
    mockFetch({
      '/api/auth/login': () => authResponse({ message: 'Invalid email or password' }, 401),
    });
    renderWithProvider(<LoginPage />);
    await screen.findByLabelText(/email/i);
    fireEvent.change(screen.getByLabelText(/email/i), { target: { value: 'me@x.com' } });
    fireEvent.change(screen.getByLabelText(/password/i), { target: { value: 'wrong' } });
    fireEvent.click(screen.getByRole('button', { name: /sign in/i }));
    expect(await screen.findByRole('alert')).toHaveTextContent(/invalid email or password/i);
    expect(pushMock).not.toHaveBeenCalled();
  });
});
