/**
 * Cloudflare Email Worker for Trips App
 * Receives emails at travel@quadjourney.com and forwards to trips API for parsing
 */

export default {
  async fetch(request, env, ctx) {
    return new Response('Trips Email Worker - Send emails to travel@quadjourney.com', {
      headers: { 'Content-Type': 'text/plain' }
    });
  },

  async email(message, env, ctx) {
    console.log(`Received email from: ${message.from}`);
    console.log(`Subject: ${message.headers.get('subject')}`);

    try {
      // Read the raw email
      const rawEmail = await new Response(message.raw).text();

      // Debug: log first 500 chars and search for Content-Type
      console.log('Raw email preview:', rawEmail.substring(0, 500));

      // Find where Content-Type header is
      const ctIndex = rawEmail.indexOf('Content-Type:');
      console.log('Content-Type header at position:', ctIndex);
      if (ctIndex > 0) {
        console.log('Content-Type section:', rawEmail.substring(ctIndex, ctIndex + 200));
      }

      // Extract email parts
      const subject = decodeRFC2047(message.headers.get('subject') || '');
      const from = message.from;

      // Parse email body and attachments
      const { textBody, htmlBody, attachments } = parseEmail(rawEmail);

      console.log('Parsed text length:', textBody.length);
      console.log('Parsed html length:', htmlBody.length);

      // Prepare the content for the trips API
      const bodyContent = textBody || stripHtml(htmlBody) || '(No text content)';
      const emailContent = `
Subject: ${subject}
From: ${from}
Date: ${new Date().toISOString()}

${bodyContent}
      `.trim();

      console.log('Final content length:', emailContent.length);

      // Send to trips API
      const result = await sendToTripsAPI(env, emailContent, attachments);

      console.log('Parse result:', JSON.stringify(result));

      // Optionally forward the email to a backup address
      if (env.FORWARD_TO) {
        await message.forward(env.FORWARD_TO);
      }

    } catch (error) {
      console.error('Error processing email:', error);
      if (env.FORWARD_TO) {
        await message.forward(env.FORWARD_TO);
      }
    }
  }
};

/**
 * Decode RFC 2047 encoded words (=?UTF-8?Q?...?= or =?UTF-8?B?...?=)
 */
function decodeRFC2047(str) {
  if (!str) return '';

  // Match encoded words
  return str.replace(/=\?([^?]+)\?([BQ])\?([^?]*)\?=/gi, (match, charset, encoding, text) => {
    try {
      if (encoding.toUpperCase() === 'B') {
        // Base64
        return atob(text);
      } else if (encoding.toUpperCase() === 'Q') {
        // Quoted-printable
        return decodeQuotedPrintable(text.replace(/_/g, ' '));
      }
    } catch (e) {
      console.error('Failed to decode RFC2047:', e);
    }
    return match;
  });
}

/**
 * Decode quoted-printable encoding
 */
function decodeQuotedPrintable(str) {
  return str
    .replace(/=\r?\n/g, '') // Remove soft line breaks
    .replace(/=([0-9A-Fa-f]{2})/g, (match, hex) => {
      return String.fromCharCode(parseInt(hex, 16));
    });
}

/**
 * Parse email to extract body and attachments (iterative, handles nested multipart)
 */
function parseEmail(rawEmail) {
  const result = {
    textBody: '',
    htmlBody: '',
    attachments: []
  };

  // Normalize line endings to \r\n
  const normalizedEmail = rawEmail.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');

  // Collect all parts to process (use a queue instead of recursion)
  const partsToProcess = [normalizedEmail];
  const processedBoundaries = new Set();

  while (partsToProcess.length > 0) {
    const content = partsToProcess.shift();

    // Find boundary - search more of the content since headers can be long
    // First try to find the Content-Type header with boundary
    const boundaryMatch = content.match(/Content-Type:.*?boundary="?([^"\r\n;]+)"?/is);

    console.log('Searching for boundary, found:', boundaryMatch ? boundaryMatch[1] : 'NONE');

    if (boundaryMatch) {
      const boundary = boundaryMatch[1].trim();
      console.log('Found boundary:', boundary);

      // Skip if we've already processed this boundary
      if (processedBoundaries.has(boundary)) {
        continue;
      }
      processedBoundaries.add(boundary);

      // Split by boundary
      const parts = content.split('--' + boundary);
      console.log('Split into', parts.length, 'parts');

      for (let i = 1; i < parts.length; i++) { // Skip first part (preamble)
        const part = parts[i];
        if (part.trim() === '--' || part.trim() === '') continue;

        // Check if this part has its own boundary (nested multipart)
        const partHeader = part.substring(0, 1000);
        const nestedBoundary = partHeader.match(/boundary="?([^"\r\n;]+)"?/i);
        if (nestedBoundary) {
          console.log('Found nested boundary:', nestedBoundary[1]);
          // Add to queue for processing
          partsToProcess.push(part);
        } else if (part.includes('Content-Type:')) {
          // Extract content from this part
          extractPartContent(part, result);
        }
      }
    } else {
      // Not multipart - try to extract content directly
      const bodyStart = content.indexOf('\r\n\r\n');
      if (bodyStart !== -1) {
        const headers = content.substring(0, bodyStart).toLowerCase();
        const body = content.substring(bodyStart + 4);

        if (!result.textBody && !headers.includes('content-type:')) {
          // Plain text email without explicit content-type
          result.textBody = body;
        }
      }
    }
  }

  // Decode quoted-printable in results
  if (result.textBody) {
    result.textBody = decodeQuotedPrintable(result.textBody);
  }
  if (result.htmlBody) {
    result.htmlBody = decodeQuotedPrintable(result.htmlBody);
  }

  return result;
}

