// @vitest-environment jsdom
/**
 * Component tests for ProgressTab — the execution timeline + raw activity.log
 * view. Verifies the parsed-event count renders, the current-movement line
 * reflects job state (and its pending fallback), the empty timeline label shows
 * when there are no events, the raw activity.log body renders, and the
 * "view full log" control fires its callback.
 */
import '../../../test/dom-setup';
import { describe, it, expect, vi } from 'vitest';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test/render-helpers';
import '../../../i18n';
import type { LocalTask } from '../../../api';
import * as taskDetailHooks from '../../../hooks/useTaskDetail';
import { ProgressTab } from './ProgressTab';

// Control the activity.log content without touching the network.
vi.mock('../../../hooks/useTaskDetail', async (importOriginal) => {
  const actual = await importOriginal<typeof import('../../../hooks/useTaskDetail')>();
  return { ...actual, useLocalActivityLog: vi.fn() };
});

const mockedHook = vi.mocked(taskDetailHooks.useLocalActivityLog);

function mockLog(data: string, isLoading = false) {
  // Only the .data + .isLoading fields are read by ProgressTab.
  mockedHook.mockReturnValue({ data, isLoading } as unknown as ReturnType<
    typeof taskDetailHooks.useLocalActivityLog
  >);
}

function makeTask(overrides: Partial<LocalTask> = {}): LocalTask {
  return {
    id: 1,
    title: 'T',
    body: '',
    pieceName: 'chat',
    profile: 'default',
    outputFormat: 'markdown',
    askPolicy: 'auto',
    priority: 'normal',
    state: 'open',
    workspacePath: null,
    createdAt: '2026-06-01T00:00:00Z',
    updatedAt: '2026-06-01T00:00:00Z',
    latestJob: { id: 'job-1', status: 'running', currentMovement: 'analyze' },
    ...overrides,
  };
}

describe('ProgressTab', () => {
  it('renders the current movement line from the job state', () => {
    mockLog('');
    renderWithProviders(
      <ProgressTab task={makeTask({ latestJob: { id: 'j', status: 'running', currentMovement: 'analyze' } })} onViewFullLog={() => {}} />,
    );
    expect(screen.getByText(/analyze/)).toBeInTheDocument();
  });

  it('shows the empty-timeline label when the log has no events', () => {
    mockLog('');
    renderWithProviders(<ProgressTab task={makeTask()} onViewFullLog={() => {}} />);
    expect(screen.getByText('No progress yet.')).toBeInTheDocument();
    // 0 events counter.
    expect(screen.getByText(/0 events/)).toBeInTheDocument();
  });

  it('renders the raw activity.log content in the pre block', () => {
    mockLog('hello from the activity log');
    const { container } = renderWithProviders(<ProgressTab task={makeTask()} onViewFullLog={() => {}} />);
    // The raw log is rendered verbatim inside the <pre> block (the timeline may
    // also echo parsed lines, so scope the assertion to the pre element).
    const pre = container.querySelector('pre');
    expect(pre).not.toBeNull();
    expect(pre!.textContent).toContain('hello from the activity log');
  });

  it('fires onViewFullLog when the view-full control is clicked', async () => {
    mockLog('some log');
    const user = userEvent.setup();
    const onViewFullLog = vi.fn();
    renderWithProviders(<ProgressTab task={makeTask()} onViewFullLog={onViewFullLog} />);
    await user.click(screen.getByRole('button'));
    expect(onViewFullLog).toHaveBeenCalledTimes(1);
  });
});
