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

import { useLaunchDelete } from './useLaunchDelete';

const mocks = vi.hoisted(() => ({
  confirm: vi.fn(),
  error: vi.fn(),
  getInfo: vi.fn(),
  hideLoading: vi.fn(),
  reload: vi.fn(),
  remove: vi.fn(),
  sign: vi.fn(),
  showLoading: vi.fn(),
  success: vi.fn(),
}));

vi.mock('@jnpf/hooks', () => ({ useMessage: () => ({ createConfirm: mocks.confirm, createMessage: mocks }) }));
vi.mock('#/api/workFlow/task', () => ({ delFlowLaunch: mocks.remove, getFlowTaskInfo: mocks.getInfo }));
vi.mock('#/locales', () => ({ $t: (key: string) => key }));
vi.mock('#/utils/jnpf', () => ({ onlineUtils: { hideLoading: mocks.hideLoading, showLoading: mocks.showLoading, sign: mocks.sign } }));

const record = { id: 'task-1', flowId: 'flow-1' };
function detail(enabled = true) {
  return {
    data: {
      formInfo: {
        id: 'form-1',
        columnData: JSON.stringify({ columnBtnsList: [{ value: 'remove', label: '废弃', event: { biz_sign_enabled: enabled } }] }),
      },
      formData: { id: 'business-1', title: '测试流程', biz_sign: 'historical-sign' },
    },
  };
}

describe('my initiated workflow deletion', () => {
  beforeEach(() => {
    vi.resetAllMocks();
    mocks.getInfo.mockResolvedValue(detail());
    mocks.remove.mockResolvedValue({ msg: '删除成功' });
  });

  it('reads the start form and signs before deleting with the fresh signature', async () => {
    const { deleting, handleDelete } = useLaunchDelete(mocks.reload);
    const pending = handleDelete(record);
    expect(mocks.showLoading).toHaveBeenCalledOnce();
    expect(mocks.hideLoading).not.toHaveBeenCalled();
    expect(deleting.value).toBe(true);
    await pending;
    expect(mocks.getInfo).toHaveBeenCalledWith('task-1', { flowId: 'flow-1', opType: '-1' });
    expect(mocks.confirm).not.toHaveBeenCalled();
    expect(mocks.remove).not.toHaveBeenCalled();
    const options = mocks.sign.mock.calls[0]![0];
    expect(mocks.hideLoading).not.toHaveBeenCalled();
    options.onOpen();
    expect(mocks.hideLoading).toHaveBeenCalledOnce();
    expect(deleting.value).toBe(true);
    expect(options.metaData).toMatchObject({ biz_button: '废弃', biz_form_id: 'form-1', biz_data: [detail().data.formData], is_biz_form: false });
    await options.onSubmit({ biz_sign: 'delete-sign' });
    expect(mocks.remove).toHaveBeenCalledWith('task-1', { biz_sign: 'delete-sign' });
    expect(mocks.reload).toHaveBeenCalledOnce();
    expect(deleting.value).toBe(false);
    expect(mocks.showLoading).toHaveBeenCalledTimes(2);
    expect(mocks.hideLoading).toHaveBeenCalledTimes(2);
  });

  it('cancels signing without deleting and allows another attempt', async () => {
    const { deleting, handleDelete } = useLaunchDelete(mocks.reload);
    await handleDelete(record);
    mocks.sign.mock.calls[0]![0].onCancel();
    expect(mocks.hideLoading).toHaveBeenCalledOnce();
    expect(mocks.remove).not.toHaveBeenCalled();
    expect(deleting.value).toBe(false);
    await handleDelete(record);
    expect(mocks.sign).toHaveBeenCalledTimes(2);
  });

  it('does not delete when signing provides no valid credential', async () => {
    const { handleDelete } = useLaunchDelete(mocks.reload);
    await handleDelete(record);
    await mocks.sign.mock.calls[0]![0].onSubmit({});
    expect(mocks.error).toHaveBeenCalled();
    expect(mocks.remove).not.toHaveBeenCalled();
  });

  it('confirms an unsigned delete without reusing a historical signature', async () => {
    mocks.getInfo.mockResolvedValue(detail(false));
    const { deleting, handleDelete } = useLaunchDelete(mocks.reload);
    await handleDelete(record);
    expect(mocks.sign).not.toHaveBeenCalled();
    expect(mocks.remove).not.toHaveBeenCalled();
    expect(mocks.hideLoading).toHaveBeenCalledOnce();
    await mocks.confirm.mock.calls[0]![0].onOk();
    expect(mocks.remove).toHaveBeenCalledWith('task-1', undefined);
    expect(deleting.value).toBe(false);
    expect(mocks.hideLoading).toHaveBeenCalledTimes(2);
  });

  it('does not delete when the confirmation is cancelled', async () => {
    mocks.getInfo.mockResolvedValue(detail(false));
    const { deleting, handleDelete } = useLaunchDelete(mocks.reload);
    await handleDelete(record);
    mocks.confirm.mock.calls[0]![0].onCancel();
    expect(mocks.remove).not.toHaveBeenCalled();
    expect(deleting.value).toBe(false);
  });

  it('closes the full screen loading and releases the guard if the signature modal fails to open', async () => {
    const { deleting, handleDelete } = useLaunchDelete(mocks.reload);
    await handleDelete(record);
    mocks.sign.mock.calls[0]![0].onOpenError(new Error('modal failed'));
    expect(mocks.hideLoading).toHaveBeenCalledOnce();
    expect(deleting.value).toBe(false);
    expect(mocks.remove).not.toHaveBeenCalled();
  });

  it.each(['request', 'missing', 'malformed'])('stops deletion on %s configuration failure', async (failure) => {
    if (failure === 'request') mocks.getInfo.mockRejectedValue(new Error('network'));
    if (failure === 'missing') mocks.getInfo.mockResolvedValue({ data: {} });
    if (failure === 'malformed') mocks.getInfo.mockResolvedValue({ data: { formInfo: { columnData: '{' } } });
    const { deleting, handleDelete } = useLaunchDelete(mocks.reload);
    await handleDelete(record);
    expect(mocks.error).toHaveBeenCalled();
    expect(mocks.sign).not.toHaveBeenCalled();
    expect(mocks.confirm).not.toHaveBeenCalled();
    expect(mocks.remove).not.toHaveBeenCalled();
    expect(deleting.value).toBe(false);
    expect(mocks.hideLoading).toHaveBeenCalledOnce();
  });

  it('prevents repeated clicks while the signature dialog is open', async () => {
    const { handleDelete } = useLaunchDelete(mocks.reload);
    await handleDelete(record);
    await handleDelete(record);
    expect(mocks.getInfo).toHaveBeenCalledOnce();
    expect(mocks.sign).toHaveBeenCalledOnce();
  });

  it('releases the busy state and does not reload if the delete fails', async () => {
    mocks.remove.mockRejectedValue(new Error('delete failed'));
    const { deleting, handleDelete } = useLaunchDelete(mocks.reload);
    await handleDelete(record);
    await expect(mocks.sign.mock.calls[0]![0].onSubmit({ biz_sign: 'delete-sign' })).rejects.toThrow('delete failed');
    expect(deleting.value).toBe(false);
    expect(mocks.reload).not.toHaveBeenCalled();
  });
});