/**
 * Extract content from a single MIME part
 */
function extractPartContent(part, result) {
  const contentTypeMatch = part.match(/Content-Type:\s*([^\r\n;]+)/i);
  const contentType = contentTypeMatch ? contentTypeMatch[1].toLowerCase().trim() : '';
  const contentDisposition = part.match(/Content-Disposition:\s*([^\r\n]+)/i)?.[1] || '';

  console.log('Extracting part with Content-Type:', contentType);

  // Find the body (after double newline)
  const bodyStart = part.indexOf('\r\n\r\n');
  if (bodyStart === -1) {
    console.log('No body separator found in part');
    return;
  }

  let body = part.substring(bodyStart + 4);
  console.log('Body length before trim:', body.length);

  // Remove trailing boundary markers
  const boundaryIndex = body.indexOf('\r\n--');
  if (boundaryIndex !== -1) {
    body = body.substring(0, boundaryIndex);
  }

  body = body.trim();
  if (!body) return;

  // Check encoding
  const isBase64 = part.toLowerCase().includes('content-transfer-encoding: base64');
  const isQuotedPrintable = part.toLowerCase().includes('content-transfer-encoding: quoted-printable');

  // Decode if needed
  if (isBase64) {
    try {
      body = atob(body.replace(/\s/g, ''));
    } catch (e) {
      console.error('Base64 decode failed:', e);
    }
  } else if (isQuotedPrintable) {
    body = decodeQuotedPrintable(body);
  }

  // Categorize the content
  if (contentType.includes('text/plain') && !contentDisposition.includes('attachment')) {
    // Append to existing text (for forwarded emails with multiple text parts)
    result.textBody += (result.textBody ? '\n\n' : '') + body;
  } else if (contentType.includes('text/html') && !contentDisposition.includes('attachment')) {
    result.htmlBody += (result.htmlBody ? '\n\n' : '') + body;
  } else if (contentDisposition.includes('attachment') ||
             contentType.includes('application/pdf') ||
             contentType.includes('image/')) {
    const filenameMatch = contentDisposition.match(/filename="?([^";\r\n]+)"?/i);
    const filename = filenameMatch ? filenameMatch[1] : 'attachment';

    result.attachments.push({
      filename,
      contentType,
      data: body.replace(/\s/g, ''),
      isBase64: isBase64
    });
  }
}

/**
 * Strip HTML tags from content
 */
function stripHtml(html) {
  if (!html) return '';
  return html
    .replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
    .replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
    .replace(/<[^>]+>/g, ' ')
    .replace(/&nbsp;/g, ' ')
    .replace(/&amp;/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/\s+/g, ' ')
    .trim();
}

/**
 * Send parsed content to Trips API
 */
async function sendToTripsAPI(env, textContent, attachments) {
  const apiUrl = env.TRIPS_API_URL || 'https://trips.quadjourney.com';
  const apiKey = env.TRIPS_API_KEY;

  if (!apiKey) {
    throw new Error('TRIPS_API_KEY not configured');
  }

  // If we have PDF/image attachments, send the first one
  if (attachments.length > 0) {
    const attachment = attachments[0];

    const formData = new FormData();

    try {
      const binaryData = atob(attachment.data);
      const bytes = new Uint8Array(binaryData.length);
      for (let i = 0; i < binaryData.length; i++) {
        bytes[i] = binaryData.charCodeAt(i);
      }
      const blob = new Blob([bytes], { type: attachment.contentType });
      formData.append('file', blob, attachment.filename);
    } catch (e) {
      console.error('Failed to process attachment:', e);
    }

    formData.append('text', textContent);

    const response = await fetch(`${apiUrl}/api/parse-email`, {
      method: 'POST',
      headers: {
        'X-API-Key': apiKey
      },
      body: formData
    });

    return await response.json();
  }

  // Text only
  const response = await fetch(`${apiUrl}/api/parse-email`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': apiKey
    },
    body: JSON.stringify({ text: textContent })
  });

  return await response.json();
}
