/**
 * Utility functions for downloading reports
 * Extracted from Reports.tsx for better code organization
 */

/**
 * Sanitize filename while preserving Vietnamese characters where possible
 * Falls back to ASCII-only if needed
 */
export const sanitizeFilename = (filename: string, extension: string): string => {
  // Remove or replace invalid filename characters
  // Keep Vietnamese characters, spaces, hyphens, underscores
  let sanitized = filename
    .replace(/[<>:"/\\|?*]/g, '_') // Replace invalid Windows filename chars
    .replace(/\s+/g, ' ') // Normalize whitespace
    .trim();

  // If sanitized is empty or too short, use a default
  if (!sanitized || sanitized.length < 1) {
    sanitized = `BaoCao_${Date.now()}`;
  }

  // Limit length to avoid filesystem issues
  if (sanitized.length > 200) {
    sanitized = sanitized.substring(0, 200);
  }

  return `${sanitized}.${extension}`;
};

/**
 * Clean and validate base64 string
 */
export const cleanBase64 = (base64: string): string => {
  if (!base64 || typeof base64 !== 'string') {
    throw new Error('Base64 string is invalid or empty');
  }

  // Remove whitespace, newlines, tabs, and other control characters
  let cleaned = base64.replace(/[\s\n\r\t\u0000-\u001F\u007F-\u009F]/g, '');
  
  // Decode URI component if it's URL encoded (handles % encoding)
  try {
    cleaned = decodeURIComponent(cleaned);
  } catch (e) {
    // If decodeURIComponent fails, use original string
    // This handles cases where it's not URL encoded
  }
  
  // Handle URL-safe base64 (replace - with + and _ with /)
  cleaned = cleaned.replace(/-/g, '+').replace(/_/g, '/');
  
  // Remove any remaining invalid characters (keep only valid base64 chars)
  cleaned = cleaned.replace(/[^A-Za-z0-9+/=]/g, '');
  
  // Validate base64 characters (only allow A-Z, a-z, 0-9, +, /, =)
  if (!/^[A-Za-z0-9+/=]+$/.test(cleaned)) {
    throw new Error(`Base64 string contains invalid characters. First 100 chars: ${cleaned.substring(0, 100)}`);
  }
  
  // Add padding if needed (base64 strings should be multiple of 4)
  const padding = cleaned.length % 4;
  if (padding !== 0) {
    cleaned += '='.repeat(4 - padding);
  }
  
  return cleaned;
};

/**
 * Decode base64 string safely with multiple fallback methods
 */
export const decodeBase64Safe = (base64: string): Uint8Array => {
  // Method 1: Try standard atob
  try {
    const decoded = atob(base64);
    const bytes = new Uint8Array(decoded.length);
    for (let i = 0; i < decoded.length; i++) {
      bytes[i] = decoded.charCodeAt(i);
    }
    return bytes;
  } catch (e) {
    // Method 2: Try manual base64 decoding
    try {
      const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
      let result = '';
      let i = 0;
      let encoded = base64.replace(/[^A-Za-z0-9+/]/g, '');
      
      while (i < encoded.length) {
        const enc1 = chars.indexOf(encoded.charAt(i++));
        const enc2 = chars.indexOf(encoded.charAt(i++));
        const enc3 = chars.indexOf(encoded.charAt(i++));
        const enc4 = chars.indexOf(encoded.charAt(i++));
        
        const bitmap = (enc1 << 18) | (enc2 << 12) | (enc3 << 6) | enc4;
        
        if (enc3 === 64) {
          result += String.fromCharCode((bitmap >> 16) & 255);
        } else if (enc4 === 64) {
          result += String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255);
        } else {
          result += String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255, bitmap & 255);
        }
      }
      
      const bytes = new Uint8Array(result.length);
      for (let i = 0; i < result.length; i++) {
        bytes[i] = result.charCodeAt(i);
      }
      return bytes;
    } catch (e2) {
      throw new Error(`Both base64 decode methods failed. Original error: ${(e as Error).message || String(e)}`);
    }
  }
};

/**
 * Validate PDF header and structure
 */
const validatePDF = (byteArray: Uint8Array): Uint8Array => {
  if (byteArray.length < 4) {
    throw new Error('PDF data is too short to be valid');
  }
  
  // Check PDF header: %PDF should be at the start
  const pdfHeader = String.fromCharCode(byteArray[0], byteArray[1], byteArray[2], byteArray[3]);
  if (pdfHeader !== '%PDF') {
    // Sometimes PDFs have BOM or other prefixes, check a bit further
    let foundPdfHeader = false;
    let headerOffset = 0;
    for (let i = 0; i < Math.min(100, byteArray.length - 4); i++) {
      const header = String.fromCharCode(
        byteArray[i],
        byteArray[i + 1],
        byteArray[i + 2],
        byteArray[i + 3]
      );
      if (header === '%PDF') {
        foundPdfHeader = true;
        headerOffset = i;
        break;
      }
    }
    
    if (!foundPdfHeader) {
      const preview = Array.from(byteArray.slice(0, 20))
        .map(b => String.fromCharCode(b))
        .join('');
      console.error('Invalid PDF header:', {
        expected: '%PDF',
        found: pdfHeader,
        preview: preview,
        byteArrayLength: byteArray.length,
        firstBytes: Array.from(byteArray.slice(0, 10)).map(b => `0x${b.toString(16).padStart(2, '0')}`).join(' ')
      });
      throw new Error(`PDF header không hợp lệ. File có thể bị corrupt hoặc không phải là PDF hợp lệ. Header tìm thấy: "${pdfHeader}"`);
    }
    
    // If PDF header is not at the start, trim the prefix
    if (headerOffset > 0) {
      console.warn(`PDF header found at offset ${headerOffset}, trimming prefix`);
      byteArray = byteArray.slice(headerOffset);
    }
  }
  
  return byteArray;
};

