import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { useRouter } from 'next/navigation';
import RegisterPage from '../page';

vi.mock('next/navigation', () => ({
  useRouter: vi.fn(),
}));

const registerMock = vi.fn();
vi.mock('@/stores/auth', () => ({
  useAuthStore: () => ({ register: registerMock }),
}));

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

function fill({
  name = 'Jane Doe',
  email = 'jane@example.com',
  password = 'secret123',
  confirm = 'secret123',
} = {}) {
  fireEvent.change(screen.getByLabelText('Name'), { target: { value: name } });
  fireEvent.change(screen.getByLabelText('Email'), { target: { value: email } });
  fireEvent.change(screen.getByLabelText('Password'), {
    target: { value: password },
  });
  fireEvent.change(screen.getByLabelText('Confirm password'), {
    target: { value: confirm },
  });
}

describe('RegisterPage', () => {
  const push = vi.fn();

  beforeEach(() => {
    sessionStorage.clear();
    window.history.replaceState(null, '', '/register');
    push.mockClear();
    registerMock.mockReset();
    vi.mocked(useRouter).mockReturnValue({
      push,
      back: vi.fn(),
      forward: vi.fn(),
      refresh: vi.fn(),
      replace: vi.fn(),
      prefetch: vi.fn(),
    } as unknown as ReturnType<typeof useRouter>);
  });

  it('requires phone for invitation registration and resumes the invitation using the standard register action', async () => {
    sessionStorage.setItem('alqove.pending-invitation', 'invite-secret');
    registerMock.mockResolvedValue(undefined);
    render(<RegisterPage />);
    fill();
    const phone = screen.getByLabelText('Phone number');
    expect(phone).toBeRequired();
    fireEvent.submit(screen.getByRole('button', { name: 'Create Account' }).closest('form')!);
    expect(registerMock).not.toHaveBeenCalled();
    fireEvent.change(phone, { target: { value: '+13125550123' } });
    fireEvent.click(screen.getByRole('button', { name: 'Create Account' }));
    await waitFor(() => expect(registerMock).toHaveBeenCalledWith('Jane Doe', 'jane@example.com', 'secret123', '+13125550123'));
    expect(push).toHaveBeenCalledWith('/invitations/accept');
    expect(sessionStorage.getItem('alqove.pending-invitation')).toBe('invite-secret');
    expect(screen.queryByRole('button', { name: /Continue with Google/ })).not.toBeInTheDocument();
  });

  it('successful submit calls register and routes home', async () => {
    registerMock.mockResolvedValue(undefined);
    render(<RegisterPage />);
    fill();
    fireEvent.click(screen.getByRole('button', { name: /Create Account/ }));
    await waitFor(() =>
      expect(registerMock).toHaveBeenCalledWith(
        'Jane Doe',
        'jane@example.com',
        'secret123',
      ),
    );
    expect(push).toHaveBeenCalledWith('/');
  });

  it('blocks submit and shows error if passwords do not match', async () => {
    render(<RegisterPage />);
    fill({ password: 'secret123', confirm: 'different' });
    fireEvent.click(screen.getByRole('button', { name: /Create Account/ }));
    await waitFor(() =>
      expect(screen.getByText('Passwords do not match.')).toBeInTheDocument(),
    );
    expect(registerMock).not.toHaveBeenCalled();
    expect(push).not.toHaveBeenCalled();
  });

  it('shows API error message when register fails', async () => {
    registerMock.mockRejectedValue({ message: 'Email already taken' });
    render(<RegisterPage />);
    fill();
    fireEvent.click(screen.getByRole('button', { name: /Create Account/ }));
    await waitFor(() =>
      expect(screen.getByText('Email already taken')).toBeInTheDocument(),
    );
  });

  it('renders per-field validation errors from the API', async () => {
    registerMock.mockRejectedValue({
      message: 'Validation failed',
      errors: { email: ['has already been taken'] },
    });
    render(<RegisterPage />);
    fill();
    fireEvent.click(screen.getByRole('button', { name: /Create Account/ }));
    await waitFor(() =>
      expect(screen.getByText('has already been taken')).toBeInTheDocument(),
    );
  });
});
