import createDOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';

const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window as any);

/**
 * Configuration options for HTML sanitization
 */
export interface SanitizerOptions {
  /**
   * List of allowed HTML tags
   * @default ['a', 'b', 'br', 'code', 'div', 'em', 'i', 'li', 'ol', 'p', 'pre', 'span', 'strong', 'ul']
   */
  allowedTags?: string[];

  /**
   * List of allowed HTML attributes
   * @default ['href', 'target', 'class', 'id', 'style']
   */
  allowedAttributes?: string[] | Record<string, string[]>;

  /**
   * Whether to allow data attributes (data-*)
   * @default false
   */
  allowDataAttributes?: boolean;

  /**
   * Whether to strip all HTML and return text only
   * @default false
   */
  stripAllTags?: boolean;
}

/**
 * Default sanitizer options with safe defaults
 */
const DEFAULT_OPTIONS: SanitizerOptions = {
  allowedTags: ['a', 'b', 'br', 'code', 'div', 'em', 'i', 'li', 'ol', 'p', 'pre', 'span', 'strong', 'ul'],
  allowedAttributes: ['href', 'target', 'class', 'id', 'style'],
  allowDataAttributes: false,
  stripAllTags: false,
};

/**
 * HTML Sanitizer class for preventing XSS attacks
 * Uses DOMPurify under the hood with configurable options
 */
export class HtmlSanitizer {
  private options: SanitizerOptions;

  /**
   * Create a new HtmlSanitizer instance
   * @param options Sanitizer configuration options
   */
  constructor(options: SanitizerOptions = {}) {
    this.options = { ...DEFAULT_OPTIONS, ...options };
  }

  /**
   * Sanitize HTML content to prevent XSS attacks
   * @param html HTML content to sanitize
   * @returns Sanitized HTML
   */
  sanitize(html: string): string {
    if (this.options.stripAllTags) {
      return DOMPurify.sanitize(html, { ALLOWED_TAGS: [] }) as unknown as string;
    }

    const config: any = {
      ALLOWED_TAGS: this.options.allowedTags,
      ALLOW_DATA_ATTR: this.options.allowDataAttributes,
    };

    // Handle allowedAttributes which can be string[] or Record<string, string[]>
    if (Array.isArray(this.options.allowedAttributes)) {
      config.ALLOWED_ATTR = this.options.allowedAttributes;
    } else if (this.options.allowedAttributes) {
      config.ALLOWED_ATTR = this.options.allowedAttributes;
    }

    return DOMPurify.sanitize(html, config) as unknown as string;
  }

  /**
   * Update sanitizer options
   * @param options New sanitizer options
   */
  updateOptions(options: Partial<SanitizerOptions>): void {
    this.options = { ...this.options, ...options };
  }

  /**
   * Create a sanitizer with strict settings (minimal allowed tags)
   * @returns A new HtmlSanitizer instance with strict settings
   */
  static createStrict(): HtmlSanitizer {
    return new HtmlSanitizer({
      allowedTags: ['b', 'em', 'i', 'strong', 'span'],
      allowedAttributes: ['class'],
      allowDataAttributes: false,
    });
  }

  /**
   * Create a sanitizer that strips all HTML tags
   * @returns A new HtmlSanitizer instance that strips all HTML
   */
  static createTextOnly(): HtmlSanitizer {
    return new HtmlSanitizer({
      stripAllTags: true,
    });
  }
}

/**
 * Create a default HTML sanitizer instance
 */
export const defaultSanitizer = new HtmlSanitizer();

/**
 * Sanitize HTML content using the default sanitizer
 * @param html HTML content to sanitize
 * @returns Sanitized HTML
 */
export function sanitizeHtml(html: string): string {
  return defaultSanitizer.sanitize(html);
}
