import DOMPurify from 'dompurify';
import type { Transformer } from './types';
// import { diff } from '@/utils/diff';

const DOCTYPE_XHTML11 = `<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">`;

export const sanitizerTransformer: Transformer = {
  name: 'sanitizer',

  transform: async (ctx) => {
    const allowScript = ctx.viewSettings.allowScript;
    if (allowScript) return ctx.content;

    const result = ctx.content.replaceAll('&nbsp;', '&#160;');

    const sanitized = DOMPurify.sanitize(result, {
      WHOLE_DOCUMENT: true,
      FORBID_TAGS: ['script', 'iframe', 'object', 'embed'],
      FORBID_ATTR: ['srcdoc'],
      ALLOWED_URI_REGEXP:
        /^(?:(?:(?:f|ht)tps?|mailto|tel|callto|sms|cid|xmpp|blob|data):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i,
      ADD_TAGS: ['link', 'meta'],
      ADD_ATTR: (attributeName: string) => {
        const attrWhitelist = [
          'xmlns',
          'http-equiv',
          'content',
          'charset',
          'link',
          'vlink',
          'imt-state', // custom attribute generated by some translators
          'data-wr-footernote', // custom attribute for weread footnotes
          'zy-footnote', // custom attribute for zhangyue footnotes
          'cfi-inert', // custom attribute to mark nodes as inert for CFI processing
          'cfi-skip', // custom attribute to mark nodes to be skipped in CFI processing
        ];
        return (
          attrWhitelist.includes(attributeName) ||
          attributeName.startsWith('xml:') ||
          attributeName.startsWith('xmlns:') ||
          attributeName.startsWith('epub:')
        );
      },
      RETURN_DOM: true,
    });

    const serializer = new XMLSerializer();
    let serialized = serializer.serializeToString(sanitized);
    serialized = serialized.replaceAll('&#160;', '&nbsp;').replaceAll('\u00A0', '&nbsp;');
    serialized = '<?xml version="1.0" encoding="utf-8"?>' + DOCTYPE_XHTML11 + serialized;
    serialized = serialized.replace(/(<head[^>]*>)/i, '\n$1');
    serialized = serialized.replace(/(<\/body>)(<\/html>)/i, '$1\n$2');

    // console.log(`Sanitizer diff:\n${diff(result, serialized)}`);

    return serialized;
  },
};
