import BmsFloatingWindow from '@/components/layout/BmsFloatingWindow.vue';
import { flushPromises, mount, MountingOptions } from '@vue/test-utils';
import { describe, expect, it, vi } from 'vitest';
import { nextTick } from 'vue';

window.ResizeObserver = vi.fn().mockImplementation(() => ({
  observe: vi.fn(),
  unobserve: vi.fn(),
  disconnect: vi.fn(),
}));

const BmsIconButtonStub = {
  template: `<button><slot /></button>`,
};

const factory = (options: MountingOptions<any, {}> = {}) => {
  const wrapper = mount(BmsFloatingWindow, {
    global: {
      stubs: {
        BmsIconButton: BmsIconButtonStub,
      },
    },
    props: {
      title: 'Test Window',
      modelValue: true,
    },
    slots: {
      default: 'Window content',
    },
    ...options,
  } as any);

  return { wrapper };
};

describe('BmsFloatingWindow', () => {
  describe('rendering', () => {
    it('renders the title', () => {
      const { wrapper } = factory();
      expect(wrapper.find('.floating-window__header__title').text()).toBe(
        'Test Window',
      );
    });

    it('renders slot content', () => {
      const { wrapper } = factory();
      expect(wrapper.find('.floating-window__content').text()).toContain(
        'Window content',
      );
    });

    it('is visible when open is true', () => {
      const { wrapper } = factory();
      expect(wrapper.find('.floating-window').isVisible()).toBe(true);
    });

    it('is hidden when open is false', () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: false },
      });
      expect(wrapper.find('.floating-window').isVisible()).toBe(false);
    });
  });

  describe('close button', () => {
    it('emits update:modelValue with false when close button is clicked', async () => {
      const { wrapper } = factory();
      const buttons = wrapper.findAll('button');
      await buttons[buttons.length - 1].trigger('click');
      await nextTick();
      expect(wrapper.emitted()['update:modelValue']).toBeTruthy();
      expect(wrapper.emitted()['update:modelValue'][0]).toEqual([false]);
    });

    it('hides the window after close button click', async () => {
      const { wrapper } = factory();
      const buttons = wrapper.findAll('button');
      await buttons[buttons.length - 1].trigger('click');
      await nextTick();
      expect(wrapper.find('.floating-window').isVisible()).toBe(false);
    });
  });

  describe('expandable', () => {
    it('does not show expand button when expandable is false', () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: true, expandable: false },
      });
      expect(wrapper.find('.floating-window__expand-btn').exists()).toBe(false);
    });

    it('shows expand button when expandable is true', () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: true, expandable: true },
      });
      expect(wrapper.find('.floating-window__expand-btn').exists()).toBe(true);
    });

    it('adds floating-window--expanded class on expand button click', async () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: true, expandable: true },
      });
      expect(wrapper.find('.floating-window--expanded').exists()).toBe(false);
      await wrapper.find('.floating-window__expand-btn').trigger('click');
      await nextTick();
      expect(wrapper.find('.floating-window--expanded').exists()).toBe(true);
    });

    it('removes floating-window--expanded class on second expand button click', async () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: true, expandable: true },
      });
      await wrapper.find('.floating-window__expand-btn').trigger('click');
      await nextTick();
      expect(wrapper.find('.floating-window--expanded').exists()).toBe(true);
      await wrapper.find('.floating-window__expand-btn').trigger('click');
      await nextTick();
      expect(wrapper.find('.floating-window--expanded').exists()).toBe(false);
    });

    it('starts expanded when expanded prop is true', async () => {
      const { wrapper } = factory({
        props: {
          title: 'Test Window',
          modelValue: true,
          expandable: true,
          expanded: true,
        },
      });
      await nextTick();
      expect(wrapper.find('.floating-window--expanded').exists()).toBe(true);
    });

    it('starts expanded when startExpanded prop is true (deprecated)', async () => {
      const { wrapper } = factory({
        props: {
          title: 'Test Window',
          modelValue: true,
          expandable: true,
          startExpanded: true,
        },
      });
      await nextTick();
      expect(wrapper.find('.floating-window--expanded').exists()).toBe(true);
    });

    it('expands when expanded prop changes to true', async () => {
      const { wrapper } = factory({
        props: {
          title: 'Test Window',
          modelValue: true,
          expandable: true,
          expanded: false,
        },
      });
      expect(wrapper.find('.floating-window--expanded').exists()).toBe(false);
      await wrapper.setProps({ expanded: true });
      await nextTick();
      expect(wrapper.find('.floating-window--expanded').exists()).toBe(true);
    });
  });

  describe('drag', () => {
    it('shows drag overlay when dragging starts', async () => {
      const { wrapper } = factory();
      expect(wrapper.find('.floating-window__drag-overlay').exists()).toBe(
        false,
      );
      await wrapper
        .find('.floating-window__header')
        .trigger('mousedown', { clientX: 100, clientY: 100 });
      await nextTick();
      expect(wrapper.find('.floating-window__drag-overlay').exists()).toBe(
        true,
      );
    });

    it('adds floating-window--dragging class when dragging', async () => {
      const { wrapper } = factory();
      await wrapper
        .find('.floating-window__header')
        .trigger('mousedown', { clientX: 100, clientY: 100 });
      await nextTick();
      expect(wrapper.find('.floating-window--dragging').exists()).toBe(true);
    });

    it('stops dragging on mouseup on drag overlay', async () => {
      const { wrapper } = factory();
      await wrapper
        .find('.floating-window__header')
        .trigger('mousedown', { clientX: 100, clientY: 100 });
      await nextTick();
      await wrapper.find('.floating-window__drag-overlay').trigger('mouseup');
      await nextTick();
      expect(wrapper.find('.floating-window__drag-overlay').exists()).toBe(
        false,
      );
      expect(wrapper.find('.floating-window--dragging').exists()).toBe(false);
    });

    it('does not start dragging when mousedown originates from header buttons area', async () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: true, expandable: true },
      });
      await wrapper
        .find('.floating-window__header__buttons')
        .trigger('mousedown', { clientX: 100, clientY: 100 });
      await nextTick();
      expect(wrapper.find('.floating-window__drag-overlay').exists()).toBe(
        false,
      );
    });

    it('does not start dragging when window is expanded', async () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: true, expandable: true },
      });
      await wrapper.find('.floating-window__expand-btn').trigger('click');
      await nextTick();
      await wrapper
        .find('.floating-window__header')
        .trigger('mousedown', { clientX: 100, clientY: 100 });
      await nextTick();
      expect(wrapper.find('.floating-window__drag-overlay').exists()).toBe(
        false,
      );
    });

    it('updates position while dragging', async () => {
      const { wrapper } = factory();
      await wrapper
        .find('.floating-window__header')
        .trigger('mousedown', { clientX: 50, clientY: 50 });
      await nextTick();
      await wrapper
        .find('.floating-window__drag-overlay')
        .trigger('mousemove', { clientX: 200, clientY: 150 });
      await nextTick();
      const style = wrapper.find('.floating-window').attributes('style') ?? '';
      expect(style).toContain('left:');
      expect(style).toContain('top:');
    });
  });

  describe('exposed', () => {
    it('exposes isExpanded as false by default', () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: true, expandable: true },
      });
      expect((wrapper.vm as any).isExpanded).toBe(false);
    });

    it('exposes isExpanded following expand/collapse state', async () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: true, expandable: true },
      });
      expect((wrapper.vm as any).isExpanded).toBe(false);
      await wrapper.find('.floating-window__expand-btn').trigger('click');
      await nextTick();
      expect((wrapper.vm as any).isExpanded).toBe(true);
      await wrapper.find('.floating-window__expand-btn').trigger('click');
      await nextTick();
      expect((wrapper.vm as any).isExpanded).toBe(false);
    });
  });

  describe('open watcher', () => {
    it('shows the window when open transitions from false to true', async () => {
      const { wrapper } = factory({
        props: { title: 'Test Window', modelValue: false },
      });
      const el = wrapper.find('.floating-window');
      expect((el.element as HTMLElement).style.display).toBe('none');
      await wrapper.setProps({ modelValue: true });
      await flushPromises();
      expect((el.element as HTMLElement).style.display).not.toBe('none');
    });
  });
});
