import * as pdfjsLib from 'pdfjs-dist';

// Configure worker - using CDNs since dynamic import in browser might be tricky in some envs
// In a real prod build, you might want to copy the worker file to public assets
pdfjsLib.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js`;

/**
 * Extracts text content from a PDF file
 * @param {File} file 
 * @returns {Promise<string>}
 */
async function extractTextFromPDF(file) {
  const arrayBuffer = await file.arrayBuffer();
  const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
  
  let fullText = '';
  
  // Iterate through each page
  for (let i = 1; i <= pdf.numPages; i++) {
    const page = await pdf.getPage(i);
    const textContent = await page.getTextContent();
    const pageText = textContent.items.map(item => item.str).join(' ');
    fullText += pageText + '\n';
  }
  
  return fullText;
}

/**
 * Parses raw text to extract bill details
 * @param {string} text 
 * @returns {Object} Extracted data
 */
function parseBillText(text) {
  const normalizedText = text.replace(/\s+/g, ' ');

  // Regex patterns
  const patterns = {
    // Amounts often have $ sign or appear as decimals at the end of lines
    amount: /(\$?\s?[\d,]+\.\d{2})/gi,
    // Dates often follow keywords or standard formats MM/DD/YYYY
    date: /(\d{1,2}[\/\-\.]\d{1,2}[\/\-\.]\d{2,4})|((Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]* \d{1,2},? \d{4})/gi,
    // Invoice numbers often follow keywords
    invoice: /(?:Invoice|Inv|Reference|Bill|Order)\s*(?:#|No\.?|Number)?\s*:?\s*([A-Z0-9\-_]+)/i,
    // PO numbers
    po: /(?:P\.?O\.?|Purchase\s*Order)\s*(?:#|No\.?|Number)?\s*:?\s*([A-Z0-9\-_]+)/i,
    // Email addresses
    email: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
  };

  // Extraction logic
  const extracted = {
    vendor_name: '', // Hard to extract accurately without NLP or dictionary, usually first few lines
    invoice_number: '',
    bill_date: '',
    due_date: '',
    amount: 0,
    description: '',
    line_items: [],
    raw_text: text,
    confidence: {
      vendor: 'low',
      amount: 'low',
      date: 'low'
    }
  };

  // 1. Amount Extraction (Find the largest dollar amount usually implies total)
  const amountMatches = text.match(patterns.amount);
  if (amountMatches) {
    const amounts = amountMatches.map(a => parseFloat(a.replace(/[$,\s]/g, ''))).filter(n => !isNaN(n));
    if (amounts.length > 0) {
      // Usually the largest amount is the total
      extracted.amount = Math.max(...amounts);
      extracted.confidence.amount = 'medium';
    }
  }

  // 2. Date Extraction
  const dateMatches = text.match(patterns.date);
  if (dateMatches) {
    // Sort dates, often the first is invoice date, later ones might be due dates
    // This is heuristic
    extracted.bill_date = new Date(dateMatches[0]).toISOString().split('T')[0];
    
    // Attempt to find specific "Due Date" label
    const dueDateLabel = text.match(/(?:Due\s*Date|Payment\s*Due)\s*:?\s*(\d{1,2}[\/\-\.]\d{1,2}[\/\-\.]\d{2,4})/i);
    if (dueDateLabel && dueDateLabel[1]) {
        try {
            extracted.due_date = new Date(dueDateLabel[1]).toISOString().split('T')[0];
        } catch (e) { /* ignore invalid date parse */ }
    } else if (dateMatches.length > 1) {
       // Fallback: assume the latest date found might be due date if significantly later
       const d1 = new Date(dateMatches[0]);
       const lastD = new Date(dateMatches[dateMatches.length - 1]);
       if (lastD > d1) {
           extracted.due_date = lastD.toISOString().split('T')[0];
       }
    }
    extracted.confidence.date = 'medium';
  }

  // 3. Invoice Number
  const invMatch = text.match(patterns.invoice);
  if (invMatch && invMatch[1]) {
    extracted.invoice_number = invMatch[1].trim();
  }

  // 4. Vendor Name Guessing
  // Simple heuristic: The first non-empty line that doesn't look like a header often contains the vendor name
  const lines = text.split('\n').map(l => l.trim()).filter(l => l.length > 0);
  if (lines.length > 0) {
      // Skip lines that look like "Invoice" or dates only
      for(let i=0; i < Math.min(5, lines.length); i++) {
          const line = lines[i];
          if (!line.match(/invoice|bill to|ship to|page|date/i) && line.length > 3) {
              extracted.vendor_name = line;
              break;
          }
      }
  }

  // 5. Description
  // Default description
  extracted.description = `Invoice ${extracted.invoice_number || 'Unknown'} from ${extracted.vendor_name || 'Unknown Vendor'}`;

  // 6. Basic Line Item Extraction (Very experimental without structured data)
  // We look for lines that end in a number (amount) and have some text before it
  // This is a naive implementation
  const possibleLines = text.split('\n');
  const lineItemRegex = /(.+?)\s+(\$?\d{1,3}(?:,\d{3})*\.\d{2})$/; // Text followed by amount at end of line
  
  possibleLines.forEach(line => {
      const match = line.match(lineItemRegex);
      if (match && match[1].length > 5 && !match[1].match(/total|subtotal|tax|balance|due|amount/i)) {
          const val = parseFloat(match[2].replace(/[$,]/g, ''));
          // Only if it's not the grand total we extracted earlier
          if (val !== extracted.amount && val > 0) {
              extracted.line_items.push({
                  description: match[1].trim(),
                  quantity: 1,
                  unit_price: val,
                  total: val
              });
          }
      }
  });

  return extracted;
}

/**
 * Main function to process PDF bill
 * @param {File} file 
 * @returns {Promise<Object>}
 */
export async function parsePDFBill(file) {
  try {
    const text = await extractTextFromPDF(file);
    const data = parseBillText(text);
    return { success: true, data };
  } catch (error) {
    console.error("PDF Parse Error:", error);
    return { success: false, error: error.message };
  }
}