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

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

import CodeViewer from './CodeViewer.vue';

const originalClipboard = navigator.clipboard;
const originalCreateObjectURL = URL.createObjectURL;
const originalRevokeObjectURL = URL.revokeObjectURL;

afterEach(() => {
  vi.restoreAllMocks();
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: originalClipboard,
  });
  Object.defineProperty(URL, 'createObjectURL', {
    configurable: true,
    value: originalCreateObjectURL,
  });
  Object.defineProperty(URL, 'revokeObjectURL', {
    configurable: true,
    value: originalRevokeObjectURL,
  });
});

describe('code viewer', () => {
  it('copies visible content and emits the shared download action', async () => {
    const writeText = vi.fn().mockResolvedValue(undefined);
    Object.defineProperty(navigator, 'clipboard', {
      configurable: true,
      value: { writeText },
    });
    const wrapper = mount(CodeViewer, {
      global: { stubs: { CodeEditor: true } },
      props: {
        content: '{"ready":true}',
        downloadMode: 'emit',
        filename: 'references/data.json',
        language: 'json',
      },
    });

    const copyButton = wrapper
      .findAll('button')
      .find((button) => button.text() === '复制');
    const downloadButton = wrapper
      .findAll('button')
      .find((button) => button.text() === '下载');
    if (!copyButton || !downloadButton) throw new Error('缺少查看器操作按钮');

    await copyButton.trigger('click');
    await flushPromises();

    expect(writeText).toHaveBeenCalledWith('{"ready":true}');
    expect(copyButton.text()).toBe('已复制');

    await downloadButton.trigger('click');
    expect(wrapper.emitted('download')).toHaveLength(1);
    wrapper.unmount();
  });

  it('downloads an empty local text file and always releases its object URL', async () => {
    const createObjectURL = vi.fn(() => 'blob:empty-source');
    const revokeObjectURL = vi.fn();
    Object.defineProperty(URL, 'createObjectURL', {
      configurable: true,
      value: createObjectURL,
    });
    Object.defineProperty(URL, 'revokeObjectURL', {
      configurable: true,
      value: revokeObjectURL,
    });
    const click = vi
      .spyOn(HTMLAnchorElement.prototype, 'click')
      .mockImplementation(() => undefined);
    const wrapper = mount(CodeViewer, {
      global: { stubs: { CodeEditor: true } },
      props: { content: '', filename: 'empty.txt' },
    });
    const downloadButton = wrapper
      .findAll('button')
      .find((button) => button.text() === '下载');

    await downloadButton?.trigger('click');

    expect(createObjectURL).toHaveBeenCalledOnce();
    expect(createObjectURL.mock.calls[0]?.[0]).toBeInstanceOf(Blob);
    expect(click).toHaveBeenCalledOnce();
    expect(revokeObjectURL).toHaveBeenCalledWith('blob:empty-source');
    wrapper.unmount();
  });

  it('leaves fullscreen mode with Escape', async () => {
    const wrapper = mount(CodeViewer, {
      global: { stubs: { CodeEditor: true } },
      props: { content: 'echo ready', filename: 'scripts/check.sh' },
    });
    const fullscreenButton = wrapper
      .findAll('button')
      .find((button) => button.text() === '全屏');

    await fullscreenButton?.trigger('click');
    expect(wrapper.classes()).toContain('is-fullscreen');
    expect(
      wrapper
        .findAll('button')
        .find((button) => button.text() === '退出全屏')
        ?.attributes('aria-pressed'),
    ).toBe('true');

    window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
    await wrapper.vm.$nextTick();

    expect(wrapper.classes()).not.toContain('is-fullscreen');
    wrapper.unmount();
  });
});
