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

import {
  isWorkflowShareRequest,
  readWorkflowShareKey,
  resolveWorkflowShareFailureReason,
  resolveWorkflowShareWorkflowId,
  withWorkflowShareHeader,
  WORKFLOW_SHARE_HEADER,
} from '#/utils/workflow-share-context';

describe('workflow share context', () => {
  it('reads the share key from a history-mode URL', () => {
    expect(
      readWorkflowShareKey(
        'https://example.test/share/workflow?shareKey=abc123',
      ),
    ).toBe('abc123');
  });

  it('reads the share key from a hash-mode URL', () => {
    expect(
      readWorkflowShareKey(
        'https://example.test/#/share/workflow?shareKey=hash-key',
      ),
    ).toBe('hash-key');
  });

  it('reads the share key from the standalone workflow share route', () => {
    expect(
      readWorkflowShareKey(
        'https://example.test/flow/#/share/workflow?shareKey=standalone-key',
      ),
    ).toBe('standalone-key');
  });

  it('adds the workflow share header without dropping existing headers', () => {
    expect(
      withWorkflowShareHeader(
        { 'Accept-Language': 'zh-CN' },
        {
          pageUrl: 'https://example.test/share/workflow?shareKey=abc123',
          requestMethod: 'GET',
          requestUrl: '/api/v1/workflowChat/descriptor?workflowId=1',
        },
      ),
    ).toEqual({
      'Accept-Language': 'zh-CN',
      [WORKFLOW_SHARE_HEADER]: 'abc123',
    });
  });

  it('leaves headers unchanged outside a shared URL', () => {
    const headers = { 'Accept-Language': 'zh-CN' };

    expect(
      withWorkflowShareHeader(headers, {
        pageUrl: 'https://example.test/share/workflow',
        requestMethod: 'GET',
        requestUrl: '/api/v1/workflowChat/descriptor?workflowId=1',
      }),
    ).toEqual(headers);
  });

  it('ignores a knowledge share key left before the active hash route', () => {
    const pollutedUrl =
      'https://example.test/flow/share/knowledge?shareKey=knowledge-key#/ai/workflow';

    expect(readWorkflowShareKey(pollutedUrl)).toBeNull();
    expect(
      withWorkflowShareHeader(
        { 'Accept-Language': 'zh-CN' },
        {
          pageUrl: pollutedUrl,
          requestMethod: 'POST',
          requestUrl: '/api/v1/workflow/submitPublishApproval',
        },
      ),
    ).toEqual({ 'Accept-Language': 'zh-CN' });
  });

  it('does not attach chat sharing capabilities to workflow design', () => {
    expect(
      readWorkflowShareKey(
        'https://example.test/flow/#/ai/workflow/design?id=1&shareKey=workflow-key',
      ),
    ).toBeNull();
  });

  it('only attaches the share key to explicitly allowed workflow requests', () => {
    const pageUrl =
      'https://example.test/flow/#/share/workflow?shareKey=workflow-key';

    expect(
      withWorkflowShareHeader(
        {},
        {
          pageUrl,
          requestMethod: 'GET',
          requestUrl: '/api/v1/workflow/page',
        },
      ),
    ).toEqual({});
    expect(
      withWorkflowShareHeader(
        {},
        {
          pageUrl,
          requestMethod: 'POST',
          requestUrl: '/api/v1/bot/chat',
        },
      ),
    ).toEqual({});
    expect(
      withWorkflowShareHeader(
        {},
        {
          pageUrl,
          requestMethod: 'POST',
          requestUrl: '/api/v1/workflowChat/run',
        },
      ),
    ).toEqual({
      [WORKFLOW_SHARE_HEADER]: 'workflow-key',
    });
  });

  it('matches only the workflow sharing endpoint whitelist', () => {
    expect(
      isWorkflowShareRequest('/flow/api/v1/workflowChat/run', 'post'),
    ).toBe(true);
    expect(
      isWorkflowShareRequest('/flow/api/v1/workflowChat/execution', 'get'),
    ).toBe(true);
    expect(isWorkflowShareRequest('/flow/api/v1/workflow/update', 'post')).toBe(
      false,
    );
    expect(isWorkflowShareRequest('/flow/api/v1/workflow/page', 'get')).toBe(
      false,
    );
    expect(isWorkflowShareRequest('/flow/api/v1/model/list', 'get')).toBe(
      false,
    );
  });

  it('resolves the workflow id for a shared URL', async () => {
    const resolve = vi.fn().mockResolvedValue('workflow-1');
    const onFailure = vi.fn();

    await expect(
      resolveWorkflowShareWorkflowId({
        currentWorkflowId: undefined,
        onFailure,
        resolve,
        shareKey: 'share-key',
      }),
    ).resolves.toBe('workflow-1');
    expect(resolve).toHaveBeenCalledOnce();
    expect(onFailure).not.toHaveBeenCalled();
  });

  it('handles an expired share without leaking the resolve error', async () => {
    const resolveError = new Error('expired');
    const resolve = vi.fn().mockRejectedValue(resolveError);
    const onFailure = vi.fn().mockResolvedValue(undefined);

    await expect(
      resolveWorkflowShareWorkflowId({
        currentWorkflowId: undefined,
        onFailure,
        resolve,
        shareKey: 'expired-key',
      }),
    ).resolves.toBeNull();
    expect(onFailure).toHaveBeenCalledWith(resolveError);
  });

  it('maps workflow share failures to the expired page reason', () => {
    expect(
      resolveWorkflowShareFailureReason({
        response: { data: { message: '工作流分享链接已过期' } },
      }),
    ).toBe('expired');
    expect(
      resolveWorkflowShareFailureReason(new Error('工作流分享链接无效')),
    ).toBe('invalid');
  });
});
