import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { AuthContext, type AuthContextValue } from './AuthGate';
import { registerNavigationGuard } from './navigationGuard';
import { UserMenu } from './UserMenu';

function renderMenu(overrides: Partial<AuthContextValue> = {}) {
  const signOut = overrides.signOut ?? vi.fn();
  const value: AuthContextValue = {
    username: 'admin',
    method: 'local',
    signOut,
    requireLogin: vi.fn(),
    ...overrides
  };
  render(
    <AuthContext.Provider value={value}>
      <UserMenu />
    </AuthContext.Provider>
  );
  return { signOut };
}

describe('UserMenu', () => {
  beforeEach(() => {
    vi.restoreAllMocks();
  });

  afterEach(() => {
    cleanup();
  });

  it('shows the authenticated username', () => {
    renderMenu();
    expect(screen.getByRole('button', { name: /admin/ })).toBeTruthy();
  });

  it('signs out: POSTs logout then calls AuthContext.signOut on 204', async () => {
    const fetchMock = vi.spyOn(window, 'fetch').mockResolvedValue(new Response(null, { status: 204 }));
    const { signOut } = renderMenu();

    fireEvent.click(screen.getByRole('button', { name: /admin/ }));
    fireEvent.click(screen.getByRole('button', { name: 'Sign out' }));

    await waitFor(() => expect(signOut).toHaveBeenCalledTimes(1));
    expect(fetchMock).toHaveBeenCalledWith('/api/v1/auth/logout', expect.objectContaining({ method: 'POST' }));
  });

  it('does not log out when the unsaved-changes guard vetoes', () => {
    const fetchMock = vi.spyOn(window, 'fetch');
    const { signOut } = renderMenu();
    const unregister = registerNavigationGuard(() => false);

    fireEvent.click(screen.getByRole('button', { name: /admin/ }));
    fireEvent.click(screen.getByRole('button', { name: 'Sign out' }));

    expect(fetchMock).not.toHaveBeenCalled();
    expect(signOut).not.toHaveBeenCalled();

    unregister();
  });
});
