// @vitest-environment jsdom
/**
 * Component tests for OverviewTab — the task summary tab. Verifies the title +
 * status badge + piece/priority chips render, the body shows (with the
 * "(no body)" fallback), the FeedbackPanel only appears for completed jobs, and
 * the "🧠 Learned N things" reflection badge surfaces only when the latest
 * reflection actually changed something. Network calls (reflection, delegate
 * runs) are mocked so the tab mounts in isolation.
 */
import '../../../test/dom-setup';
import { describe, it, expect, vi } from 'vitest';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test/render-helpers';
import '../../../i18n';
import type { LocalTask, LatestReflectionForTask } from '../../../api';
import * as api from '../../../api';
import { OverviewTab } from './OverviewTab';

vi.mock('../../../api', async (importOriginal) => {
  const actual = await importOriginal<typeof import('../../../api')>();
  return {
    ...actual,
    getLatestReflectionForTask: vi.fn().mockResolvedValue(null),
    fetchDelegateRuns: vi.fn().mockResolvedValue({ runs: [], subtasks: [] }),
    putFeedback: vi.fn(),
  };
});

const mockedReflection = vi.mocked(api.getLatestReflectionForTask);
const mockedPutFeedback = vi.mocked(api.putFeedback);

function makeTask(overrides: Partial<LocalTask> = {}): LocalTask {
  return {
    id: 10,
    title: 'Quarterly review',
    body: 'Analyze last quarter performance.',
    pieceName: 'analysis',
    profile: 'default',
    outputFormat: 'markdown',
    askPolicy: 'auto',
    priority: 'high',
    state: 'open',
    workspacePath: null,
    createdAt: '2026-06-01T00:00:00Z',
    updatedAt: '2026-06-01T00:00:00Z',
    latestJob: { id: 'job-10', status: 'running' },
    ...overrides,
  };
}

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

describe('OverviewTab', () => {
  it('renders the title, status badge and piece/priority chips', async () => {
    mockedReflection.mockResolvedValue(null);
    renderWithProviders(<OverviewTab task={makeTask()} />);
    expect(screen.getByText('Quarterly review')).toBeInTheDocument();
    expect(screen.getByText('Running')).toBeInTheDocument();
    expect(screen.getByText('analysis')).toBeInTheDocument();
    expect(screen.getByText('high')).toBeInTheDocument();
  });

  it('renders the body, and the (no body) fallback when empty', async () => {
    mockedReflection.mockResolvedValue(null);
    const { rerender } = renderWithProviders(<OverviewTab task={makeTask()} />);
    expect(screen.getByText('Analyze last quarter performance.')).toBeInTheDocument();
    rerender(<OverviewTab task={makeTask({ body: '' })} />);
    expect(screen.getByText('(no body)')).toBeInTheDocument();
  });

  it('hides the feedback panel while the job is still running', async () => {
    mockedReflection.mockResolvedValue(null);
    renderWithProviders(<OverviewTab task={makeTask({ latestJob: { id: 'j', status: 'running' } })} />);
    // FeedbackPanel returns null until the job is complete.
    expect(screen.queryByText('Feedback')).not.toBeInTheDocument();
  });

  it('shows the feedback panel once the job has succeeded', async () => {
    mockedReflection.mockResolvedValue(null);
    renderWithProviders(<OverviewTab task={makeTask({ latestJob: { id: 'j', status: 'succeeded' } })} />);
    expect(screen.getByText('Feedback')).toBeInTheDocument();
  });

  it('does not show the reflection badge when no reflection changed anything', async () => {
    mockedReflection.mockResolvedValue(null);
    renderWithProviders(<OverviewTab task={makeTask()} />);
    await waitFor(() => expect(mockedReflection).toHaveBeenCalled());
    expect(screen.queryByText(/Learned/)).not.toBeInTheDocument();
  });

  it('shows the "🧠 Learned N things" badge when reflection applied changes', async () => {
    mockedReflection.mockResolvedValue(reflection({ memoryChanges: 3 }));
    renderWithProviders(<OverviewTab task={makeTask()} />);
    expect(await screen.findByText(/Learned 3 things/)).toBeInTheDocument();
  });

  describe('feedback tags (i18n)', () => {
    it('localizes a previously-saved tag for display (EN), hiding the stored JA value', async () => {
      mockedReflection.mockResolvedValue(null);
      renderWithProviders(<OverviewTab task={makeTask({
        latestJob: { id: 'j', status: 'succeeded' },
        feedbackRating: 'good',
        feedbackTags: ['出力の精度が高い'],
      })} />);
      // The stored canonical JA string is shown localized; the raw JA is gone.
      expect(screen.getByText('Accurate output')).toBeInTheDocument();
      expect(screen.queryByText('出力の精度が高い')).not.toBeInTheDocument();
    });

    it('falls back to the raw value for an unknown/legacy stored tag', async () => {
      mockedReflection.mockResolvedValue(null);
      renderWithProviders(<OverviewTab task={makeTask({
        latestJob: { id: 'j', status: 'succeeded' },
        feedbackRating: 'good',
        feedbackTags: ['レガシータグ'],
      })} />);
      expect(screen.getByText('レガシータグ')).toBeInTheDocument();
    });

    it('persists the canonical JA value (not the localized label) when a tag is picked', async () => {
      mockedReflection.mockResolvedValue(null);
      mockedPutFeedback.mockResolvedValue(makeTask());
      const user = userEvent.setup();
      renderWithProviders(<OverviewTab task={makeTask({ latestJob: { id: 'j', status: 'succeeded' } })} />);

      await user.click(screen.getByRole('button', { name: /Good/ }));
      // The tag picker shows the localized (EN) label...
      await user.click(screen.getByRole('button', { name: 'Accurate output' }));
      await user.click(screen.getByRole('button', { name: 'Submit' }));

      await waitFor(() => expect(mockedPutFeedback).toHaveBeenCalledTimes(1));
      // ...but the stored value stays the canonical JA string (backend/reflection contract).
      const [, body] = mockedPutFeedback.mock.calls[0];
      expect(body.tags).toEqual(['出力の精度が高い']);
      expect(body.rating).toBe('good');
    });
  });
});
