// @vitest-environment jsdom
/**
 * Component tests for ReflectionBadge — the "🧠 Learned N things" pill on the
 * Overview tab. The badge only appears when the latest reflection for a task
 * actually changed something. Verifies: hidden when no reflection; hidden when
 * outcome is abstained/failed; hidden when 0 memory changes and no piece edit;
 * shown with correct singular/plural label; shows "+ piece edit" suffix; links
 * to the memory-learning settings section anchored at the snapshot.
 */
import '../../test/dom-setup';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { screen, waitFor } from '@testing-library/react';
import { renderWithProviders } from '../../test/render-helpers';
import type { LatestReflectionForTask } from '../../api';
import * as api from '../../api';
import { ReflectionBadge } from './ReflectionBadge';

// Mock just the one network call the badge makes.
vi.mock('../../api', async (importOriginal) => {
  const actual = await importOriginal<typeof import('../../api')>();
  return { ...actual, getLatestReflectionForTask: vi.fn() };
});

const mockedGet = vi.mocked(api.getLatestReflectionForTask);

function reflection(overrides: Partial<LatestReflectionForTask> = {}): LatestReflectionForTask {
  return {
    snapshotId: 'snap-123',
    outcome: 'applied',
    memoryChanges: 2,
    pieceEdited: false,
    ...overrides,
  };
}

describe('ReflectionBadge', () => {
  beforeEach(() => {
    mockedGet.mockReset();
  });

  it('renders nothing when there is no reflection', async () => {
    mockedGet.mockResolvedValue(null);
    const { container } = renderWithProviders(<ReflectionBadge taskId={1} />);
    // Give the (resolved-null) query a tick; badge must stay empty.
    await waitFor(() => expect(mockedGet).toHaveBeenCalled());
    expect(container.querySelector('a')).toBeNull();
  });

  it('renders nothing when the outcome is abstained', async () => {
    mockedGet.mockResolvedValue(reflection({ outcome: 'abstained', memoryChanges: 3 }));
    const { container } = renderWithProviders(<ReflectionBadge taskId={2} />);
    await waitFor(() => expect(mockedGet).toHaveBeenCalled());
    expect(container.querySelector('a')).toBeNull();
  });

  it('renders nothing when 0 memory changes and no piece edit', async () => {
    mockedGet.mockResolvedValue(reflection({ memoryChanges: 0, pieceEdited: false }));
    const { container } = renderWithProviders(<ReflectionBadge taskId={3} />);
    await waitFor(() => expect(mockedGet).toHaveBeenCalled());
    expect(container.querySelector('a')).toBeNull();
  });

  it('shows the pluralized label when reflection changed multiple things', async () => {
    mockedGet.mockResolvedValue(reflection({ memoryChanges: 2 }));
    renderWithProviders(<ReflectionBadge taskId={4} />);
    const link = await screen.findByRole('link');
    expect(link).toHaveTextContent('🧠 Learned 2 things');
    expect(link).not.toHaveTextContent('piece edit');
  });

  it('uses the singular "thing" for exactly one memory change', async () => {
    mockedGet.mockResolvedValue(reflection({ memoryChanges: 1 }));
    renderWithProviders(<ReflectionBadge taskId={5} />);
    const link = await screen.findByRole('link');
    expect(link).toHaveTextContent('Learned 1 thing');
    expect(link).not.toHaveTextContent('things');
  });

  it('appends "+ piece edit" and links to the snapshot when a piece was edited', async () => {
    mockedGet.mockResolvedValue(reflection({ memoryChanges: 0, pieceEdited: true }));
    renderWithProviders(<ReflectionBadge taskId={6} />);
    const link = await screen.findByRole('link');
    expect(link).toHaveTextContent('Learned 0 things + piece edit');
    expect(link).toHaveAttribute(
      'href',
      '?page=settings&section=memory-learning#snapshot-snap-123',
    );
  });
});
