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

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

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

const socialRedirectMock = vi.fn();
vi.mock('@/lib/api', () => ({
  api: {
    auth: { socialRedirect: (...a: unknown[]) => socialRedirectMock(...a) },
  },
}));

const originalLocation = window.location;

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

  beforeEach(() => {
    sessionStorage.clear();
    Object.defineProperty(window, 'location', { value: originalLocation, writable: true, configurable: true });
    window.history.replaceState(null, '', '/login');
    push.mockClear();
    loginMock.mockReset();
    socialRedirectMock.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.each([
    ['invite-secret', 'https://evil.test', '/invitations/accept'],
    [null, '/staff', '/staff'],
    [null, '//evil.test', '/'],
  ])('continues only to trusted destinations after login', async (token, next, destination) => {
    if (token) sessionStorage.setItem('alqove.pending-invitation', token);
    window.history.replaceState(null, '', `/login?next=${encodeURIComponent(next!)}`);
    loginMock.mockResolvedValue(undefined);
    render(<LoginPage />);
    fireEvent.change(screen.getByLabelText('Email'), { target: { value: 'invited@example.com' } });
    fireEvent.change(screen.getByLabelText('Password'), { target: { value: 'secret123' } });
    fireEvent.click(screen.getByRole('button', { name: 'Sign In' }));
    await waitFor(() => expect(push).toHaveBeenCalledWith(destination));
    expect(sessionStorage.getItem('alqove.pending-invitation')).toBe(token);
  });

  it('successful submit calls login with credentials and routes home', async () => {
    loginMock.mockResolvedValue(undefined);
    render(<LoginPage />);
    fireEvent.change(screen.getByLabelText('Email'), {
      target: { value: 'jane@example.com' },
    });
    fireEvent.change(screen.getByLabelText('Password'), {
      target: { value: 'secret123' },
    });
    fireEvent.click(screen.getByRole('button', { name: /Sign In/ }));
    await waitFor(() =>
      expect(loginMock).toHaveBeenCalledWith('jane@example.com', 'secret123'),
    );
    expect(push).toHaveBeenCalledWith('/');
  });

  it('shows the API error message when login fails', async () => {
    loginMock.mockRejectedValue({ message: 'Invalid credentials' });
    render(<LoginPage />);
    fireEvent.change(screen.getByLabelText('Email'), {
      target: { value: 'jane@example.com' },
    });
    fireEvent.change(screen.getByLabelText('Password'), {
      target: { value: 'wrong' },
    });
    fireEvent.click(screen.getByRole('button', { name: /Sign In/ }));
    await waitFor(() =>
      expect(screen.getByText('Invalid credentials')).toBeInTheDocument(),
    );
    expect(push).not.toHaveBeenCalled();
  });

  it('renders per-field validation errors from the API', async () => {
    loginMock.mockRejectedValue({
      message: 'Validation failed',
      errors: { email: ['must be a valid email'] },
    });
    render(<LoginPage />);
    fireEvent.change(screen.getByLabelText('Email'), {
      target: { value: 'jane@example.com' },
    });
    fireEvent.change(screen.getByLabelText('Password'), {
      target: { value: 'secret' },
    });
    fireEvent.click(screen.getByRole('button', { name: /Sign In/ }));
    await waitFor(() =>
      expect(screen.getByText('must be a valid email')).toBeInTheDocument(),
    );
  });

  it('Google button calls socialRedirect and navigates to the returned URL', async () => {
    socialRedirectMock.mockResolvedValue({ url: 'https://accounts.google.com/oauth' });
    Object.defineProperty(window, 'location', {
      value: { ...originalLocation, href: '' },
      writable: true,
    });
    render(<LoginPage />);
    fireEvent.click(screen.getByRole('button', { name: /Continue with Google/ }));
    await waitFor(() => expect(socialRedirectMock).toHaveBeenCalledWith('google'));
    expect(window.location.href).toBe('https://accounts.google.com/oauth');
  });

  it('shows a connection error if socialRedirect rejects', async () => {
    socialRedirectMock.mockRejectedValue(new Error('boom'));
    render(<LoginPage />);
    fireEvent.click(screen.getByRole('button', { name: /Continue with Apple/ }));
    await waitFor(() =>
      expect(screen.getByText('Failed to connect to apple.')).toBeInTheDocument(),
    );
  });
});
