/**
 * Tests for EventList Component
 */

import { render, screen, fireEvent } from '@testing-library/react';
import { EventList } from '@/components/events/EventList';
import { EventType, type ProjectEvent } from '@/lib/types/events';

/**
 * Helper to create mock event
 */
function createMockEvent(overrides: Partial<ProjectEvent> = {}): ProjectEvent {
  return {
    id: `event-${Math.random().toString(36).substr(2, 9)}`,
    type: EventType.AGENT_MESSAGE,
    timestamp: new Date().toISOString(),
    project_id: 'project-123',
    actor_id: 'agent-456',
    actor_type: 'agent',
    payload: { message: 'Test message' },
    ...overrides,
  };
}

describe('EventList', () => {
  const mockOnEventClick = jest.fn();

  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('empty state', () => {
    it('shows empty message when no events', () => {
      render(<EventList events={[]} />);

      expect(screen.getByText('No events yet')).toBeInTheDocument();
    });

    it('shows custom empty message', () => {
      render(<EventList events={[]} emptyMessage="Waiting for activity..." />);

      expect(screen.getByText('Waiting for activity...')).toBeInTheDocument();
    });
  });

  describe('header', () => {
    it('shows header by default', () => {
      render(<EventList events={[]} />);

      expect(screen.getByText('Activity Feed')).toBeInTheDocument();
    });

    it('shows custom title', () => {
      render(<EventList events={[]} title="Project Events" />);

      expect(screen.getByText('Project Events')).toBeInTheDocument();
    });

    it('hides header when showHeader is false', () => {
      render(<EventList events={[]} showHeader={false} />);

      expect(screen.queryByText('Activity Feed')).not.toBeInTheDocument();
    });

    it('shows event count in header', () => {
      const events = [
        createMockEvent({ id: 'event-1' }),
        createMockEvent({ id: 'event-2' }),
        createMockEvent({ id: 'event-3' }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('3 events')).toBeInTheDocument();
    });

    it('shows singular "event" for one event', () => {
      const events = [createMockEvent()];

      render(<EventList events={events} />);

      expect(screen.getByText('1 event')).toBeInTheDocument();
    });
  });

  describe('event display', () => {
    it('displays agent events correctly', () => {
      const events = [
        createMockEvent({
          type: EventType.AGENT_MESSAGE,
          payload: { message: 'Processing task...' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Agent Message')).toBeInTheDocument();
      expect(screen.getByText('Processing task...')).toBeInTheDocument();
    });

    it('displays issue events correctly', () => {
      const events = [
        createMockEvent({
          type: EventType.ISSUE_CREATED,
          payload: { title: 'Fix login bug' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Issue Created')).toBeInTheDocument();
      expect(screen.getByText('Fix login bug')).toBeInTheDocument();
    });

    it('displays sprint events correctly', () => {
      const events = [
        createMockEvent({
          type: EventType.SPRINT_STARTED,
          payload: { sprint_name: 'Sprint 1' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Sprint Started')).toBeInTheDocument();
      expect(screen.getByText(/Sprint "Sprint 1" started/)).toBeInTheDocument();
    });

    it('displays approval events correctly', () => {
      const events = [
        createMockEvent({
          type: EventType.APPROVAL_REQUESTED,
          payload: { description: 'Need approval for deployment' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Approval Requested')).toBeInTheDocument();
      expect(screen.getByText('Need approval for deployment')).toBeInTheDocument();
    });

    it('displays workflow events correctly', () => {
      const events = [
        createMockEvent({
          type: EventType.WORKFLOW_COMPLETED,
          payload: { duration_seconds: 120 },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Workflow Completed')).toBeInTheDocument();
      expect(screen.getByText('Completed in 120s')).toBeInTheDocument();
    });

    it('displays actor type', () => {
      const events = [
        createMockEvent({ actor_type: 'agent' }),
        createMockEvent({ actor_type: 'user', id: 'event-2' }),
        createMockEvent({ actor_type: 'system', id: 'event-3' }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Agent')).toBeInTheDocument();
      expect(screen.getByText('User')).toBeInTheDocument();
      expect(screen.getByText('System')).toBeInTheDocument();
    });
  });

  describe('event sorting', () => {
    it('sorts events by timestamp, newest first', () => {
      const events = [
        createMockEvent({
          id: 'older',
          timestamp: '2024-01-01T10:00:00Z',
          payload: { message: 'Older event' },
        }),
        createMockEvent({
          id: 'newer',
          timestamp: '2024-01-01T12:00:00Z',
          payload: { message: 'Newer event' },
        }),
      ];

      render(<EventList events={events} />);

      const eventTexts = screen.getAllByText(/event$/i);
      // The "Newer event" should appear first in the DOM
      const newerIndex = eventTexts.findIndex((el) =>
        el.closest('[class*="flex gap-3"]')?.textContent?.includes('Newer')
      );
      const olderIndex = eventTexts.findIndex((el) =>
        el.closest('[class*="flex gap-3"]')?.textContent?.includes('Older')
      );

      // In a sorted list, newer should have lower index
      expect(newerIndex).toBeLessThan(olderIndex);
    });
  });

  describe('payload expansion', () => {
    it('shows expand button when showPayloads is true', () => {
      const events = [createMockEvent()];

      render(<EventList events={events} showPayloads />);

      // Should have a chevron button
      expect(screen.getByRole('button')).toBeInTheDocument();
    });

    it('expands payload on click', async () => {
      const events = [
        createMockEvent({
          payload: { custom_field: 'custom_value' },
        }),
      ];

      render(<EventList events={events} showPayloads />);

      const eventItem = screen.getByText('Agent Message').closest('[class*="flex gap-3"]');
      expect(eventItem).toBeInTheDocument();

      fireEvent.click(eventItem!);

      // Should show the JSON payload
      expect(screen.getByText(/"custom_field"/)).toBeInTheDocument();
      expect(screen.getByText(/"custom_value"/)).toBeInTheDocument();
    });
  });

  describe('event click', () => {
    it('calls onEventClick when event is clicked', () => {
      const events = [createMockEvent({ id: 'test-event' })];

      render(<EventList events={events} onEventClick={mockOnEventClick} />);

      const eventItem = screen.getByText('Agent Message').closest('[class*="flex gap-3"]');
      fireEvent.click(eventItem!);

      expect(mockOnEventClick).toHaveBeenCalledWith(expect.objectContaining({ id: 'test-event' }));
    });

    it('makes event item focusable when clickable', () => {
      const events = [createMockEvent()];

      render(<EventList events={events} onEventClick={mockOnEventClick} />);

      const eventItem = screen.getByText('Agent Message').closest('[class*="flex gap-3"]');
      expect(eventItem).toHaveAttribute('tabIndex', '0');
    });

    it('handles keyboard activation', () => {
      const events = [createMockEvent({ id: 'keyboard-event' })];

      render(<EventList events={events} onEventClick={mockOnEventClick} />);

      const eventItem = screen.getByText('Agent Message').closest('[class*="flex gap-3"]');
      fireEvent.keyDown(eventItem!, { key: 'Enter' });

      expect(mockOnEventClick).toHaveBeenCalledWith(
        expect.objectContaining({ id: 'keyboard-event' })
      );
    });

    it('handles space key activation', () => {
      const events = [createMockEvent({ id: 'space-event' })];

      render(<EventList events={events} onEventClick={mockOnEventClick} />);

      const eventItem = screen.getByText('Agent Message').closest('[class*="flex gap-3"]');
      fireEvent.keyDown(eventItem!, { key: ' ' });

      expect(mockOnEventClick).toHaveBeenCalledWith(expect.objectContaining({ id: 'space-event' }));
    });
  });

  describe('scrolling', () => {
    it('applies maxHeight style', () => {
      const events = [createMockEvent()];

      const { container } = render(<EventList events={events} maxHeight={300} />);

      const scrollContainer = container.querySelector('.overflow-y-auto');
      expect(scrollContainer).toHaveStyle({ maxHeight: '300px' });
    });

    it('accepts string maxHeight', () => {
      const events = [createMockEvent()];

      const { container } = render(<EventList events={events} maxHeight="50vh" />);

      const scrollContainer = container.querySelector('.overflow-y-auto');
      expect(scrollContainer).toHaveStyle({ maxHeight: '50vh' });
    });
  });

  describe('className prop', () => {
    it('applies custom className', () => {
      const { container } = render(<EventList events={[]} className="custom-event-list" />);

      expect(container.querySelector('.custom-event-list')).toBeInTheDocument();
    });
  });

  describe('different event types', () => {
    it('handles agent spawned event', () => {
      const events = [
        createMockEvent({
          type: EventType.AGENT_SPAWNED,
          payload: { agent_name: 'Product Owner', role: 'po' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Agent Spawned')).toBeInTheDocument();
      expect(screen.getByText(/Product Owner spawned as po/)).toBeInTheDocument();
    });

    it('handles agent terminated event', () => {
      const events = [
        createMockEvent({
          type: EventType.AGENT_TERMINATED,
          payload: { termination_reason: 'Task completed' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Agent Terminated')).toBeInTheDocument();
      expect(screen.getByText('Task completed')).toBeInTheDocument();
    });

    it('handles workflow failed event', () => {
      const events = [
        createMockEvent({
          type: EventType.WORKFLOW_FAILED,
          payload: { error_message: 'Build failed' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Workflow Failed')).toBeInTheDocument();
      expect(screen.getByText('Build failed')).toBeInTheDocument();
    });

    it('handles workflow step completed event', () => {
      const events = [
        createMockEvent({
          type: EventType.WORKFLOW_STEP_COMPLETED,
          payload: { step_name: 'Build', step_number: 2, total_steps: 5 },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Step Completed')).toBeInTheDocument();
      expect(screen.getByText(/Step 2\/5: Build/)).toBeInTheDocument();
    });

    it('handles approval denied event', () => {
      const events = [
        createMockEvent({
          type: EventType.APPROVAL_DENIED,
          payload: { reason: 'Security review needed' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Approval Denied')).toBeInTheDocument();
      expect(screen.getByText(/Denied: Security review needed/)).toBeInTheDocument();
    });

    it('handles agent status changed event', () => {
      const events = [
        createMockEvent({
          type: EventType.AGENT_STATUS_CHANGED,
          payload: { previous_status: 'idle', new_status: 'working' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Status Changed')).toBeInTheDocument();
      expect(screen.getByText(/Status: idle -> working/)).toBeInTheDocument();
    });

    it('handles issue updated event', () => {
      const events = [
        createMockEvent({
          type: EventType.ISSUE_UPDATED,
          payload: { issue_id: 'ISSUE-42' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Issue Updated')).toBeInTheDocument();
      expect(screen.getByText(/Issue ISSUE-42 updated/)).toBeInTheDocument();
    });

    it('handles issue assigned event with assignee', () => {
      const events = [
        createMockEvent({
          type: EventType.ISSUE_ASSIGNED,
          payload: { assignee_name: 'John Doe' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Issue Assigned')).toBeInTheDocument();
      expect(screen.getByText(/Assigned to John Doe/)).toBeInTheDocument();
    });

    it('handles issue assigned event without assignee', () => {
      const events = [
        createMockEvent({
          type: EventType.ISSUE_ASSIGNED,
          payload: {},
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Issue Assigned')).toBeInTheDocument();
      expect(screen.getByText(/Issue assignment changed/)).toBeInTheDocument();
    });

    it('handles issue closed event', () => {
      const events = [
        createMockEvent({
          type: EventType.ISSUE_CLOSED,
          payload: { resolution: 'Fixed in PR #123' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Issue Closed')).toBeInTheDocument();
      expect(screen.getByText(/Closed: Fixed in PR #123/)).toBeInTheDocument();
    });

    it('handles approval granted event', () => {
      const events = [
        createMockEvent({
          type: EventType.APPROVAL_GRANTED,
          payload: {},
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Approval Granted')).toBeInTheDocument();
      expect(screen.getByText('Approval granted')).toBeInTheDocument();
    });

    it('handles workflow started event', () => {
      const events = [
        createMockEvent({
          type: EventType.WORKFLOW_STARTED,
          payload: { workflow_type: 'CI/CD' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Workflow Started')).toBeInTheDocument();
      expect(screen.getByText(/CI\/CD workflow started/)).toBeInTheDocument();
    });

    it('handles sprint completed event', () => {
      const events = [
        createMockEvent({
          type: EventType.SPRINT_COMPLETED,
          payload: { sprint_name: 'Sprint 2' },
        }),
      ];

      render(<EventList events={events} />);

      expect(screen.getByText('Sprint Completed')).toBeInTheDocument();
      expect(screen.getByText(/Sprint "Sprint 2" completed/)).toBeInTheDocument();
    });

    it('handles project created event', () => {
      const events = [
        createMockEvent({
          type: EventType.PROJECT_CREATED,
          payload: {},
        }),
      ];

      const { container } = render(<EventList events={events} />);

      // Project events use teal color styling
      expect(container.querySelector('.bg-teal-100')).toBeInTheDocument();
      // And the event count should show
      expect(screen.getByText('1 event')).toBeInTheDocument();
    });
  });
});
