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

import { withDmsPreviewTicketRefresh } from '../helpers/dmsPreviewTicket';
import type { NormalizedOfficeDocumentConfig } from '../types';

const expired = () => new Error('DMS 预览票据无效或已过期');
function setup() {
  const refresh = vi
    .fn()
    .mockResolvedValue({ accessTicket: 'new', expiresAt: new Date(Date.now() + 300000).toISOString(), fileVersionId: 'v1', fileName: 'a.pdf' });
  const config: NormalizedOfficeDocumentConfig = {
    file: { fileId: 'v1', name: 'a.pdf' },
    fileType: 'dms',
    mode: 'view',
    title: 'a',
    dmsAccessTicket: 'old',
    refreshDmsPreviewTicket: refresh,
  };
  return { config, refresh };
}

describe('DMS preview ticket recovery', () => {
  it('refreshes immediately and retries with the new ticket', async () => {
    const { config, refresh } = setup();
    const load = vi
      .fn()
      .mockRejectedValueOnce(expired())
      .mockImplementationOnce(async () => config.dmsAccessTicket);
    await expect(withDmsPreviewTicketRefresh(config, () => true, load)).resolves.toBe('new');
    expect(refresh).toHaveBeenCalledTimes(1);
    expect(load).toHaveBeenCalledTimes(2);
  });
  it('refreshes a known expired ticket before loading', async () => {
    const { config, refresh } = setup();
    config.dmsPreviewExpiresAt = new Date(0).toISOString();
    const load = vi.fn(async () => config.dmsAccessTicket);
    await expect(withDmsPreviewTicketRefresh(config, () => true, load)).resolves.toBe('new');
    expect(refresh).toHaveBeenCalledTimes(1);
    expect(load).toHaveBeenCalledTimes(1);
  });
  it('stops after three retries', async () => {
    const { config, refresh } = setup();
    const load = vi.fn().mockRejectedValue(expired());
    await expect(withDmsPreviewTicketRefresh(config, () => true, load)).rejects.toThrow('已过期');
    expect(refresh).toHaveBeenCalledTimes(3);
    expect(load).toHaveBeenCalledTimes(4);
  });
  it('can recover on the third retry', async () => {
    const { config, refresh } = setup();
    const load = vi.fn().mockRejectedValueOnce(expired()).mockRejectedValueOnce(expired()).mockRejectedValueOnce(expired()).mockResolvedValueOnce('ready');
    await expect(withDmsPreviewTicketRefresh(config, () => true, load)).resolves.toBe('ready');
    expect(refresh).toHaveBeenCalledTimes(3);
    expect(load).toHaveBeenCalledTimes(4);
  });
  it('does not retry permission errors', async () => {
    const { config, refresh } = setup();
    await expect(withDmsPreviewTicketRefresh(config, () => true, vi.fn().mockRejectedValue(new Error('没有权限')))).rejects.toThrow('没有权限');
    expect(refresh).not.toHaveBeenCalled();
  });
  it('does not load after the window is closed during refresh', async () => {
    const { config, refresh } = setup();
    let current = true;
    refresh.mockImplementation(async () => {
      current = false;
      return {};
    });
    const load = vi.fn().mockRejectedValue(expired());
    await withDmsPreviewTicketRefresh(config, () => current, load);
    expect(load).toHaveBeenCalledTimes(1);
    expect(config.dmsAccessTicket).toBe('old');
  });
});
