'use client';

import { useEffect, useRef, useState } from 'react';
import { cn } from '@/lib/utils';

type KatexType = typeof import('katex');

let katexModule: KatexType | null = null;

async function loadKatex(): Promise<KatexType | null> {
  if (typeof window === 'undefined') return null;
  if (katexModule) return katexModule;
  
  const katexLib = await import(/* webpackIgnore: true */ 'katex');
  katexModule = katexLib;
  
  return katexLib;
}

/* SECURITY: Lista de comandos matemáticos seguros (documentación)
const ALLOWED_COMMANDS = new Set([
  '\\frac', '\\sqrt', '\\sum', '\\int', '\\alpha', '\\beta', ...
]); */

// SECURITY: Patrones peligrosos que deben ser bloqueados
const DANGEROUS_PATTERNS = [
  // XSS via href con javascript
  /\\href\s*\{\s*javascript:/i,
  /\\href\s*\{[^}]*javascript:/i,
  /\\href\s*\{[^}]*data:/i,
  /\\href\s*\{[^}]*vbscript:/i,
  // HTML data injection
  /\\htmlData/i,
  /\\htmlStyle/i,
  /\\htmlId/i,
  /\\htmlClass/i,
  // URL patterns
  /\\url\s*\{/i,
  // File inclusion (peligroso)
  /\\input/i,
  /\\include/i,
  /\\includegraphics/i,
  /\\verbatiminput/i,
  /\\lstinputlisting/i,
  // Macros peligrosos
  /\\def\s*\\/i,
  /\\newcommand/i,
  /\\renewcommand/i,
  /\\let\s*\\/i,
  /\\global\s*\\/i,
  /\\edef/i,
  /\\xdef/i,
  /\\gdef/i,
  // CSS/Style injection
  /\\cssId/i,
  /\\class/i,
  /\\id/i,
  /\\style/i,
  /\\data/i,
  // Scripts
  /\\write18/i,
  /\\immediate/i,
  /\\openout/i,
  /\\read/i,
  /\\special/i,
  // Unicode attacks
  /\\char\s*["\']?0x/i,
  /\\char\s*["\']?[0-9]/i,
  /\\Uchar/i,
  // SVG/MathML injection
  /\\svg/i,
  /\\mml/i,
  // Obfuscation
  /\\catcode/i,
  /\\mathchoice\s*\{[^}]*\\/i,
];

// SECURITY: Función de validación de fórmulas
export function validateFormula(formula: string): { isValid: boolean; error?: string } {
  // Check 1: Longitud máxima
  const MAX_FORMULA_LENGTH = 5000;
  if (formula.length > MAX_FORMULA_LENGTH) {
    return { 
      isValid: false, 
      error: `Fórmula demasiado larga. Máximo ${MAX_FORMULA_LENGTH} caracteres permitidos.` 
    };
  }

  // Check 2: Patrones peligrosos
  for (const pattern of DANGEROUS_PATTERNS) {
    if (pattern.test(formula)) {
      return { 
        isValid: false, 
        error: 'Comandos no permitidos detectados. La fórmula contiene elementos potencialmente peligrosos.' 
      };
    }
  }

  // Check 3: Caracteres nulos o de control
  if (/[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x9f]/.test(formula)) {
    return { 
      isValid: false, 
      error: 'Caracteres de control no permitidos detectados.' 
    };
  }

  // Check 4: Unicode bi-directional override (CVE-2021-42574)
  if (/[\u202A-\u202E]/.test(formula)) {
    return { 
      isValid: false, 
      error: 'Caracteres de control de dirección bidireccional no permitidos.' 
    };
  }

  return { isValid: true };
}

// SECURITY: Escape HTML básico para pre-renderizado
export function escapeHtml(unsafe: string): string {
  return unsafe
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#039;');
}

export interface MathFormulaProps {
  formula: string;
  displayMode?: boolean;
  throwOnError?: boolean;
  errorComponent?: React.ReactNode;
  className?: string;
  katexOptions?: import('katex').KatexOptions | undefined;
}

export function MathFormula({
  formula,
  displayMode = false,
  throwOnError = false,
  errorComponent,
  className,
  katexOptions,
}: MathFormulaProps) {
  const containerRef = useRef<HTMLSpanElement>(null);
  const [error, setError] = useState<string | null>(null);
  const [isMounted, setIsMounted] = useState(false);
  const [validationError, setValidationError] = useState<string | null>(null);

  useEffect(() => {
    setIsMounted(true);
  }, []);

  useEffect(() => {
    // SECURITY: Validar fórmula antes de renderizar
    const validation = validateFormula(formula);
    if (!validation.isValid) {
      setValidationError(validation.error ?? 'Fórmula inválida');
      return;
    }
    setValidationError(null);
  }, [formula]);

  useEffect(() => {
    if (!isMounted || !containerRef.current || validationError) return;

    let isActive = true;

    async function renderMath() {
      const katex = await loadKatex();
      if (!isActive || !containerRef.current || !katex) return;

      try {
        // SECURITY: Opciones seguras de KaTeX
        const secureOptions = {
          displayMode,
          throwOnError,
          output: 'html' as const,
          trust: false, // SECURITY: Deshabilitar macros de confianza
          strict: true, // SECURITY: Modo estricto
          maxSize: 500, // SECURITY: Limitar tamaño del output
          maxExpand: 1000, // SECURITY: Limitar expansión de macros
          ...katexOptions,
        };

        katex.render(formula, containerRef.current, secureOptions);
        if (isActive) setError(null);
      } catch (err) {
        const errorMessage = err instanceof Error ? err.message : 'Error rendering formula';
        console.error('KaTeX render error:', errorMessage);
        if (isActive) setError(errorMessage);

        if (throwOnError) {
          throw err;
        }
      }
    }

    void renderMath();

    return () => {
      isActive = false;
    };
  }, [formula, displayMode, throwOnError, katexOptions, isMounted, validationError]);

  if (!isMounted) {
    return (
      <span
        className={cn(
          displayMode ? 'math-formula block text-center' : 'math-inline',
          className
        )}
      >
        {formula}
      </span>
    );
  }

  // SECURITY: Mostrar error de validación con la fórmula original
  if (validationError) {
    return (
      <span
        className={cn(
          displayMode ? 'math-formula block text-center' : 'math-inline',
          'text-destructive border border-destructive/30 rounded px-2 py-1',
          className
        )}
        role="alert"
        aria-live="polite"
        aria-label={`Error de seguridad: ${validationError}`}
      >
        <span className="text-sm font-medium">⚠️ {validationError}</span>
        <br />
        <span className="text-xs text-muted-foreground font-mono mt-1 block">
          Fórmula original: {escapeHtml(formula.substring(0, 100))}
          {formula.length > 100 ? '...' : ''}
        </span>
      </span>
    );
  }

  if (error && errorComponent) {
    return (
      <span
        className={cn(
          displayMode ? 'math-formula block text-center' : 'math-inline',
          'text-destructive',
          className
        )}
        role="alert"
        aria-live="polite"
        aria-label={`Error renderizando fórmula: ${error}`}
      >
        {errorComponent}
        <span className="text-xs text-muted-foreground font-mono mt-1 block">
          Fórmula: {escapeHtml(formula.substring(0, 100))}
          {formula.length > 100 ? '...' : ''}
        </span>
      </span>
    );
  }

  return (
    <span
      ref={containerRef}
      className={cn(
        displayMode ? 'math-formula block text-center' : 'math-inline',
        error && 'text-destructive',
        className
      )}
      aria-label={`Fórmula matemática: ${formula.replace(/\\/g, ' backslash ')}`}
      aria-live="polite"
      role="math"
    />
  );
}

export interface MathBlockProps {
  formula: string;
  className?: string;
  katexOptions?: import('katex').KatexOptions | undefined;
}

export function MathBlock({ formula, className, katexOptions }: MathBlockProps) {
  return (
    <MathFormula
      formula={formula}
      displayMode={true}
      className={cn('my-6', className)}
      katexOptions={katexOptions}
    />
  );
}

export interface MathInlineProps {
  formula: string;
  className?: string;
  katexOptions?: import('katex').KatexOptions | undefined;
}

export function MathInline({ formula, className, katexOptions }: MathInlineProps) {
  return (
    <MathFormula
      formula={formula}
      displayMode={false}
      className={cn(className)}
      katexOptions={katexOptions}
    />
  );
}

export interface MathTextProps {
  text: string;
  className?: string;
  inlineDelimiter?: string;
  displayDelimiter?: string;
}

export function MathText({
  text,
  className,
  inlineDelimiter = '$',
  displayDelimiter = '$$',
}: MathTextProps) {
  const parseMathText = (input: string): React.ReactNode[] => {
    const parts: React.ReactNode[] = [];
    let lastIndex = 0;

    const regex = new RegExp(
      `(${displayDelimiter}([\\s\\S]*?)${displayDelimiter}|${inlineDelimiter}([^${inlineDelimiter}]*)${inlineDelimiter})`,
      'g'
    );

    let match;
    let keyIndex = 0;

    while ((match = regex.exec(input)) !== null) {
      if (match.index > lastIndex) {
        parts.push(input.slice(lastIndex, match.index));
      }

      const fullMatch = match[1] ?? '';
      const content = match[2] ?? match[3] ?? '';
      const isDisplay = match[2] !== undefined;

      parts.push(
        <MathFormula
          key={`math-${keyIndex++}`}
          formula={content.trim()}
          displayMode={isDisplay}
        />
      );

      lastIndex = match.index + fullMatch.length;
    }

    if (lastIndex < input.length) {
      parts.push(input.slice(lastIndex));
    }

    return parts;
  };

  return (
    <span className={cn(className)}>
      {parseMathText(text)}
    </span>
  );
}
