import { ref } from 'vue';

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

import { useFlowForm } from './useFlowForm';

vi.mock('@jnpf/hooks', () => ({ useMessage: () => ({ createMessage: { error: vi.fn() } }) }));
vi.mock('@vben/stores', () => ({ useUserStore: () => ({ getUserInfo: {} }) }));
vi.mock('#/api/system/billRule', () => ({ getBillNumber: vi.fn() }));

function createForm(validate: () => Promise<any>, onSubmit: () => void) {
  const emit = vi.fn(onSubmit);
  const formRef = ref({ validate });
  const { dataFormSubmit } = useFlowForm({ config: {}, selfState: { dataForm: {}, dataRule: {} }, emit, formRef } as any);
  return { dataFormSubmit, emit };
}

describe('useFlowForm post-validation step', () => {
  it.each(['save', 'saveAudit', 'saveAssist'])('preserves incomplete form and child rows without validation for %s', async (eventType) => {
    const validate = vi.fn().mockRejectedValue(new Error('required field missing'));
    const selfExist = vi.fn(() => false);
    const afterValidation = vi.fn(async () => false);
    const emit = vi.fn();
    const formData = {
      title: '',
      rows: [
        { id: 'existing', name: '' },
        { name: 'unfinished', amount: null },
      ],
    };
    const { dataFormSubmit } = useFlowForm({
      config: {},
      selfState: { dataForm: formData, dataRule: {} },
      emit,
      formRef: ref({ validate }),
      selfExist,
    } as any);

    await dataFormSubmit(eventType, 1, afterValidation);

    expect(validate).not.toHaveBeenCalled();
    expect(selfExist).not.toHaveBeenCalled();
    expect(afterValidation).not.toHaveBeenCalled();
    expect(emit).toHaveBeenCalledExactlyOnceWith('eventReceiver', { formData }, eventType);
  });

  it.each(['submit', 'delegateSubmit', 'audit', 'reject', 'freeApprover'])('still blocks invalid data for %s', async (eventType) => {
    const validate = vi.fn().mockRejectedValue(new Error('required field missing'));
    const { dataFormSubmit, emit } = createForm(validate, vi.fn());

    await dataFormSubmit(eventType, 1);

    expect(validate).toHaveBeenCalledOnce();
    expect(emit).not.toHaveBeenCalled();
  });

  it.each(['audit', 'reject'])('runs after validation and before submitting %s', async (eventType) => {
    const steps: string[] = [];
    const { dataFormSubmit } = createForm(
      async () => {
        steps.push('validate');
        return {};
      },
      () => steps.push('submit'),
    );

    await dataFormSubmit(eventType, 1, async () => {
      steps.push('approval check');
      return true;
    });

    expect(steps).toEqual(['validate', 'approval check', 'submit']);
  });

  it('does not check approval when validation fails', async () => {
    const afterValidation = vi.fn(async () => true);
    const { dataFormSubmit, emit } = createForm(async () => {
      throw new Error('invalid');
    }, vi.fn());

    await dataFormSubmit('reject', 1, afterValidation);

    expect(afterValidation).not.toHaveBeenCalled();
    expect(emit).not.toHaveBeenCalled();
  });

  it('does not submit when approval check stops submission', async () => {
    const { dataFormSubmit, emit } = createForm(async () => ({}), vi.fn());

    await dataFormSubmit('reject', 1, async () => false);

    expect(emit).not.toHaveBeenCalled();
  });
});
