import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { describe, it, expect } from 'vitest';

import { BannerStack, type BannerEntry } from './BannerStack';

function makeEntry(key: string, priority: BannerEntry['priority']): BannerEntry {
  return { key, priority, render: () => <div data-testid={`banner-${key}`}>{key}</div> };
}

describe('BannerStack', () => {
  it('renders nothing when no banners are passed', () => {
    const { container } = render(<BannerStack banners={[]} />);
    expect(container.firstChild).toBeNull();
  });

  it('orders banners by priority (connection > billing > setup > nudge)', () => {
    const banners = [
      makeEntry('nudge', 'nudge'),
      makeEntry('setup', 'setup'),
      makeEntry('connection', 'connection'),
      makeEntry('billing', 'billing'),
    ];

    render(<BannerStack banners={banners} visibleCount={4} />);

    const rendered = screen.getAllByTestId(/^banner-/);
    expect(rendered.map((el) => el.dataset.testid)).toEqual([
      'banner-connection',
      'banner-billing',
      'banner-setup',
      'banner-nudge',
    ]);
  });

  it('hides banners beyond visibleCount and shows the n-more affordance', () => {
    const banners = [
      makeEntry('connection', 'connection'),
      makeEntry('billing', 'billing'),
      makeEntry('setup', 'setup'),
      makeEntry('nudge', 'nudge'),
    ];

    render(<BannerStack banners={banners} />);

    expect(screen.getByTestId('banner-connection')).toBeInTheDocument();
    expect(screen.getByTestId('banner-billing')).toBeInTheDocument();
    expect(screen.queryByTestId('banner-setup')).not.toBeInTheDocument();
    expect(screen.queryByTestId('banner-nudge')).not.toBeInTheDocument();
    expect(screen.getByRole('button', { name: /2 more notifications/i })).toBeInTheDocument();
  });

  it('uses singular "notification" when exactly 1 banner is hidden', () => {
    const banners = [
      makeEntry('a', 'connection'),
      makeEntry('b', 'connection'),
      makeEntry('c', 'nudge'),
    ];

    render(<BannerStack banners={banners} />);

    expect(screen.getByRole('button', { name: /^1 more notification$/i })).toBeInTheDocument();
  });

  it('expands all hidden banners when the affordance is clicked', async () => {
    const user = userEvent.setup();
    const banners = [
      makeEntry('connection', 'connection'),
      makeEntry('billing', 'billing'),
      makeEntry('setup', 'setup'),
      makeEntry('nudge', 'nudge'),
    ];

    render(<BannerStack banners={banners} />);

    await user.click(screen.getByRole('button', { name: /more notifications/i }));

    expect(screen.getByTestId('banner-setup')).toBeInTheDocument();
    expect(screen.getByTestId('banner-nudge')).toBeInTheDocument();
    expect(screen.queryByRole('button', { name: /more notifications/i })).not.toBeInTheDocument();
  });

  it('does not render the affordance when banners.length <= visibleCount', () => {
    const banners = [makeEntry('connection', 'connection'), makeEntry('billing', 'billing')];

    render(<BannerStack banners={banners} visibleCount={2} />);

    expect(screen.queryByRole('button', { name: /more notifications/i })).not.toBeInTheDocument();
  });

  it('preserves insertion order among banners with equal priority (stable sort)', () => {
    const banners = [
      makeEntry('first', 'setup'),
      makeEntry('second', 'setup'),
      makeEntry('third', 'setup'),
    ];

    render(<BannerStack banners={banners} visibleCount={3} />);

    const rendered = screen.getAllByTestId(/^banner-/);
    expect(rendered.map((el) => el.dataset.testid)).toEqual([
      'banner-first',
      'banner-second',
      'banner-third',
    ]);
  });
});
