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

import {
  buildFailureSupplementPrintHtml,
  openFailureSupplementPrintView,
} from '@/components/shared/FailureSupplementPrintView';
import type { StandardizationFailureItem } from '@/services/standardizedService';

function createFailureItem(overrides: Partial<StandardizationFailureItem> = {}): StandardizationFailureItem {
  return {
    id: 'failure-001',
    task_id: 'task-001',
    batch_id: 'BATCH-001',
    source_material_id: 'mat-001',
    original_name: '球墨铸铁井盖',
    original_spec: 'DN700',
    quantity: 12,
    unit: '套',
    invoice_type: '专票',
    standardized_name: '球墨铸铁井盖',
    can_search_1688: false,
    missing_required_fields: ['规格型号'],
    failure_reason: '系统内部失败详情不应出现在补充卡',
    human_fix_hint: '请补充规格型号',
    llm_model: 'glm-5.1',
    created_at: '2026-04-20T10:00:00.000Z',
    updated_at: '2026-04-20T10:00:00.000Z',
    source_snapshot: null,
    ...overrides,
  };
}

function createPrintWindow(readyState: DocumentReadyState = 'complete') {
  const printWindow = {
    document: {
      readyState,
      open: vi.fn(),
      write: vi.fn(),
      close: vi.fn(),
    },
    focus: vi.fn(),
    print: vi.fn(),
    close: vi.fn(),
    addEventListener: vi.fn(),
    removeEventListener: vi.fn(),
  };

  return printWindow as unknown as Window & {
    document: Document & {
      open: ReturnType<typeof vi.fn>;
      write: ReturnType<typeof vi.fn>;
      close: ReturnType<typeof vi.fn>;
    };
    focus: ReturnType<typeof vi.fn>;
    print: ReturnType<typeof vi.fn>;
    close: ReturnType<typeof vi.fn>;
    addEventListener: ReturnType<typeof vi.fn>;
    removeEventListener: ReturnType<typeof vi.fn>;
  };
}

function mockDownloadApis() {
  const clickSpy = vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => {});
  const createObjectUrlMock = vi.fn(() => 'blob:supplement-card');
  const revokeObjectUrlMock = vi.fn();

  Object.defineProperty(window.URL, 'createObjectURL', {
    configurable: true,
    value: createObjectUrlMock,
  });
  Object.defineProperty(window.URL, 'revokeObjectURL', {
    configurable: true,
    value: revokeObjectUrlMock,
  });

  return { clickSpy, createObjectUrlMock, revokeObjectUrlMock };
}

function readBlobText(blob: Blob) {
  return new Promise<string>((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(String(reader.result ?? ''));
    reader.onerror = () => reject(reader.error);
    reader.readAsText(blob);
  });
}

describe('FailureSupplementPrintView', () => {
  afterEach(() => {
    vi.restoreAllMocks();
    document.body.innerHTML = '';
  });

  it('builds printable cards without system identifiers or failure details', () => {
    const html = buildFailureSupplementPrintHtml([createFailureItem()]);

    expect(html).toContain('球墨铸铁井盖');
    expect(html).toContain('请补充规格型号');
    expect(html).toContain('规格型号');
    expect(html).toContain('class="required">*</span>');
    expect(html).toContain('负责人（签字）：');
    expect(html).toContain('盖                章：');
    expect(html).not.toContain('BATCH-001');
    expect(html).not.toContain('task-001');
    expect(html).not.toContain('mat-001');
    expect(html).not.toContain('系统内部失败详情');
  });

  it('writes HTML, waits for the print document to load, then calls print', async () => {
    const printWindow = createPrintWindow('loading');
    let loadHandler: (() => void) | undefined;
    printWindow.addEventListener.mockImplementation((event: string, handler: EventListenerOrEventListenerObject) => {
      if (event === 'load') {
        loadHandler = handler as () => void;
      }
    });
    const openMock = vi.spyOn(window, 'open').mockReturnValue(printWindow);

    const resultPromise = openFailureSupplementPrintView([createFailureItem()]);
    await Promise.resolve();

    expect(printWindow.document.open).toHaveBeenCalled();
    expect(printWindow.document.write).toHaveBeenCalledWith(expect.stringContaining('球墨铸铁井盖'));
    expect(printWindow.document.close).toHaveBeenCalled();
    expect(printWindow.print).not.toHaveBeenCalled();
    expect(openMock.mock.calls[0][2]).not.toContain('noopener');
    expect(openMock.mock.calls[0][2]).not.toContain('noreferrer');

    loadHandler?.();
    await expect(resultPromise).resolves.toEqual({ mode: 'print', count: 1 });
    expect(printWindow.print).toHaveBeenCalled();
  });

  it('downloads the supplement HTML when the print popup is blocked', async () => {
    const { clickSpy, createObjectUrlMock } = mockDownloadApis();
    vi.spyOn(window, 'open').mockReturnValue(null);

    const result = await openFailureSupplementPrintView([createFailureItem()]);
    const blob = createObjectUrlMock.mock.calls[0][0] as Blob;
    const downloadedHtml = await readBlobText(blob);

    expect(result).toMatchObject({ mode: 'download', count: 1, reason: 'popup-blocked' });
    expect(result.mode === 'download' ? result.filename : '').toMatch(/球墨铸铁井盖-BATCH-001-\d{8}T\d{6}\.html/);
    expect(clickSpy).toHaveBeenCalled();
    expect(downloadedHtml).toContain('球墨铸铁井盖');
    expect(downloadedHtml).not.toContain('task-001');
    expect(downloadedHtml).not.toContain('系统内部失败详情');
  });

  it('downloads the supplement HTML and closes the print window when print throws', async () => {
    mockDownloadApis();
    const printWindow = createPrintWindow();
    printWindow.print.mockImplementation(() => {
      throw new Error('print failed');
    });
    vi.spyOn(window, 'open').mockReturnValue(printWindow);

    await expect(openFailureSupplementPrintView([createFailureItem()])).resolves.toMatchObject({
      mode: 'download',
      count: 1,
      reason: 'print-failed',
    });
    expect(printWindow.close).toHaveBeenCalled();
  });
});
