import { nextTick, watchEffect } from 'vue';

import { afterEach, describe, expect, it, vi } from 'vitest';

import { useChatDocumentUploads } from './useChatDocumentUploads';

const mediaApi = vi.hoisted(() => ({
  deleteAgentChatDocument: vi.fn(),
  getAgentChatDocumentStatus: vi.fn(),
  retryAgentChatDocument: vi.fn(),
  uploadAgentChatDocument: vi.fn(),
}));

vi.mock('./mediaApi', () => mediaApi);

describe('useChatDocumentUploads', () => {
  afterEach(() => {
    vi.restoreAllMocks();
    vi.unstubAllGlobals();
    Object.values(mediaApi).forEach((mock) => mock.mockReset());
  });

  it('reactively exposes the ready state after upload', async () => {
    let resolveUpload: (value: any) => void = () => undefined;
    mediaApi.uploadAgentChatDocument.mockReturnValue(
      new Promise((resolve) => {
        resolveUpload = resolve;
      }),
    );

    const uploads = useChatDocumentUploads();
    const observedStatuses: (string | undefined)[] = [];
    const stop = watchEffect(() => {
      observedStatuses.push(uploads.items.value[0]?.status);
    });
    const pending = uploads.addFiles(
      [
        new File(['document'], 'test.docx', {
          type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        }),
      ],
      { agentId: 'agent-1', mode: 'FORMAL', sessionId: 'session-1' },
    );
    await nextTick();

    resolveUpload({
      data: {
        attachmentRef: 'document:test',
        mimeType:
          'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        name: 'test.docx',
        size: 8,
        status: 'READY',
        uploadId: 'upload-1',
      },
      errorCode: 0,
    });
    await pending;
    await nextTick();
    stop();

    expect(observedStatuses).toContain('uploading');
    expect(observedStatuses.at(-1)).toBe('ready');
    expect(uploads.uploadIds.value).toEqual(['upload-1']);
    expect(mediaApi.uploadAgentChatDocument).toHaveBeenCalledWith(
      expect.any(File),
      { agentId: 'agent-1', mode: 'FORMAL', sessionId: 'session-1' },
      expect.stringMatching(/^[a-f0-9]{32}$/),
    );
  });

  it('uses getRandomValues when randomUUID is unavailable', async () => {
    const getRandomValues = vi.fn((values: Uint8Array) => {
      values.fill(171);
      return values;
    });
    vi.stubGlobal('crypto', { getRandomValues });
    mediaApi.uploadAgentChatDocument.mockImplementation(
      async (file, _context, uploadId) => ({
        data: {
          attachmentRef: 'document:chrome-90',
          mimeType: file.type,
          name: file.name,
          size: file.size,
          status: 'READY',
          uploadId,
        },
        errorCode: 0,
      }),
    );

    const uploads = useChatDocumentUploads();
    await uploads.addFiles(
      [new File(['document'], 'chrome-90.pdf', { type: 'application/pdf' })],
      { agentId: 'agent-1', mode: 'FORMAL', sessionId: 'session-1' },
    );

    expect(getRandomValues).toHaveBeenCalled();
    expect(mediaApi.uploadAgentChatDocument).toHaveBeenCalledWith(
      expect.any(File),
      { agentId: 'agent-1', mode: 'FORMAL', sessionId: 'session-1' },
      expect.stringMatching(/^[a-f0-9]{32}$/),
    );
    expect(uploads.items.value[0]).toMatchObject({
      status: 'ready',
      uploadId: expect.stringMatching(/^[a-f0-9]{32}$/),
    });
  });

  it('rejects unsupported files without adding an error attachment', async () => {
    const uploads = useChatDocumentUploads();
    const result = await uploads.addFiles(
      [new File(['archive'], 'unsupported.zip', { type: 'application/zip' })],
      { agentId: 'agent-1', mode: 'FORMAL', sessionId: 'session-1' },
    );

    expect(result).toEqual({
      rejectedCount: 0,
      unsupportedCount: 1,
    });
    expect(uploads.items.value).toEqual([]);
    expect(mediaApi.uploadAgentChatDocument).not.toHaveBeenCalled();
  });

  it('exposes a server read failure without continuing to poll', async () => {
    mediaApi.uploadAgentChatDocument.mockResolvedValue({
      data: {
        attachmentRef: 'document:failed',
        errorMessage: '未检测到可读取文字',
        mimeType: 'application/pdf',
        name: 'scan.pdf',
        size: 8,
        status: 'READ_FAILED',
        uploadId: 'upload-failed',
      },
      errorCode: 0,
    });

    const uploads = useChatDocumentUploads();
    await uploads.addFiles(
      [new File(['document'], 'scan.pdf', { type: 'application/pdf' })],
      { agentId: 'agent-1', mode: 'FORMAL', sessionId: 'session-1' },
    );

    expect(uploads.items.value[0]).toMatchObject({
      error: '未检测到可读取文字',
      status: 'error',
      uploadId: 'upload-failed',
    });
    expect(mediaApi.getAgentChatDocumentStatus).not.toHaveBeenCalled();
  });
});
