import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { Membership } from '@alqove/api-client';
import StaffLayout from '../layout';

const mocks = vi.hoisted(() => ({
  replace: vi.fn(),
  state: {
    user: null as { id: string } | null,
    isLoading: false,
    memberships: null as Membership[] | null,
    selectedStoreId: null as string | null,
    selectStore: vi.fn(), refreshMemberships: vi.fn(), logout: vi.fn(),
  },
}));
vi.mock('next/navigation', () => ({ useRouter: () => ({ replace: mocks.replace }) }));
vi.mock('@/stores/auth', () => ({ useAuthStore: () => mocks.state }));
vi.mock('@/lib/stores/store-context', () => ({
  useSelectedMembership: () => mocks.state.memberships?.find(m => m.store_id === mocks.state.selectedStoreId),
}));

const membership = (id: string, name: string): Membership => ({
  id: `membership-${id}`, store_id: id, store_name: name,
  role: 'sales', capabilities: ['team.view'], is_exempt: false,
});

beforeEach(() => {
  vi.clearAllMocks();
  Object.assign(mocks.state, { user: null, isLoading: false, memberships: null, selectedStoreId: null });
});

describe('staff access', () => {
  it('offers a retry and a safe exit when membership discovery is unavailable', async () => {
    mocks.state.user = { id: 'u1' };
    mocks.state.refreshMemberships.mockResolvedValue(undefined);
    render(<StaffLayout><p>Private staff content</p></StaffLayout>);
    fireEvent.click(screen.getByRole('button', { name: 'Retry store access' }));
    await waitFor(() => expect(mocks.state.refreshMemberships).toHaveBeenCalledOnce());
    expect(screen.getByRole('link', { name: 'Marketplace' })).toHaveAttribute('href', '/');
    expect(screen.queryByText('Private staff content')).not.toBeInTheDocument();
  });
  it('provides sign out from the staff shell', async () => {
    Object.assign(mocks.state, { user: { id: 'u1' }, memberships: [membership('a', 'North')], selectedStoreId: 'a' });
    mocks.state.logout.mockResolvedValue(undefined);
    render(<StaffLayout><p>Private staff content</p></StaffLayout>);
    fireEvent.click(screen.getByRole('button', { name: 'Sign out' }));
    await waitFor(() => expect(mocks.replace).toHaveBeenCalledWith('/login'));
    expect(mocks.state.logout).toHaveBeenCalledOnce();
  });
  it('offers self-service navigation for any membership, without seller privilege', () => {
    Object.assign(mocks.state, { user: { id: 'u1' }, memberships: [membership('a', 'North')], selectedStoreId: 'a' });
    render(<StaffLayout><p>Private staff content</p></StaffLayout>);
    expect(screen.getByRole('link', { name: 'My schedule' })).toHaveAttribute('href', '/staff/schedule');
    expect(screen.getByRole('link', { name: 'Clock' })).toHaveAttribute('href', '/staff/clock');
    expect(screen.getByRole('link', { name: 'My hours' })).toHaveAttribute('href', '/staff/hours');
    expect(screen.queryByRole('combobox')).not.toBeInTheDocument();
  });
  it('switches between authoritative memberships and ejects stale selections', () => {
    Object.assign(mocks.state, { user: { id: 'u1' }, memberships: [membership('a', 'North'), membership('b', 'South')], selectedStoreId: 'a' });
    const { rerender } = render(<StaffLayout><p>Private staff content</p></StaffLayout>);
    expect(screen.getByText('Private staff content')).toBeInTheDocument();
    fireEvent.change(screen.getByRole('combobox', { name: 'Store' }), { target: { value: 'b' } });
    expect(mocks.state.selectStore).toHaveBeenCalledWith('b');
    mocks.state.memberships = [membership('b', 'South')];
    rerender(<StaffLayout><p>Private staff content</p></StaffLayout>);
    expect(screen.queryByText('Private staff content')).not.toBeInTheDocument();
    expect(screen.getByText(/Choose a store/)).toBeInTheDocument();
  });
  it.each([[null], [[]]])('fails closed for unavailable or empty memberships: %j', (memberships) => {
    Object.assign(mocks.state, { user: { id: 'u1' }, memberships, selectedStoreId: 'old-store' });
    render(<StaffLayout><p>Private staff content</p></StaffLayout>);
    expect(screen.queryByText('Private staff content')).not.toBeInTheDocument();
    expect(screen.getByText(memberships === null ? /Unable to verify store access/ : /No active store memberships/)).toBeInTheDocument();
  });
  it('requires authentication before rendering staff content', () => {
    render(<StaffLayout><p>Private staff content</p></StaffLayout>);
    expect(screen.queryByText('Private staff content')).not.toBeInTheDocument();
    expect(mocks.replace).toHaveBeenCalledWith('/login?next=/staff');
  });
});
