// @vitest-environment happy-dom

import { useUserStore } from '@easyflow/stores';

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

import { clearAgentChatBrowserCache } from '#/utils/agent-chat-cache';

import { agentChatRuntimeManager } from './agentChatRuntimeManager';
import { sendAgentChat } from './api';

vi.mock('./api', () => ({
  generateAgentSessionId: vi.fn(),
  sendAgentChat: vi.fn(),
  stopAgentChatStream: vi.fn(),
}));

describe('agentChatRuntimeManager', () => {
  beforeEach(() => {
    setActivePinia(createPinia());
    sessionStorage.clear();
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('replaces accepted attachments and isolates snapshots by account', async () => {
    let callbacks: any;
    vi.mocked(sendAgentChat).mockImplementation((_data, options) => {
      callbacks = options;
      return Promise.resolve() as any;
    });
    const userStore = useUserStore();
    const firstAccount = {
      avatar: '',
      id: 'user-1',
      loginName: 'admin',
      nickname: '管理员',
      tenantId: 'tenant-1',
    };
    userStore.setUserInfo(firstAccount);

    await agentChatRuntimeManager.start({
      agentId: 'agent-1',
      documentUploadIds: ['document-upload-1'],
      documents: [
        {
          downloadUrl:
            '/api/v1/agent/media/document/content?reference=draft%3Adocument-upload-1',
          name: 'draft.docx',
          status: 'ready',
          uploadId: 'document-upload-1',
        },
      ],
      images: [
        {
          name: 'draft.png',
          previewUrl: '/api/v1/agent/media/content?reference=draft%3Aupload-1',
          status: 'ready',
          uploadId: 'upload-1',
        },
      ],
      prompt: '识别图片',
      sessionId: '101',
    });
    callbacks.onMessage({
      data: JSON.stringify({
        domain: 'SYSTEM',
        payload: {
          attachments: [
            {
              attachmentRef: 'formal:101:201:document:0',
              downloadUrl:
                '/api/v1/agent/media/document/content?reference=formal:101:201:document:0',
              name: 'draft.docx',
              readSnapshotId: 'snapshot-1',
              size: 2048,
            },
          ],
          images: [
            {
              imageRef: 'formal:101:201:0:png',
              name: 'draft.png',
              previewUrl:
                '/api/v1/agent/media/content?reference=formal:101:201:0:png',
            },
          ],
        },
        type: 'INPUT_ACCEPTED',
      }),
    });

    const accepted = agentChatRuntimeManager.getSnapshot('101');
    const userMessage = accepted?.items.find(
      (item) => item.type === 'message' && item.role === 'user',
    );
    expect(
      userMessage?.type === 'message' ? userMessage.images?.[0] : null,
    ).toEqual(
      expect.objectContaining({
        imageRef: 'formal:101:201:0:png',
        previewUrl:
          '/api/v1/agent/media/content?reference=formal:101:201:0:png',
      }),
    );
    expect(
      userMessage?.type === 'message' ? userMessage.documents?.[0] : null,
    ).toEqual(
      expect.objectContaining({
        attachmentRef: 'formal:101:201:document:0',
        readSnapshotId: 'snapshot-1',
        status: 'ready',
      }),
    );
    expect(vi.mocked(sendAgentChat).mock.calls[0]?.[0]).toEqual(
      expect.objectContaining({
        documentUploadIds: ['document-upload-1'],
      }),
    );

    userStore.setUserInfo({
      ...firstAccount,
      id: 'user-2',
      loginName: 'other',
      nickname: '其他用户',
    });
    expect(agentChatRuntimeManager.getSnapshot('101')).toBeUndefined();

    clearAgentChatBrowserCache(firstAccount);
    userStore.setUserInfo(firstAccount);
    expect(agentChatRuntimeManager.getSnapshot('101')).toBeUndefined();
  });

  it('coalesces streaming notifications and persists terminal state immediately', async () => {
    vi.useFakeTimers();
    let callbacks: any;
    vi.mocked(sendAgentChat).mockImplementation((_data, options) => {
      callbacks = options;
      return Promise.resolve() as any;
    });
    const account = {
      avatar: '',
      id: 'stream-user',
      loginName: 'stream-user',
      nickname: '流式用户',
      tenantId: 'tenant-1',
    };
    useUserStore().setUserInfo(account);

    await agentChatRuntimeManager.start({
      agentId: 'agent-1',
      prompt: '流式测试',
      sessionId: 'stream-session',
    });
    const listener = vi.fn();
    const unsubscribe = agentChatRuntimeManager.subscribe(listener);
    const storageSpy = vi.spyOn(sessionStorage, 'setItem');

    for (const delta of ['A', 'B', 'C']) {
      callbacks.onMessage({
        data: JSON.stringify({
          domain: 'LLM',
          payload: { delta },
          type: 'MESSAGE',
        }),
      });
    }

    expect(listener).not.toHaveBeenCalled();
    expect(storageSpy).not.toHaveBeenCalled();
    expect(
      JSON.stringify(
        agentChatRuntimeManager.getSnapshot('stream-session')?.items,
      ),
    ).toContain('ABC');

    await vi.advanceTimersByTimeAsync(50);
    expect(listener).toHaveBeenCalledTimes(1);
    expect(storageSpy).not.toHaveBeenCalled();

    await vi.advanceTimersByTimeAsync(250);
    expect(storageSpy).toHaveBeenCalledTimes(2);

    callbacks.onMessage({
      data: JSON.stringify({
        domain: 'LLM',
        payload: { delta: 'D' },
        type: 'MESSAGE',
      }),
    });
    listener.mockClear();
    storageSpy.mockClear();
    callbacks.onFinished();

    expect(listener).toHaveBeenCalledTimes(1);
    expect(storageSpy).toHaveBeenCalledTimes(2);
    expect(agentChatRuntimeManager.getSnapshot('stream-session')).toEqual(
      expect.objectContaining({ completed: true, sending: false }),
    );

    unsubscribe();
    storageSpy.mockRestore();
    clearAgentChatBrowserCache(account);
  });
});
