import { afterEach, describe, expect, it, vi } from 'vitest';
import api from '../../api';
import {
  aiChatApi,
  resolveImportPreflight,
  type ImportPreflightResolveUpdate,
} from './api';

describe('AI chat API adapter', () => {
  afterEach(() => vi.restoreAllMocks());

  it('unwraps the backend success/data response', async () => {
    vi.spyOn(api, 'get').mockResolvedValue({
      success: true,
      data: [
        {
          id: 1,
          title: '会话',
          lockedSkillKey: null,
          createdAt: '2026-07-23T00:00:00.000Z',
          updatedAt: '2026-07-23T00:00:00.000Z',
          lastMessageAt: null,
        },
      ],
    });

    await expect(aiChatApi.listConversations()).resolves.toMatchObject([{ id: 1, title: '会话' }]);
  });

  it('loads every history page in chronological page order', async () => {
    vi.spyOn(api, 'get')
      .mockResolvedValueOnce({
        success: true,
        data: { items: [{ id: 1 }], total: 101, page: 1, limit: 100 },
      })
      .mockResolvedValueOnce({
        success: true,
        data: { items: [{ id: 101 }], total: 101, page: 2, limit: 100 },
      });

    const page = await aiChatApi.listMessages(3);
    expect(page.items.map((item) => item.id)).toEqual([1, 101]);
  });

  it('confirmReviewStep posts to the per-section confirm endpoint', async () => {
    const updated = {
      id: 'review-1',
      title: '批量导入',
      status: 'pending',
      sections: [
        { key: 'students', type: 'students', title: '学生', status: 'submitted' },
      ],
    };
    vi.spyOn(api, 'post').mockResolvedValue({ success: true, data: updated });

    await expect(aiChatApi.confirmReviewStep('review-1', 'students')).resolves.toEqual(updated);
    expect(api.post).toHaveBeenCalledWith(
      '/ai/chat/reviews/review-1/steps/students/confirm',
    );
  });

  it('confirmReviewGroup posts to the per-type confirm endpoint', async () => {
    const updated = {
      id: 'review-1',
      title: '批量导入',
      status: 'pending',
      sections: [
        { key: 'checkins_a', type: 'checkins', title: '入住A', status: 'submitted' },
      ],
    };
    vi.spyOn(api, 'post').mockResolvedValue({ success: true, data: updated });

    await expect(aiChatApi.confirmReviewGroup('review-1', 'checkins')).resolves.toEqual(updated);
    expect(api.post).toHaveBeenCalledWith(
      '/ai/chat/reviews/review-1/types/checkins/confirm',
    );
  });

  it('resolveImportPreflight 流式解析 SSE 事件并回调预检卡/向导更新', async () => {
    const encoder = new TextEncoder();
    const body = new ReadableStream<Uint8Array>({
      start(controller) {
        controller.enqueue(
          encoder.encode(
            'event: ui.import_preflight\ndata: {"preflight":{"verdict":"ready","resolved":true,"runId":"run-1"}}\n\n',
          ),
        );
        controller.enqueue(
          encoder.encode(
            'event: ui.import_wizard\ndata: {"wizard":{"runId":"run-1","fileName":"students.xlsx","sheets":[],"steps":[]}}\n\n',
          ),
        );
        controller.enqueue(encoder.encode('event: done\ndata: {}\n\n'));
        controller.close();
      },
    });
    const fetchMock = vi.fn().mockResolvedValue({ ok: true, status: 200, body });
    vi.stubGlobal('fetch', fetchMock);

    const updates: ImportPreflightResolveUpdate[] = [];
    await resolveImportPreflight(
      42,
      { mapping: { students: { name: '姓名' } }, settings: { updateExisting: false } },
      (update) => updates.push(update),
    );

    expect(fetchMock).toHaveBeenCalledWith(
      '/api/ai/chat/import/preflight/42/resolve/stream',
      expect.objectContaining({ method: 'POST' }),
    );
    expect(updates).toEqual([
      { preflight: { verdict: 'ready', resolved: true, runId: 'run-1' } },
      { wizard: { runId: 'run-1', fileName: 'students.xlsx', sheets: [], steps: [] } },
    ]);
  });

  it('resolveImportPreflight 非 2xx 时抛出服务端错误', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: false,
      status: 400,
      json: async () => ({ message: '列映射「x」不在工作表表头中' }),
    });
    vi.stubGlobal('fetch', fetchMock);

    await expect(
      resolveImportPreflight(42, { mapping: {} }, () => undefined),
    ).rejects.toThrow('列映射「x」不在工作表表头中');
  });
});