/**
 * Download file from base64 data URL
 */
export const downloadFromBase64 = async (dataUrl: string, filename: string): Promise<void> => {
  try {
    const trimmedUrl = dataUrl.trim();
    const matches = trimmedUrl.match(/^data:([^;]+);base64,(.+)$/);
    
    if (!matches || matches.length < 3) {
      // Try alternative format without semicolon
      const altMatches = trimmedUrl.match(/^data:([^,]+),(.+)$/);
      if (altMatches && altMatches.length >= 3) {
        const mimeType = altMatches[1] || 'application/pdf';
        let base64Data = altMatches[2];
        
        base64Data = cleanBase64(base64Data);
        let byteArray = decodeBase64Safe(base64Data);
        
        // Validate PDF if it's a PDF
        if (mimeType?.includes('pdf') || filename.toLowerCase().endsWith('.pdf')) {
          byteArray = validatePDF(byteArray);
        }
        
        const blob = new Blob([byteArray], { type: mimeType });
        
        if (blob.size === 0) {
          throw new Error('Created blob is empty after decode');
        }
        
        triggerDownload(blob, filename);
        return;
      }
      throw new Error(`Invalid base64 data URL format. URL length: ${trimmedUrl.length}, starts with: ${trimmedUrl.substring(0, 50)}`);
    }

    const mimeType = matches[1];
    let base64Data = matches[2];

    // Clean and validate base64 string
    base64Data = cleanBase64(base64Data);

    if (!base64Data || base64Data.length === 0) {
      throw new Error('Base64 data is empty after cleaning');
    }

    // Decode base64 safely
    let byteArray: Uint8Array;
    try {
      const binaryString = atob(base64Data);
      byteArray = new Uint8Array(binaryString.length);
      for (let i = 0; i < binaryString.length; i++) {
        byteArray[i] = binaryString.charCodeAt(i);
      }
    } catch (e) {
      byteArray = decodeBase64Safe(base64Data);
    }
    
    // Validate PDF if it's a PDF
    if (mimeType?.includes('pdf') || filename.toLowerCase().endsWith('.pdf')) {
      byteArray = validatePDF(byteArray);
    }
    
    // Create blob with proper MIME type
    const arrayBuffer = new ArrayBuffer(byteArray.length);
    const view = new Uint8Array(arrayBuffer);
    view.set(byteArray);
    
    const blob = new Blob([arrayBuffer], { 
      type: mimeType || 'application/pdf'
    });

    if (blob.size === 0) {
      throw new Error('Created blob is empty');
    }
    
    if (mimeType?.includes('pdf') && blob.size < 100) {
      console.warn('PDF file is suspiciously small:', blob.size, 'bytes');
    }

    triggerDownload(blob, filename);
  } catch (error: any) {
    const errorMessage = error.message || 'Unknown error occurred';
    console.error('Base64 download error details:', {
      error: errorMessage,
      dataUrlLength: dataUrl?.length,
      dataUrlPreview: dataUrl?.substring(0, 200),
      filename
    });
    throw new Error(`Failed to download from base64: ${errorMessage}`);
  }
};

/**
 * Download file from blob
 */
export const downloadFromBlob = (blob: Blob, filename: string): void => {
  if (!blob || blob.size === 0) {
    throw new Error('Blob is empty or invalid');
  }

  triggerDownload(blob, filename);
};

/**
 * Download file from external URL
 */
export const downloadFromUrl = async (url: string, filename: string): Promise<void> => {
  try {
    const response = await fetch(url, {
      method: 'GET',
      mode: 'cors',
    });

    if (response.ok) {
      const blob = await response.blob();
      downloadFromBlob(blob, filename);
    } else {
      // Fallback to direct link
      const link = document.createElement('a');
      link.href = url;
      link.download = filename;
      link.target = '_blank';
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    }
  } catch (error: any) {
    // If fetch fails (CORS issue), fallback to direct link
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    link.target = '_blank';
    document.body.appendChild(link);
    link.click();
    document.body.removeChild(link);
  }
};

/**
 * Trigger browser download
 */
const triggerDownload = (blob: Blob, filename: string): void => {
  const url = URL.createObjectURL(blob);
  const link = document.createElement('a');
  link.href = url;
  link.download = filename;
  link.style.display = 'none';
  document.body.appendChild(link);
  
  setTimeout(() => {
    try {
      link.click();
      setTimeout(() => {
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
      }, 100);
    } catch (clickError: any) {
      document.body.removeChild(link);
      URL.revokeObjectURL(url);
      throw new Error(`Failed to trigger download: ${clickError.message}`);
    }
  }, 10);
};
