/* eslint-disable vue/one-component-per-file -- Test-specific host components keep lazy-mount scenarios isolated. */
import { flushPromises, mount } from '@vue/test-utils';
import { defineComponent, h } from 'vue';

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

import { createLazyComponentController } from './lazy-component';

interface DeferredInstance {
  openDialog: () => void;
}

describe('createLazyComponentController', () => {
  it('loads and mounts the component only when its instance is requested', async () => {
    const openDialog = vi.fn();
    const DeferredComponent = defineComponent({
      setup(_, { expose }) {
        expose({ openDialog });
        return () => h('div', 'deferred');
      },
    });
    const loader = vi.fn().mockResolvedValue({ default: DeferredComponent });
    const controller = createLazyComponentController<DeferredInstance>(loader);
    const Host = defineComponent({
      setup() {
        return () =>
          controller.component.value
            ? h(controller.component.value, {
                ref: controller.componentRef,
              })
            : null;
      },
    });

    const wrapper = mount(Host);

    expect(loader).not.toHaveBeenCalled();
    expect(controller.loading.value).toBe(false);

    const instance = await controller.ensureInstance();
    instance.openDialog();

    expect(loader).toHaveBeenCalledTimes(1);
    expect(openDialog).toHaveBeenCalledTimes(1);
    expect(wrapper.text()).toBe('deferred');
    expect(controller.loading.value).toBe(false);

    await controller.ensureInstance();
    expect(loader).toHaveBeenCalledTimes(1);
  });

  it('clears loading state and allows retry after a loading failure', async () => {
    const DeferredComponent = defineComponent({
      setup() {
        return () => h('div', 'deferred');
      },
    });
    const loader = vi
      .fn()
      .mockRejectedValueOnce(new Error('chunk failed'))
      .mockResolvedValueOnce({ default: DeferredComponent });
    const controller = createLazyComponentController<DeferredInstance>(loader);
    const Host = defineComponent({
      setup() {
        return () =>
          controller.component.value
            ? h(controller.component.value, {
                ref: controller.componentRef,
              })
            : null;
      },
    });

    mount(Host);

    await expect(controller.ensureInstance()).rejects.toThrow('chunk failed');
    expect(controller.loading.value).toBe(false);

    const retryPromise = controller.ensureInstance();
    await flushPromises();

    await expect(retryPromise).resolves.toBeDefined();
    expect(loader).toHaveBeenCalledTimes(2);
    expect(controller.loading.value).toBe(false);
  });
});
