import type { DmsPreviewResourceType } from '#/api/x/dms/preview';
import type { DmsWatermarkParameters } from '#/api/x/dms/watermarkParameters';

import { useGlobSetting } from '@jnpf/hooks';
import { createImgPreview } from '@jnpf/ui';

import { v4 as uuidv4 } from 'uuid';

import { createDmsFilePreviewTicket } from '#/api/x/dms/preview';
import { watermarkParameters } from '#/api/x/dms/watermarkParameters';
import { onlineUtils } from '#/utils/jnpf';

const IMAGE_EXTENSIONS = new Set(['bmp', 'gif', 'jpeg', 'jpg', 'png', 'webp']);
const IMAGE_MIME_TYPES = new Set(['image/bmp', 'image/gif', 'image/jpeg', 'image/png', 'image/webp', 'image/x-ms-bmp']);
const IMAGE_PREVIEW_OPENING_LOCK_MS = 500;
const ONLY_OFFICE_EXTENSIONS = new Set([
  'csv',
  'doc',
  'docm',
  'docx',
  'dot',
  'dotx',
  'htm',
  'html',
  'odp',
  'ods',
  'odt',
  'otp',
  'ots',
  'ott',
  'pdf',
  'pot',
  'potx',
  'pps',
  'ppsx',
  'ppt',
  'pptm',
  'pptx',
  'rtf',
  'txt',
  'xls',
  'xlsm',
  'xlsx',
  'xlt',
  'xltx',
]);
const openingResources = new Set<string>();

export interface OpenDmsFilePreviewOptions extends DmsWatermarkParameters {
  fileName: string;
  mimeType?: string;
  resourceId: string;
  resourceType: DmsPreviewResourceType;
}

function fileExtension(fileName: string) {
  const normalized = fileName.trim().toLowerCase();
  return normalized.includes('.') ? normalized.split('.').pop() || '' : '';
}

export function supportsImagePreview(fileName: string, mimeType?: string) {
  const normalizedMimeType = mimeType?.split(';')[0]?.trim().toLowerCase() || '';
  return IMAGE_EXTENSIONS.has(fileExtension(fileName)) || IMAGE_MIME_TYPES.has(normalizedMimeType);
}

export function supportsOnlyOfficePreview(fileName: string) {
  return ONLY_OFFICE_EXTENSIONS.has(fileExtension(fileName));
}

export function dmsPreviewStreamUrl(fileVersionId: string, accessTicket: string) {
  const apiURL = (useGlobSetting().apiURL || '').replace(/\/+$/, '');
  const query = new URLSearchParams({ ticket: accessTicket });
  return `${apiURL}/api/file/Dms/OnlyOffice/${encodeURIComponent(fileVersionId)}?${query.toString()}`;
}

export async function openDmsFilePreview(options: OpenDmsFilePreviewOptions) {
  if (!supportsImagePreview(options.fileName, options.mimeType) && !supportsOnlyOfficePreview(options.fileName)) {
    throw new Error('OnlyOffice 不支持该文件格式');
  }
  const openingKey = JSON.stringify([options.resourceType, options.resourceId, watermarkParameters(options)]);
  if (openingResources.has(openingKey)) return false;
  openingResources.add(openingKey);
  let releaseDelay = 0;
  try {
    const response = await createDmsFilePreviewTicket({
      ...watermarkParameters(options),
      operationId: uuidv4(),
      resourceId: options.resourceId,
      resourceType: options.resourceType,
    });
    const ticket = response.data;
    if (!ticket?.accessTicket || !ticket.fileVersionId || !ticket.fileName) {
      throw new Error('后端返回的 DMS 预览票据不完整');
    }
    if (supportsImagePreview(ticket.fileName, ticket.mimeType)) {
      createImgPreview({ imageList: [dmsPreviewStreamUrl(ticket.fileVersionId, ticket.accessTicket)] });
      releaseDelay = IMAGE_PREVIEW_OPENING_LOCK_MS;
      return true;
    }
    if (!supportsOnlyOfficePreview(ticket.fileName)) {
      throw new Error('OnlyOffice 不支持该文件格式');
    }
    onlineUtils.openOfficeDocument({
      dmsAccessTicket: ticket.accessTicket,
      dmsPreviewExpiresAt: ticket.expiresAt,
      refreshDmsPreviewTicket: async () => {
        const refreshed = await createDmsFilePreviewTicket({
          ...watermarkParameters(options),
          operationId: crypto.randomUUID(),
          resourceId: ticket.fileVersionId,
          resourceType: 'FILE_VERSION',
        });
        return refreshed.data;
      },
      file: {
        fileId: ticket.fileVersionId,
        fileSize: ticket.sizeBytes,
        name: ticket.fileName,
      },
      fileType: 'dms',
      mode: 'view',
      title: ticket.fileName,
    });
    return true;
  } finally {
    if (releaseDelay) {
      setTimeout(() => openingResources.delete(openingKey), releaseDelay);
    } else {
      openingResources.delete(openingKey);
    }
  }
}

export function dmsPreviewErrorMessage(error: unknown) {
  return error instanceof Error && error.message ? error.message : '文件预览失败';
}
