import { flushPromises, mount } from '@vue/test-utils';

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

import CodeEditor from './CodeEditor.vue';

const languageMocks = vi.hoisted(() => {
  let releasePython = () => undefined;
  const pythonReady = new Promise<void>((resolve) => {
    releasePython = resolve;
  });
  return {
    javascript: vi.fn(() => []),
    python: vi.fn(() => []),
    pythonReady,
    releasePython: () => releasePython(),
  };
});

vi.mock('@codemirror/lang-python', async () => {
  await languageMocks.pythonReady;
  return { python: languageMocks.python };
});

vi.mock('@codemirror/lang-javascript', () => ({
  javascript: languageMocks.javascript,
}));

beforeEach(() => {
  languageMocks.javascript.mockReset().mockReturnValue([]);
  languageMocks.python.mockReset().mockReturnValue([]);
});

it('mounts the latest language when it changes during initial loading', async () => {
  const wrapper = mount(CodeEditor, {
    attachTo: document.body,
    props: { language: 'python', modelValue: 'const value = 1;' },
  });
  await Promise.resolve();

  expect(wrapper.attributes('aria-busy')).toBe('true');
  expect(wrapper.text()).toContain('正在加载编辑器');

  await wrapper.setProps({ language: 'javascript' });
  await flushPromises();

  expect(languageMocks.javascript).toHaveBeenCalledOnce();
  expect(wrapper.findAll('.cm-editor')).toHaveLength(1);

  languageMocks.releasePython();
  await flushPromises();
  expect(wrapper.findAll('.cm-editor')).toHaveLength(1);
  wrapper.unmount();
});

it('shows a recoverable state when a language extension fails', async () => {
  let releaseRetry = () => undefined;
  const retryReady = new Promise<never[]>((resolve) => {
    releaseRetry = () => resolve([]);
  });
  languageMocks.javascript.mockImplementationOnce(() => {
    throw new Error('load failed');
  });
  languageMocks.javascript.mockImplementationOnce(() => retryReady as never);
  const wrapper = mount(CodeEditor, {
    attachTo: document.body,
    props: { language: 'javascript', modelValue: 'const value = 1;' },
  });

  await flushPromises();

  expect(wrapper.get('[role="alert"]').text()).toContain('编辑器加载失败');
  expect(wrapper.find('.cm-editor').exists()).toBe(false);

  await wrapper.get('[role="alert"] button').trigger('click');
  expect(wrapper.attributes('aria-busy')).toBe('true');
  expect(wrapper.text()).toContain('正在加载编辑器');

  releaseRetry();
  await flushPromises();

  expect(wrapper.find('[role="alert"]').exists()).toBe(false);
  expect(wrapper.findAll('.cm-editor')).toHaveLength(1);
  wrapper.unmount();
});
