import { describe, it, expect, beforeEach, vi } from 'vitest';
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import { NotificationCenter } from '../NotificationCenter.js';
import { InboxItem } from '../InboxItem.js';
import { BannerStack } from '../BannerStack.js';
import { Announcement } from '../Announcement.js';
import type { BannerItem, NotificationItem } from '../types.js';

const NOW = new Date();
const ITEMS: NotificationItem[] = [
  { id: 'a', title: 'Build succeeded', kind: 'success', read: false, createdAt: NOW },
  { id: 'b', title: '@you in code review', kind: 'mention', read: false, createdAt: NOW },
  { id: 'c', title: 'Billing reminder', kind: 'warning', read: true, createdAt: NOW },
];

describe('NotificationCenter', () => {
  beforeEach(() => cleanup());

  it('renders trigger with unread badge', () => {
    render(<NotificationCenter items={ITEMS} />);
    expect(screen.getByTestId('bl-notification-trigger')).toBeDefined();
    expect(screen.getByTestId('bl-notification-badge').textContent).toBe('2');
  });

  it('caps badge at badgeCap with "+"', () => {
    const many: NotificationItem[] = Array.from({ length: 15 }, (_, i) => ({
      id: `n${i}`,
      title: `Item ${i}`,
      read: false,
    }));
    render(<NotificationCenter items={many} badgeCap={9} />);
    expect(screen.getByTestId('bl-notification-badge').textContent).toBe('9+');
  });

  it('opens dropdown on trigger click and shows items', () => {
    render(<NotificationCenter items={ITEMS} />);
    fireEvent.click(screen.getByTestId('bl-notification-trigger'));
    expect(screen.getByTestId('bl-notification-panel')).toBeDefined();
    expect(screen.getByText('Build succeeded')).toBeDefined();
  });

  it('switches to Unread tab and filters out read items', () => {
    render(<NotificationCenter items={ITEMS} />);
    fireEvent.click(screen.getByTestId('bl-notification-trigger'));
    fireEvent.click(screen.getByTestId('bl-notification-tab-unread'));
    expect(screen.getByText('Build succeeded')).toBeDefined();
    expect(screen.queryByText('Billing reminder')).toBeNull();
  });

  it('Mentions tab shows only kind=mention', () => {
    render(<NotificationCenter items={ITEMS} />);
    fireEvent.click(screen.getByTestId('bl-notification-trigger'));
    fireEvent.click(screen.getByTestId('bl-notification-tab-mentions'));
    expect(screen.getByText('@you in code review')).toBeDefined();
    expect(screen.queryByText('Build succeeded')).toBeNull();
  });

  it('mark-all-read button calls handler', () => {
    const onMarkAllRead = vi.fn();
    render(<NotificationCenter items={ITEMS} onMarkAllRead={onMarkAllRead} />);
    fireEvent.click(screen.getByTestId('bl-notification-trigger'));
    fireEvent.click(screen.getByTestId('bl-notification-mark-all'));
    expect(onMarkAllRead).toHaveBeenCalledOnce();
  });

  it('clicking an item fires onSelect + onMarkRead', () => {
    const onSelect = vi.fn();
    const onMarkRead = vi.fn();
    render(
      <NotificationCenter
        items={ITEMS}
        onSelect={onSelect}
        onMarkRead={onMarkRead}
      />,
    );
    fireEvent.click(screen.getByTestId('bl-notification-trigger'));
    fireEvent.click(screen.getByTestId('bl-inbox-item-trigger-a'));
    expect(onSelect).toHaveBeenCalledOnce();
    expect(onMarkRead).toHaveBeenCalledWith('a');
  });

  it('empty state when no items match the tab', () => {
    render(<NotificationCenter items={[]} />);
    fireEvent.click(screen.getByTestId('bl-notification-trigger'));
    expect(screen.getByTestId('bl-notification-empty')).toBeDefined();
  });

  it('Escape closes the dropdown', () => {
    render(<NotificationCenter items={ITEMS} />);
    fireEvent.click(screen.getByTestId('bl-notification-trigger'));
    fireEvent.keyDown(document, { key: 'Escape' });
    expect(screen.queryByTestId('bl-notification-panel')).toBeNull();
  });
});

describe('InboxItem', () => {
  beforeEach(() => cleanup());

  it('renders title, kind data-attr, and tone glyph', () => {
    render(<InboxItem item={{ id: 'x', title: 'Hello', kind: 'warning' }} />);
    expect(screen.getByText('Hello')).toBeDefined();
    expect(screen.getByTestId('bl-inbox-item-x').getAttribute('data-kind')).toBe('warning');
  });
  it('emits data-read=false when item.read is falsy', () => {
    render(<InboxItem item={{ id: 'x', title: 'X', read: false }} />);
    expect(screen.getByTestId('bl-inbox-item-x').getAttribute('data-read')).toBe('false');
  });
});

const BANNERS: BannerItem[] = [
  { id: 'b1', kind: 'announcement', title: 'New feature', body: 'try it' },
  { id: 'b2', kind: 'warning', title: 'Maintenance Friday' },
  { id: 'b3', kind: 'info', title: 'Tip', cta: { label: 'Learn more', href: '#' } },
  { id: 'b4', kind: 'info', title: 'Extra' },
];

describe('BannerStack', () => {
  beforeEach(() => cleanup());

  it('renders all banners up to maxVisible and collapses the rest', () => {
    render(<BannerStack banners={BANNERS} maxVisible={3} />);
    expect(screen.getByTestId('bl-banner-b1')).toBeDefined();
    expect(screen.getByTestId('bl-banner-b3')).toBeDefined();
    expect(screen.queryByTestId('bl-banner-b4')).toBeNull();
    expect(screen.getByTestId('bl-banner-expand').textContent).toBe('+1 more');
  });

  it('expand button shows hidden banners', () => {
    render(<BannerStack banners={BANNERS} maxVisible={2} />);
    fireEvent.click(screen.getByTestId('bl-banner-expand'));
    expect(screen.getByTestId('bl-banner-b3')).toBeDefined();
    expect(screen.getByTestId('bl-banner-b4')).toBeDefined();
  });

  it('dismiss button fires onDismiss with id', () => {
    const onDismiss = vi.fn();
    render(<BannerStack banners={BANNERS} onDismiss={onDismiss} />);
    fireEvent.click(screen.getByTestId('bl-banner-dismiss-b1'));
    expect(onDismiss).toHaveBeenCalledWith('b1');
  });

  it('cta callback overrides href navigation', () => {
    const onSelect = vi.fn();
    render(
      <BannerStack
        banners={[
          {
            id: 'x',
            kind: 'info',
            title: 'T',
            cta: { label: 'Go', href: '#', onSelect },
          },
        ]}
      />,
    );
    fireEvent.click(screen.getByTestId('bl-banner-cta-x'));
    expect(onSelect).toHaveBeenCalledOnce();
  });

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

describe('Announcement', () => {
  beforeEach(() => cleanup());
  it('renders tag + content + cta', () => {
    render(
      <Announcement tag="BETA" cta={{ label: 'Try', href: '#' }}>
        Streaming chat is here.
      </Announcement>,
    );
    expect(screen.getByText('BETA')).toBeDefined();
    expect(screen.getByText('Streaming chat is here.')).toBeDefined();
    expect(screen.getByTestId('bl-announcement-cta')).toBeDefined();
  });
});
