import { render, screen, fireEvent, within } from '@testing-library/react';
import { ExecutionTimeline } from '../ExecutionTimeline';
import type { NodeExecView } from '../types';

const nodes: NodeExecView[] = [
  {
    nodeId: 'a',
    name: 'Fetch data',
    type: 'trigger.event',
    status: 'success',
    executionOrder: 0,
    executionTimeMs: 1234,
  },
  {
    nodeId: 'b',
    name: 'Transform',
    type: 'transform.set',
    status: 'running',
    executionOrder: 1,
  },
  {
    nodeId: 'c',
    name: 'Notify',
    type: 'action.http',
    status: 'error',
    executionOrder: 2,
    executionTimeMs: 42,
    error: 'Connection refused',
  },
];

describe('ExecutionTimeline', () => {
  it('renders one row per node execution, in order', () => {
    render(<ExecutionTimeline nodeExecutions={nodes} />);
    const rows = screen.getAllByRole('listitem');
    expect(rows).toHaveLength(3);
    expect(rows[0]).toHaveTextContent('Fetch data');
    expect(rows[1]).toHaveTextContent('Transform');
    expect(rows[2]).toHaveTextContent('Notify');
  });

  it('renders a status badge per node with the correct label', () => {
    render(<ExecutionTimeline nodeExecutions={nodes} />);
    expect(screen.getByText('Success')).toBeInTheDocument();
    expect(screen.getByText('Running')).toBeInTheDocument();
    expect(screen.getByText('Error')).toBeInTheDocument();
  });

  it('renders human-readable durations', () => {
    render(<ExecutionTimeline nodeExecutions={nodes} />);
    expect(screen.getByText('1.23s')).toBeInTheDocument();
    expect(screen.getByText('42ms')).toBeInTheDocument();
  });

  it('renders inline error text for error nodes', () => {
    render(<ExecutionTimeline nodeExecutions={nodes} />);
    expect(screen.getByText('Connection refused')).toBeInTheDocument();
  });

  it('shows a live/pulsing indicator only for the running node', () => {
    const { container } = render(<ExecutionTimeline nodeExecutions={nodes} />);
    const live = container.querySelectorAll('[data-live="true"]');
    expect(live).toHaveLength(1);
    const rows = screen.getAllByRole('listitem');
    // running node is the second row (b)
    expect(within(rows[1]).getByText('Running')).toBeInTheDocument();
    expect(rows[1].querySelector('[data-live="true"]')).not.toBeNull();
  });

  it('does not show a live indicator for terminal statuses', () => {
    const terminal: NodeExecView[] = [
      { nodeId: 'a', name: 'A', type: 't', status: 'success', executionOrder: 0 },
      { nodeId: 'b', name: 'B', type: 't', status: 'error', executionOrder: 1 },
      { nodeId: 'c', name: 'C', type: 't', status: 'skipped', executionOrder: 2 },
    ];
    const { container } = render(<ExecutionTimeline nodeExecutions={terminal} />);
    expect(container.querySelectorAll('[data-live="true"]')).toHaveLength(0);
  });

  it('calls onSelectNode when a row is clicked', () => {
    const onSelectNode = jest.fn();
    render(<ExecutionTimeline nodeExecutions={nodes} onSelectNode={onSelectNode} />);
    fireEvent.click(screen.getByText('Transform'));
    expect(onSelectNode).toHaveBeenCalledWith('b');
  });

  it('marks the active node row as selected', () => {
    render(<ExecutionTimeline nodeExecutions={nodes} activeNodeId="c" />);
    const rows = screen.getAllByRole('listitem');
    expect(rows[2]).toHaveAttribute('aria-current', 'true');
    expect(rows[0]).not.toHaveAttribute('aria-current', 'true');
  });

  it('renders an empty state when there are no executions', () => {
    render(<ExecutionTimeline nodeExecutions={[]} />);
    expect(screen.queryAllByRole('listitem')).toHaveLength(0);
    expect(screen.getByText('No node executions')).toBeInTheDocument();
  });
});
