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

function renderBanner(overrides: Partial<AuthContextValue> = {}) {
  const requireLogin = overrides.requireLogin ?? vi.fn();
  const value: AuthContextValue = {
    username: null,
    method: null,
    signOut: vi.fn(),
    requireLogin,
    ...overrides
  };
  render(
    <AuthContext.Provider value={value}>
      <UnauthorizedBanner />
    </AuthContext.Provider>
  );
  return { requireLogin };
}

describe('UnauthorizedBanner', () => {
  afterEach(() => {
    cleanup();
  });

  it('stays hidden until a 401 signal fires', () => {
    renderBanner();

    expect(screen.queryByRole('alert')).toBeNull();

    act(() => notifyUnauthorized());

    expect(screen.getByRole('alert')).toBeTruthy();
    expect(screen.getByText(/session has expired or you are signed out/)).toBeTruthy();
  });

  it('flips the gate to login via requireLogin when navigation is allowed', () => {
    const { requireLogin } = renderBanner();
    act(() => notifyUnauthorized());

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

    expect(requireLogin).toHaveBeenCalledTimes(1);
    expect(screen.queryByRole('alert')).toBeNull();
  });

  it('respects the unsaved-changes guard — a vetoed leave keeps the banner and skips requireLogin', () => {
    const { requireLogin } = renderBanner();
    const unregister = registerNavigationGuard(() => false);
    act(() => notifyUnauthorized());

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

    expect(requireLogin).not.toHaveBeenCalled();
    expect(screen.getByRole('alert')).toBeTruthy();

    unregister();
  });

  it('can be dismissed', () => {
    renderBanner();
    act(() => notifyUnauthorized());

    fireEvent.click(screen.getByRole('button', { name: 'Dismiss' }));

    expect(screen.queryByRole('alert')).toBeNull();
  });
});
