import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { useRouter } from 'next/navigation';
import LoginPage from '../app/(auth)/login/page';
import api from '../lib/api';

// Mock Next.js router
jest.mock('next/navigation', () => ({
  useRouter: jest.fn(),
  useSearchParams: jest.fn(),
}));

// Mock API
jest.mock('../lib/api', () => ({
  __esModule: true,
  default: {
    getToken: jest.fn(),
    checkEmail: jest.fn(),
    requestOTP: jest.fn(),
    saveEmail: jest.fn(),
  },
}));

describe('LoginPage', () => {
  const mockPush = jest.fn();
  const mockReplace = jest.fn();

  beforeEach(() => {
    jest.clearAllMocks();
    (useRouter as jest.Mock).mockReturnValue({
      push: mockPush,
      replace: mockReplace,
    });
    (api.getToken as jest.Mock).mockResolvedValue(null);
  });

  it('renders login form correctly', () => {
    render(<LoginPage />);

    expect(screen.getByText('Welcome to WellNuo')).toBeInTheDocument();
    expect(screen.getByLabelText('Email address')).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /get verification code/i })).toBeInTheDocument();
  });

  it('redirects to dashboard if already authenticated', async () => {
    (api.getToken as jest.Mock).mockResolvedValue('mock-token');

    render(<LoginPage />);

    await waitFor(() => {
      expect(mockReplace).toHaveBeenCalledWith('/dashboard');
    });
  });

  it('shows error for empty email', async () => {
    render(<LoginPage />);

    const form = screen.getByRole('button', { name: /get verification code/i }).closest('form');
    fireEvent.submit(form!);

    await waitFor(() => {
      expect(screen.getByText('Email is required')).toBeInTheDocument();
    });
  });

  it('shows error for invalid email format', async () => {
    render(<LoginPage />);

    const emailInput = screen.getByLabelText('Email address');
    const form = screen.getByRole('button', { name: /get verification code/i }).closest('form');

    fireEvent.change(emailInput, { target: { value: 'invalid-email' } });
    fireEvent.submit(form!);

    await waitFor(() => {
      expect(screen.getByText('Please enter a valid email address')).toBeInTheDocument();
    });
  });

  it('validates correct email format', async () => {
    (api.checkEmail as jest.Mock).mockResolvedValue({ ok: true, data: { exists: true } });
    (api.requestOTP as jest.Mock).mockResolvedValue({ ok: true });

    render(<LoginPage />);

    const emailInput = screen.getByLabelText('Email address');
    const submitButton = screen.getByRole('button', { name: /get verification code/i });

    fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(api.checkEmail).toHaveBeenCalledWith('test@example.com');
      expect(api.requestOTP).toHaveBeenCalledWith('test@example.com');
    });
  });

  it('navigates to verify-otp on successful OTP request', async () => {
    (api.checkEmail as jest.Mock).mockResolvedValue({ ok: true, data: { exists: true } });
    (api.requestOTP as jest.Mock).mockResolvedValue({ ok: true });
    (api.saveEmail as jest.Mock).mockResolvedValue(undefined);

    render(<LoginPage />);

    const emailInput = screen.getByLabelText('Email address');
    const form = screen.getByRole('button', { name: /get verification code/i }).closest('form');

    fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
    fireEvent.submit(form!);

    await waitFor(() => {
      expect(mockPush).toHaveBeenCalledWith(
        expect.stringMatching(/\/verify-otp\?email=test(%40|@)example\.com/)
      );
    });
  });

  it('shows loading state during OTP request', async () => {
    (api.checkEmail as jest.Mock).mockResolvedValue({ ok: true, data: { exists: true } });
    (api.requestOTP as jest.Mock).mockImplementation(() => new Promise(() => {})); // Never resolves

    render(<LoginPage />);

    const emailInput = screen.getByLabelText('Email address');
    const submitButton = screen.getByRole('button', { name: /get verification code/i });

    fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(screen.getByText('Sending code...')).toBeInTheDocument();
      expect(submitButton).toBeDisabled();
    });
  });

  it('shows error message on failed OTP request', async () => {
    (api.checkEmail as jest.Mock).mockResolvedValue({ ok: true, data: { exists: true } });
    (api.requestOTP as jest.Mock).mockResolvedValue({
      ok: false,
      error: { message: 'Failed to send OTP' },
    });

    render(<LoginPage />);

    const emailInput = screen.getByLabelText('Email address');
    const submitButton = screen.getByRole('button', { name: /get verification code/i });

    fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(screen.getByText('Failed to send OTP')).toBeInTheDocument();
    });
  });

  it('shows invite code field when toggled', () => {
    render(<LoginPage />);

    const inviteToggle = screen.getByText('Have an invite code?');
    fireEvent.click(inviteToggle);

    expect(screen.getByLabelText('Invite code (optional)')).toBeInTheDocument();
  });

  it('includes invite code in navigation params when provided', async () => {
    (api.checkEmail as jest.Mock).mockResolvedValue({ ok: true, data: { exists: false } });
    (api.requestOTP as jest.Mock).mockResolvedValue({ ok: true });
    (api.saveEmail as jest.Mock).mockResolvedValue(undefined);

    render(<LoginPage />);

    const emailInput = screen.getByLabelText('Email address');
    const inviteToggle = screen.getByText('Have an invite code?');
    fireEvent.click(inviteToggle);

    const inviteInput = screen.getByLabelText('Invite code (optional)');
    const submitButton = screen.getByRole('button', { name: /get verification code/i });

    fireEvent.change(emailInput, { target: { value: 'new@example.com' } });
    fireEvent.change(inviteInput, { target: { value: 'INVITE123' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(mockPush).toHaveBeenCalledWith(
        expect.stringContaining('inviteCode=INVITE123')
      );
    });
  });

  it('handles network errors gracefully', async () => {
    (api.checkEmail as jest.Mock).mockRejectedValue(new Error('Network error'));

    render(<LoginPage />);

    const emailInput = screen.getByLabelText('Email address');
    const submitButton = screen.getByRole('button', { name: /get verification code/i });

    fireEvent.change(emailInput, { target: { value: 'test@example.com' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(screen.getByText('Network error. Please check your connection.')).toBeInTheDocument();
    });
  });

  it('trims and lowercases email before submission', async () => {
    (api.checkEmail as jest.Mock).mockResolvedValue({ ok: true, data: { exists: true } });
    (api.requestOTP as jest.Mock).mockResolvedValue({ ok: true });

    render(<LoginPage />);

    const emailInput = screen.getByLabelText('Email address');
    const submitButton = screen.getByRole('button', { name: /get verification code/i });

    fireEvent.change(emailInput, { target: { value: '  TEST@EXAMPLE.COM  ' } });
    fireEvent.click(submitButton);

    await waitFor(() => {
      expect(api.checkEmail).toHaveBeenCalledWith('test@example.com');
      expect(api.requestOTP).toHaveBeenCalledWith('test@example.com');
    });
  });
});
