/**
 * PDF Processing Utilities
 *
 * Provides functions for:
 * - Extracting text from PDF files
 * - Calculating SHA-256 checksums for deduplication
 * - Estimating token counts for chunking decisions
 */

import pdf from 'npm:pdf-parse@1.1.1';

/**
 * Extract text content from a PDF buffer
 *
 * @param buffer - PDF file as Uint8Array or Buffer
 * @returns Extracted text and metadata
 */
export async function extractTextFromPDF(
  buffer: Uint8Array | ArrayBuffer
): Promise<{
  text: string;
  pageCount: number;
  metadata: {
    title?: string;
    author?: string;
    creator?: string;
    producer?: string;
    creationDate?: Date;
  };
}> {
  try {
    // Convert to Uint8Array if needed (pdf-parse can handle Uint8Array in Deno)
    const pdfBuffer = buffer instanceof Uint8Array
      ? buffer
      : new Uint8Array(buffer);

    // Parse PDF
    const data = await pdf(pdfBuffer);

    // Clean up extracted text
    const cleanText = data.text
      .replace(/\r\n/g, '\n') // Normalize line endings
      .replace(/\n{3,}/g, '\n\n') // Remove excessive newlines
      .trim();

    return {
      text: cleanText,
      pageCount: data.numpages,
      metadata: {
        title: data.info?.Title,
        author: data.info?.Author,
        creator: data.info?.Creator,
        producer: data.info?.Producer,
        creationDate: data.info?.CreationDate,
      },
    };
  } catch (error) {
    console.error('PDF extraction failed:', error);
    throw new Error(
      `Failed to extract text from PDF: ${error instanceof Error ? error.message : 'Unknown error'}`
    );
  }
}

/**
 * Calculate SHA-256 checksum of a file buffer
 *
 * Used for deduplication - same file content = same checksum
 *
 * @param buffer - File content as Uint8Array or ArrayBuffer
 * @returns Hex-encoded SHA-256 hash (64 characters)
 */
export async function calculateChecksum(
  buffer: Uint8Array | ArrayBuffer
): Promise<string> {
  try {
    // Convert to Uint8Array if needed
    const data = buffer instanceof Uint8Array
      ? buffer
      : new Uint8Array(buffer);

    // Calculate SHA-256 hash
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);

    // Convert to hex string
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray
      .map(b => b.toString(16).padStart(2, '0'))
      .join('');

    return hashHex;
  } catch (error) {
    console.error('Checksum calculation failed:', error);
    throw new Error(
      `Failed to calculate checksum: ${error instanceof Error ? error.message : 'Unknown error'}`
    );
  }
}

/**
 * Estimate token count for text
 *
 * Uses a rough approximation: ~4 characters per token
 * This is conservative for most languages
 *
 * @param text - Text to estimate tokens for
 * @returns Estimated token count
 */
export function estimateTokenCount(text: string): number {
  // Rough approximation: 1 token ≈ 4 characters
  // This is conservative and works well for English and most languages
  const estimatedTokens = Math.ceil(text.length / 4);

  return estimatedTokens;
}

/**
 * Validate PDF file
 *
 * Checks if buffer is a valid PDF by looking for PDF magic number
 *
 * @param buffer - File buffer to validate
 * @returns true if valid PDF
 */
export function isValidPDF(buffer: Uint8Array | ArrayBuffer): boolean {
  try {
    const data = buffer instanceof Uint8Array
      ? buffer
      : new Uint8Array(buffer);

    // Check for PDF magic number: %PDF-
    if (data.length < 5) return false;

    const header = String.fromCharCode(...data.slice(0, 5));
    return header === '%PDF-';
  } catch {
    return false;
  }
}

/**
 * Get file size in human-readable format
 *
 * @param bytes - File size in bytes
 * @returns Formatted string (e.g., "2.5 MB")
 */
export function formatFileSize(bytes: number): string {
  const units = ['B', 'KB', 'MB', 'GB'];
  let size = bytes;
  let unitIndex = 0;

  while (size >= 1024 && unitIndex < units.length - 1) {
    size /= 1024;
    unitIndex++;
  }

  return `${size.toFixed(2)} ${units[unitIndex]}`;
}

/**
 * Extract metadata-only from PDF (without full text extraction)
 *
 * Useful for quick file validation
 *
 * @param buffer - PDF file buffer
 * @returns Basic PDF metadata
 */
export async function extractPDFMetadata(
  buffer: Uint8Array | ArrayBuffer
): Promise<{
  pageCount: number;
  info: {
    title?: string;
    author?: string;
    creator?: string;
    producer?: string;
  };
}> {
  try {
    const pdfBuffer = buffer instanceof Uint8Array
      ? buffer
      : new Uint8Array(buffer);

    // Parse only metadata (no text extraction)
    const data = await pdf(pdfBuffer, {
      max: 0, // Don't extract pages
    });

    return {
      pageCount: data.numpages,
      info: {
        title: data.info?.Title,
        author: data.info?.Author,
        creator: data.info?.Creator,
        producer: data.info?.Producer,
      },
    };
  } catch (error) {
    console.error('PDF metadata extraction failed:', error);
    throw new Error(
      `Failed to extract PDF metadata: ${error instanceof Error ? error.message : 'Unknown error'}`
    );
  }
}

/**
 * Sanitize extracted text for storage
 *
 * Removes control characters and excessive whitespace
 *
 * @param text - Raw extracted text
 * @returns Cleaned text
 */
export function sanitizeExtractedText(text: string): string {
  return text
    // Remove control characters except newlines and tabs
    .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '')
    // Normalize whitespace
    .replace(/[ \t]+/g, ' ')
    // Remove excessive newlines
    .replace(/\n{4,}/g, '\n\n\n')
    // Trim
    .trim();
}
