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

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

import { defaultPosition, defaultWatermark } from '../model';
import PaperPreview from '../PaperPreview.vue';

const mocks = vi.hoisted(() => ({ preview: vi.fn() }));
vi.mock('#/api/x/dms/watermark', () => ({ getWatermarkPreview: mocks.preview }));

beforeEach(() => {
  vi.clearAllMocks();
  vi.stubGlobal(
    'Image',
    class {
      listeners = new Map<string, () => void>();
      get src() {
        return 'blob:preview';
      }
      set src(_: string) {
        queueMicrotask(() => this.listeners.get('load')?.());
      }
      addEventListener(event: string, listener: () => void) {
        this.listeners.set(event, listener);
      }
    },
  );
  vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:preview');
  vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => {});
  mocks.preview.mockResolvedValue(new Blob(['preview'], { type: 'image/png' }));
});
afterEach(() => {
  vi.restoreAllMocks();
  vi.unstubAllGlobals();
});

describe('server-rendered watermark preview', () => {
  it.each([
    ['portrait', 210, 297, 420, 594],
    ['landscape', 297, 210, 594, 420],
  ] as const)('uses the export renderer and configured font in %s', async (orientation, width, height, canvasWidth, canvasHeight) => {
    const context = { clearRect: vi.fn(), drawImage: vi.fn() };
    vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(context as unknown as CanvasRenderingContext2D);
    const config = {
      ...defaultWatermark(),
      contentTemplate: '用户：{/CUN}',
      fontFamily: 'Directory Font',
      horizontalAlign: 'RIGHT' as const,
      verticalAlign: 'BOTTOM' as const,
    };
    const position = defaultPosition();
    const wrapper = mount(PaperPreview, { props: { config, position, orientation } });
    await flushPromises();
    expect(mocks.preview).toHaveBeenCalledWith(
      width,
      height,
      expect.objectContaining({
        fontFamily: 'Directory Font',
        horizontalAlign: 'RIGHT',
        verticalAlign: 'BOTTOM',
        positions: [position],
      }),
      '用户：张三',
    );
    expect(context.drawImage).toHaveBeenCalledWith(expect.anything(), 0, 0, canvasWidth, canvasHeight);
    expect(URL.revokeObjectURL).toHaveBeenCalledWith('blob:preview');
    expect(wrapper.find('.render-error').exists()).toBe(false);
    wrapper.unmount();
  });

  it('displays font errors from the renderer instead of substituting a browser font', async () => {
    const context = { clearRect: vi.fn(), drawImage: vi.fn() };
    vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(context as unknown as CanvasRenderingContext2D);
    mocks.preview.mockResolvedValue(new Blob([JSON.stringify({ msg: '配置字体目录中不存在字体：SimHei' })], { type: 'application/json' }));
    const wrapper = mount(PaperPreview, { props: { config: defaultWatermark(), position: defaultPosition(), orientation: 'portrait' } });
    await flushPromises();
    expect(wrapper.get('.render-error').text()).toContain('配置字体目录中不存在字体');
    expect(context.drawImage).not.toHaveBeenCalled();
    wrapper.unmount();
  });

  it('does not draw a request that finishes after the preview is closed', async () => {
    const context = { clearRect: vi.fn(), drawImage: vi.fn() };
    vi.spyOn(HTMLCanvasElement.prototype, 'getContext').mockReturnValue(context as unknown as CanvasRenderingContext2D);
    let finish!: (blob: Blob) => void;
    mocks.preview.mockReturnValue(
      new Promise<Blob>((resolve) => {
        finish = resolve;
      }),
    );
    const wrapper = mount(PaperPreview, { props: { config: defaultWatermark(), position: defaultPosition(), orientation: 'portrait' } });
    wrapper.unmount();
    finish(new Blob(['preview'], { type: 'image/png' }));
    await flushPromises();
    expect(context.drawImage).not.toHaveBeenCalled();
    expect(URL.createObjectURL).not.toHaveBeenCalled();
  });
});
