import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ThemeProvider } from '../../theme';
import type { RoleHydration } from '@/services/auth';

const mockReplace = jest.fn();
const mockEnqueue = jest.fn();

jest.mock('next/navigation', () => ({
  ...jest.requireActual('next/navigation'),
  useRouter: () => ({ replace: mockReplace }),
}));
jest.mock('next-intl', () => ({ useLocale: () => 'fa', useTranslations: () => (key: string) => key }));
jest.mock('notistack', () => ({ useSnackbar: () => ({ enqueueSnackbar: mockEnqueue }) }));

let hydration: RoleHydration;
jest.mock('@/services/auth', () => ({ useRoleHydration: () => hydration }));

import RoleGuard from './RoleGuard';

const CHILD = <div data-testid="shell">shell content</div>;

function renderGuard(expected?: 'customer' | 'nurse' | 'admin') {
  render(
    <ThemeProvider>
      <RoleGuard expected={expected}>{CHILD}</RoleGuard>
    </ThemeProvider>,
  );
}

describe('<RoleGuard/>', () => {
  beforeEach(() => {
    mockReplace.mockReset();
    mockEnqueue.mockReset();
  });

  it('renders a neutral splash (not the shell) while /me is loading', () => {
    hydration = { status: 'loading' };
    renderGuard('nurse');
    expect(screen.queryByTestId('shell')).not.toBeInTheDocument();
    expect(mockReplace).not.toHaveBeenCalled();
  });

  it('renders the account-error state (not the shell) when /me failed', () => {
    hydration = { status: 'error', retry: jest.fn(), isRetrying: false };
    renderGuard('nurse');
    expect(screen.queryByTestId('shell')).not.toBeInTheDocument();
    expect(screen.getByText('account_error_title')).toBeInTheDocument();
    expect(mockReplace).not.toHaveBeenCalled();
  });

  it('calls retry when the account-error button is pressed', async () => {
    const retry = jest.fn();
    hydration = { status: 'error', retry, isRetrying: false };
    renderGuard('nurse');
    await userEvent.click(screen.getByText('account_error_retry'));
    expect(retry).toHaveBeenCalledTimes(1);
  });

  it('renders the shell when the session holds the expected role', () => {
    hydration = { status: 'ready', me: { roles: ['nurse'] } as never, appRoles: ['nurse'] };
    renderGuard('nurse');
    expect(screen.getByTestId('shell')).toBeInTheDocument();
    expect(mockReplace).not.toHaveBeenCalled();
  });

  it('lets a dual customer+nurse session into the nurse shell', () => {
    hydration = { status: 'ready', me: { roles: ['customer', 'nurse'] } as never, appRoles: ['customer', 'nurse'] };
    renderGuard('nurse');
    expect(screen.getByTestId('shell')).toBeInTheDocument();
  });

  it('redirects (with a toast) a pure customer away from the nurse shell', async () => {
    hydration = { status: 'ready', me: { roles: ['customer'] } as never, appRoles: ['customer'] };
    renderGuard('nurse');
    expect(screen.queryByTestId('shell')).not.toBeInTheDocument();
    await waitFor(() => expect(mockReplace).toHaveBeenCalledWith('/fa/'));
    expect(mockEnqueue).toHaveBeenCalledWith('guard_denied', { variant: 'warning' });
  });

  it('redirects a role-less user to select-role', async () => {
    hydration = { status: 'ready', me: { roles: [] } as never, appRoles: [] };
    renderGuard('customer');
    await waitFor(() => expect(mockReplace).toHaveBeenCalledWith('/fa/select-role'));
  });

  it('with no expected role (partner scope) renders once /me resolves without redirecting', () => {
    hydration = { status: 'ready', me: { roles: ['customer'] } as never, appRoles: ['customer'] };
    renderGuard(undefined);
    expect(screen.getByTestId('shell')).toBeInTheDocument();
    expect(mockReplace).not.toHaveBeenCalled();
  });
});
