import type {
  ChatArtifactAttachment,
  ChatDocumentAttachment,
} from '@easyflow/common-ui';

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

import { createAgentArtifactLoader, loadAgentChatDocument } from './mediaApi';

const requestApi = vi.hoisted(() => ({
  download: vi.fn(),
}));

vi.mock('#/api/request', () => ({
  api: requestApi,
}));

const documentAttachment: ChatDocumentAttachment = {
  downloadUrl:
    '/api/v1/agent/media/document/content?reference=document:attachment',
  name: '需求说明.docx',
  status: 'ready',
};

const artifactAttachment: ChatArtifactAttachment = {
  artifactId: '01JARTIFACT',
  downloadUrl:
    'https://evil.example/report?agentId=999&mode=DRAFT&runtimeSessionId=forged',
  fileName: '结果.txt',
  status: 'available',
};

describe('agent chat document download', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:document');
    vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
    vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(
      () => undefined,
    );
  });

  afterEach(() => {
    vi.useRealTimers();
    vi.restoreAllMocks();
    requestApi.download.mockReset();
  });

  it('downloads a non-empty blob and revokes its URL after the browser takes over', async () => {
    requestApi.download.mockResolvedValue(new Blob(['document']));

    await loadAgentChatDocument(documentAttachment);

    expect(requestApi.download).toHaveBeenCalledWith(
      documentAttachment.downloadUrl,
    );
    expect(URL.createObjectURL).toHaveBeenCalledTimes(1);
    expect(HTMLAnchorElement.prototype.click).toHaveBeenCalledTimes(1);
    expect(URL.revokeObjectURL).not.toHaveBeenCalled();

    await vi.advanceTimersByTimeAsync(1000);

    expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:document');
  });

  it('rejects an empty response before creating a damaged download', async () => {
    requestApi.download.mockResolvedValue(new Blob([]));

    await expect(loadAgentChatDocument(documentAttachment)).rejects.toThrow(
      '下载内容为空，请稍后重试',
    );

    expect(URL.createObjectURL).not.toHaveBeenCalled();
    expect(HTMLAnchorElement.prototype.click).not.toHaveBeenCalled();
  });
});

describe('agent Artifact download', () => {
  beforeEach(() => {
    vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:artifact');
    vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
    vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(
      () => undefined,
    );
  });

  afterEach(() => {
    vi.restoreAllMocks();
    requestApi.download.mockReset();
  });

  it('用正式聊天的可信上下文构建同源下载地址', async () => {
    requestApi.download.mockResolvedValue(new Blob(['artifact']));
    const loader = createAgentArtifactLoader(() => ({
      agentId: '42',
      mode: 'FORMAL',
      sessionId: '10001',
    }));

    await loader(artifactAttachment);

    expect(requestApi.download).toHaveBeenCalledWith(
      '/api/v1/agent/artifacts/01JARTIFACT/content?agentId=42&mode=FORMAL&sessionId=10001',
    );
    expect(HTMLAnchorElement.prototype.click).toHaveBeenCalledTimes(1);
  });

  it('草稿模式只传 runtimeSessionId 且忽略事件伪造 URL', async () => {
    requestApi.download.mockResolvedValue(new Blob(['artifact']));
    const loader = createAgentArtifactLoader(() => ({
      agentId: '43',
      mode: 'DRAFT',
      runtimeSessionId: 'agent-draft-43',
    }));

    await loader(artifactAttachment);

    expect(requestApi.download).toHaveBeenCalledWith(
      '/api/v1/agent/artifacts/01JARTIFACT/content?agentId=43&mode=DRAFT&runtimeSessionId=agent-draft-43',
    );
    expect(requestApi.download.mock.calls[0]?.[0]).not.toContain('sessionId=');
    expect(requestApi.download.mock.calls[0]?.[0]).not.toContain(
      'evil.example',
    );
  });

  it.each([
    undefined,
    { agentId: '', mode: 'FORMAL' as const, sessionId: '10001' },
    { agentId: '42', mode: 'FORMAL' as const, sessionId: '' },
    { agentId: '42', mode: 'DRAFT' as const, runtimeSessionId: '' },
  ])('上下文缺失时拒绝请求: %o', async (context) => {
    const loader = createAgentArtifactLoader(() => context);

    await expect(loader(artifactAttachment)).rejects.toThrow(
      '产物下载上下文无效',
    );

    expect(requestApi.download).not.toHaveBeenCalled();
  });
});
