// Offscreen document management for audio recording
let creatingOffscreen;

async function setupOffscreenDocument() {
  const offscreenUrl = chrome.runtime.getURL('offscreen.html');

  // Check if offscreen document already exists
  const existingContexts = await chrome.runtime.getContexts({
    contextTypes: ['OFFSCREEN_DOCUMENT'],
    documentUrls: [offscreenUrl]
  });

  if (existingContexts.length > 0) {
    return; // Already exists
  }

  // Create offscreen document
  if (creatingOffscreen) {
    await creatingOffscreen;
  } else {
    creatingOffscreen = chrome.offscreen.createDocument({
      url: offscreenUrl,
      reasons: ['USER_MEDIA'],
      justification: 'Recording audio for transcription'
    });
    await creatingOffscreen;
    creatingOffscreen = null;
  }
}

async function closeOffscreenDocument() {
  try {
    await chrome.offscreen.closeDocument();
  } catch (e) {
    // Document might not exist
  }
}

// Helper to update the side panel for a given tab
async function updateSidePanelForTab(tabId, open) {
  try {
    await chrome.sidePanel.setOptions({
      tabId,
      path: "index.html",
      enabled: open,
    });
  } catch (err) {
    console.error(`Error updating side panel for tab ${tabId}:`, err);
  }
}

chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: "addToCalendar",
    title: "Create Calendar Event",
    contexts: ["selection"],
  });
});

// When a tab is updated, activate the side panel and clear old selections.
chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
  if (changeInfo.status === "complete") {
    chrome.storage.local.get("panelOpen", ({ panelOpen }) => {
      updateSidePanelForTab(tabId, panelOpen ?? true);
    });
    // Use session storage for temporary data (clears when Chrome closes)
    chrome.storage.session.remove(["selectedText", "pageUrl"], () =>
      console.log("Cleared old selection after tab update.")
    );
  }
});

// When a new tab becomes active.
chrome.tabs.onActivated.addListener(({ tabId }) => {
  chrome.storage.local.get("panelOpen", ({ panelOpen }) => {
    updateSidePanelForTab(tabId, panelOpen ?? true);
  });
});

// When a new tab is created.
chrome.tabs.onCreated.addListener((tab) => {
  if (!tab || !tab.id) return;
  chrome.storage.local.get("panelOpen", ({ panelOpen }) => {
    updateSidePanelForTab(tab.id, panelOpen ?? true);
  });
});

// Handle extension icon clicks.
chrome.action.onClicked.addListener((tab) => {
  if (
    !tab ||
    typeof tab.url !== "string" ||
    !(tab.url.startsWith("http://") || tab.url.startsWith("https://"))
  ) {
    console.log("Invalid or restricted URL:", tab);
    return;
  }
  chrome.scripting
    .executeScript({
      target: { tabId: tab.id },
      function: () => {
        try {
          const selection = window.getSelection();
          return selection?.toString().trim() || "";
        } catch (error) {
          console.error("Error fetching selection:", error);
          return "";
        }
      },
    })
    .then((results) => {
      const selectedText = results && results[0]?.result || "";
      if (selectedText) {
        // Use session storage for temporary data (clears when Chrome closes)
        chrome.storage.session.set({ selectedText, pageUrl: tab.url });
        console.log("Stored selection:", selectedText);
      } else {
        chrome.storage.session.remove(["selectedText", "pageUrl"]);
        console.log("No selection found.");
      }
      chrome.storage.local.set({ panelOpen: true }, () => {
        updateSidePanelForTab(tab.id, true);
        // Broadcast sync message to update UI across contexts
        chrome.runtime.sendMessage({ type: "syncState" });
      });
    });
});

// Context menu click – capture selection and open panel.
chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId !== "addToCalendar" || !tab || typeof tab.url !== "string") {
    console.log("Context menu click ignored: invalid tab or URL.");
    return;
  }
  // Use session storage for temporary data, local storage for panel state
  chrome.storage.session.set({
    selectedText: info.selectionText || "",
    pageUrl: tab.url,
  });
  chrome.storage.local.set({ panelOpen: true }, () => {
    chrome.sidePanel
      .open({ tabId: tab.id })
      .catch((err) => console.error("Panel open error:", err));
    chrome.runtime.sendMessage({ type: "syncState" });
  });
});

// Set the panel behavior to open on action icon click
chrome.runtime.onInstalled.addListener(() => {
  chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch((error) => console.error(error));
});


// Listen for React-triggered messages to toggle the panel globally.
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  // Skip messages meant for the offscreen document
  if (message.target === 'offscreen') {
    return false;
  }

  if (message.type === "togglePanel") {
    chrome.storage.local.set({ panelOpen: message.open }, async () => {
      const tabs = await chrome.tabs.query({});
      tabs.forEach((tab) => {
        if (tab?.id) updateSidePanelForTab(tab.id, message.open);
      });
      chrome.runtime.sendMessage({ type: "syncState" });
    });
  }

  // Handle currency conversion API calls from side panel
  if (message.type === "currencyConvert") {
    const { apiKey, fromCurrency, toCurrency, amount } = message;
    fetch(`https://v6.exchangerate-api.com/v6/${apiKey}/pair/${fromCurrency}/${toCurrency}/${amount}`)
      .then(response => response.json())
      .then(data => {
        sendResponse({ success: true, data });
      })
      .catch(error => {
        console.error("Currency API error:", error);
        sendResponse({ success: false, error: error.message });
      });
    return true;
  }

  // Handle historical exchange rates (using Frankfurter API - free, ECB data)
  if (message.type === "fetchExchangeHistory") {
    (async () => {
      const { fromCurrency, toCurrency, days } = message;

      try {
        // Calculate date range
        const endDate = new Date();
        const startDate = new Date();
        startDate.setDate(startDate.getDate() - (days || 30));

        const formatDate = (d) => d.toISOString().split('T')[0];
        const start = formatDate(startDate);
        const end = formatDate(endDate);

        // Frankfurter API for historical rates
        const response = await fetch(
          `https://api.frankfurter.app/${start}..${end}?from=${fromCurrency}&to=${toCurrency}`,
          { signal: AbortSignal.timeout(10000) }
        );

        if (response.ok) {
          const data = await response.json();
          // Transform to array of {date, rate}
          const rates = Object.entries(data.rates).map(([date, rates]) => ({
            date,
            time: new Date(date).getTime(),
            rate: rates[toCurrency]
          })).sort((a, b) => a.time - b.time);

          sendResponse({ success: true, data: rates });
        } else {
          sendResponse({ success: false, error: `API error: ${response.status}` });
        }
      } catch (error) {
        console.error('Exchange history error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle Salesforce OAuth authentication
  if (message.type === "salesforceAuth") {
    const { instanceUrl, clientId } = message;

    // Build OAuth URL for User-Agent flow (implicit grant)
    const redirectUri = chrome.identity.getRedirectURL();
    const authUrl = `${instanceUrl}/services/oauth2/authorize?` +
      `response_type=token&` +
      `client_id=${encodeURIComponent(clientId)}&` +
      `redirect_uri=${encodeURIComponent(redirectUri)}`;

    chrome.identity.launchWebAuthFlow(
      { url: authUrl, interactive: true },
      (redirectUrl) => {
        if (chrome.runtime.lastError) {
          console.error("OAuth error:", chrome.runtime.lastError);
          sendResponse({ success: false, error: chrome.runtime.lastError.message });
          return;
        }

        if (!redirectUrl) {
          sendResponse({ success: false, error: "No redirect URL received" });
          return;
        }

        // Parse the access token from the redirect URL (fragment)
        try {
          const url = new URL(redirectUrl);
          const hash = url.hash.substring(1); // Remove the #
          const params = new URLSearchParams(hash);
          const accessToken = params.get("access_token");
          const instanceUrlFromResponse = params.get("instance_url");

          if (accessToken) {
            // Store the access token
            chrome.storage.local.get('salesforceConfig', (data) => {
              const config = data.salesforceConfig || {};
              config.accessToken = accessToken;
              config.instanceUrl = instanceUrlFromResponse || instanceUrl;
              config.isConnected = true;
              chrome.storage.local.set({ salesforceConfig: config });
            });

            sendResponse({ success: true, accessToken, instanceUrl: instanceUrlFromResponse });
          } else {
            sendResponse({ success: false, error: "No access token in response" });
          }
        } catch (error) {
          console.error("Token parse error:", error);
          sendResponse({ success: false, error: "Failed to parse token" });
        }
      }
    );
    return true;
  }

  // Handle IP address fetching
  if (message.type === "fetchIPAddresses") {
    (async () => {
      const results = { ipv4: null, ipv6: null, ipInfo: null };

      // Fetch IPv4
      try {
        const ipv4Response = await fetch('https://api.ipify.org?format=json');
        const ipv4Data = await ipv4Response.json();
        results.ipv4 = ipv4Data.ip;
      } catch (error) {
        console.error('IPv4 fetch error:', error);
      }

      // Fetch IPv6
      try {
        const ipv6Response = await fetch('https://api64.ipify.org?format=json');
        const ipv6Data = await ipv6Response.json();
        if (ipv6Data.ip && ipv6Data.ip.includes(':')) {
          results.ipv6 = ipv6Data.ip;
        }
      } catch (error) {
        console.error('IPv6 fetch error:', error);
      }

      // Fetch location info using ip-api.com (more generous rate limit)
      try {
        const ipInfoResponse = await fetch('http://ip-api.com/json/?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,org,as,query');
        const data = await ipInfoResponse.json();
        if (data.status === 'success') {
          results.ipInfo = {
            ip: data.query,
            city: data.city,
            region: data.regionName,
            region_code: data.region,
            country_name: data.country,
            country_code: data.countryCode,
            postal: data.zip,
            latitude: data.lat,
            longitude: data.lon,
            timezone: data.timezone,
            org: data.org || data.isp
          };
        } else {
          results.ipInfo = { error: data.message || 'Failed to fetch location info' };
        }
      } catch (error) {
        console.error('IP info fetch error:', error);
        results.ipInfo = { error: 'Failed to fetch location info' };
      }

      sendResponse(results);
    })();
    return true;
  }

  // Handle Salesforce Lead creation
  if (message.type === "salesforceCreateLead") {
    const { accessToken, instanceUrl, leadData } = message;

    // Map to Salesforce Lead API field names
    const sfLead = {
      FirstName: leadData.firstName || '',
      LastName: leadData.lastName || 'Unknown',
      Email: leadData.email || '',
      Phone: leadData.phone || '',
      Company: leadData.company || 'Unknown',
      Title: leadData.title || '',
      Street: leadData.street || '',
      City: leadData.city || '',
      State: leadData.state || '',
      PostalCode: leadData.postalCode || '',
      Country: leadData.country || '',
      Website: leadData.website || '',
      Description: leadData.description || ''
    };

    // Remove empty fields (except required ones)
    Object.keys(sfLead).forEach(key => {
      if (!sfLead[key] && key !== 'LastName' && key !== 'Company') {
        delete sfLead[key];
      }
    });

    fetch(`${instanceUrl}/services/data/v59.0/sobjects/Lead`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(sfLead)
    })
      .then(response => {
        if (!response.ok) {
          return response.json().then(err => {
            throw new Error(err[0]?.message || `HTTP ${response.status}`);
          });
        }
        return response.json();
      })
      .then(data => {
        if (data.success && data.id) {
          // Return the new Lead ID so we can open it
          sendResponse({ success: true, leadId: data.id });
        } else {
          sendResponse({ success: false, error: 'Lead creation failed' });
        }
      })
      .catch(error => {
        console.error("Salesforce API error:", error);
        sendResponse({ success: false, error: error.message });
      });
    return true;
  }

  // Handle Whisper transcription
  if (message.type === "whisperTranscribe") {
    (async () => {
      const { audioData, apiKey } = message;

      console.log('Whisper transcription started');
      console.log('API key present:', !!apiKey, 'length:', apiKey?.length);
      console.log('Audio data present:', !!audioData, 'length:', audioData?.length);

      if (!apiKey) {
        sendResponse({ success: false, error: 'OpenAI API key not provided' });
        return;
      }

      if (!audioData) {
        sendResponse({ success: false, error: 'No audio data received' });
        return;
      }

      try {
        // Convert base64 to blob (without using fetch to avoid CSP issues)
        const base64Parts = audioData.split(',');
        const mimeMatch = base64Parts[0].match(/:(.*?);/);
        const mimeType = mimeMatch ? mimeMatch[1] : 'audio/webm';
        const base64Data = base64Parts[1];

        // Decode base64
        const binaryString = atob(base64Data);
        const bytes = new Uint8Array(binaryString.length);
        for (let i = 0; i < binaryString.length; i++) {
          bytes[i] = binaryString.charCodeAt(i);
        }
        const audioBlob = new Blob([bytes], { type: mimeType });
        console.log('Audio blob size:', audioBlob.size, 'type:', audioBlob.type);

        if (audioBlob.size < 1000) {
          sendResponse({ success: false, error: 'Audio recording too short or empty' });
          return;
        }

        // Determine file extension from mimeType
        let extension = 'webm';
        if (mimeType.includes('mp4')) extension = 'mp4';
        else if (mimeType.includes('ogg')) extension = 'ogg';
        else if (mimeType.includes('mpeg') || mimeType.includes('mp3')) extension = 'mp3';
        else if (mimeType.includes('wav')) extension = 'wav';

        console.log('Using extension:', extension);

        // Create form data for Whisper API
        const formData = new FormData();
        formData.append('file', audioBlob, `audio.${extension}`);
        formData.append('model', 'whisper-1');

        console.log('Calling Whisper API...');

        // Call OpenAI Whisper API
        const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${apiKey}`
          },
          body: formData
        });

        console.log('Whisper API response status:', response.status);

        if (!response.ok) {
          const errorData = await response.json();
          console.error('Whisper API error response:', errorData);
          throw new Error(errorData.error?.message || `HTTP ${response.status}`);
        }

        const data = await response.json();
        console.log('Transcription result:', data.text?.substring(0, 100));
        sendResponse({ success: true, text: data.text });
      } catch (error) {
        console.error('Whisper API error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle Groq Whisper transcription
  if (message.type === "groqWhisperTranscribe") {
    (async () => {
      const { audioData, apiKey, mimeType: providedMimeType } = message;

      console.log('Groq Whisper transcription started');

      if (!apiKey) {
        sendResponse({ success: false, error: 'Groq API key not provided' });
        return;
      }

      if (!audioData) {
        sendResponse({ success: false, error: 'No audio data received' });
        return;
      }

      try {
        let mimeType = providedMimeType || 'audio/webm';
        let base64Data = audioData;

        // Check if audioData is a full data URL or just raw base64
        if (audioData.includes(',')) {
          // Full data URL format: data:audio/webm;base64,XXXXX
          const base64Parts = audioData.split(',');
          const mimeMatch = base64Parts[0].match(/:(.*?);/);
          if (mimeMatch) {
            mimeType = mimeMatch[1];
          }
          base64Data = base64Parts[1];
        }
        // Otherwise audioData is raw base64, use providedMimeType

        console.log('Decoding base64 data, length:', base64Data?.length, 'mimeType:', mimeType);

        if (!base64Data) {
          throw new Error('No base64 data found in audio');
        }

        // Decode base64
        const binaryString = atob(base64Data);
        const bytes = new Uint8Array(binaryString.length);
        for (let i = 0; i < binaryString.length; i++) {
          bytes[i] = binaryString.charCodeAt(i);
        }
        const audioBlob = new Blob([bytes], { type: mimeType });
        console.log('Audio blob size:', audioBlob.size, 'type:', audioBlob.type);

        if (audioBlob.size < 1000) {
          sendResponse({ success: false, error: 'Audio recording too short or empty' });
          return;
        }

        // Determine file extension
        // For opus codec, use ogg extension as it's more widely supported
        let extension = 'webm';
        if (mimeType.includes('opus')) extension = 'ogg';
        else if (mimeType.includes('mp4')) extension = 'mp4';
        else if (mimeType.includes('ogg')) extension = 'ogg';
        else if (mimeType.includes('mpeg') || mimeType.includes('mp3')) extension = 'mp3';
        else if (mimeType.includes('wav')) extension = 'wav';

        console.log('Original mimeType:', mimeType, '-> extension:', extension);

        // Create form data for Groq Whisper API
        // Groq accepts: flac, mp3, mp4, mpeg, mpga, m4a, ogg, wav, webm
        const fileName = `recording.${extension}`;
        const formData = new FormData();
        formData.append('file', audioBlob, fileName);
        formData.append('model', 'whisper-large-v3-turbo');

        console.log('Calling Groq Whisper API with file:', fileName, 'size:', audioBlob.size, 'type:', mimeType);

        // Call Groq Whisper API
        const response = await fetch('https://api.groq.com/openai/v1/audio/transcriptions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${apiKey}`
          },
          body: formData
        });

        console.log('Groq API response status:', response.status);

        if (!response.ok) {
          const errorData = await response.json();
          console.error('Groq API error response:', JSON.stringify(errorData));
          throw new Error(errorData.error?.message || errorData.message || `HTTP ${response.status}`);
        }

        const data = await response.json();
        console.log('Transcription result:', data.text?.substring(0, 100));
        sendResponse({ success: true, text: data.text });
      } catch (error) {
        console.error('Groq Whisper API error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle offscreen recording - start
  if (message.type === "startRecording") {
    (async () => {
      try {
        await setupOffscreenDocument();
        const response = await chrome.runtime.sendMessage({
          target: 'offscreen',
          type: 'start-recording'
        });
        sendResponse(response);
      } catch (error) {
        console.error('Start recording error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle offscreen recording - stop
  if (message.type === "stopRecording") {
    (async () => {
      try {
        const response = await chrome.runtime.sendMessage({
          target: 'offscreen',
          type: 'stop-recording'
        });
        // Close offscreen document after recording
        await closeOffscreenDocument();
        sendResponse(response);
      } catch (error) {
        console.error('Stop recording error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle website health check
  if (message.type === "websiteHealthCheck") {
    (async () => {
      const { url, domain } = message;
      const results = {
        domain: domain,
        finalUrl: url,
        redirects: 0,
        response: null,
        https: null,
        ssl: null,
        headers: null,
        hosting: null,
        dns: null,
        tech: []
      };

      try {
        // 1. Fetch the URL and measure response time
        const startTime = performance.now();
        let response;
        let finalUrl = url;
        let redirectCount = 0;

        try {
          response = await fetch(url, {
            method: 'GET',
            redirect: 'follow',
            signal: AbortSignal.timeout(15000)
          });
          finalUrl = response.url;

          // Count redirects by comparing URLs
          if (finalUrl !== url) {
            redirectCount = 1; // At least one redirect happened
          }
        } catch (fetchError) {
          console.error('Fetch error:', fetchError);
          // Try without https if that failed
          if (url.startsWith('https://')) {
            try {
              const httpUrl = url.replace('https://', 'http://');
              response = await fetch(httpUrl, {
                method: 'GET',
                redirect: 'follow',
                signal: AbortSignal.timeout(15000)
              });
              finalUrl = response.url;
            } catch (e) {
              throw fetchError;
            }
          } else {
            throw fetchError;
          }
        }

        const endTime = performance.now();
        const responseTime = Math.round(endTime - startTime);

        results.finalUrl = finalUrl;
        results.redirects = redirectCount;

        // Response info
        results.response = {
          status: response.status,
          statusText: response.statusText,
          time: responseTime,
          contentType: response.headers.get('content-type'),
          server: response.headers.get('server')
        };

        // HTTPS check
        results.https = {
          enabled: finalUrl.startsWith('https://')
        };

        // Security headers
        results.headers = {
          hsts: response.headers.get('strict-transport-security'),
          csp: response.headers.get('content-security-policy'),
          xFrameOptions: response.headers.get('x-frame-options'),
          xContentTypeOptions: response.headers.get('x-content-type-options'),
          referrerPolicy: response.headers.get('referrer-policy'),
          permissionsPolicy: response.headers.get('permissions-policy') || response.headers.get('feature-policy')
        };

        // Detect technologies from headers
        const serverHeader = response.headers.get('server');
        const poweredBy = response.headers.get('x-powered-by');
        const tech = [];

        if (serverHeader) {
          if (serverHeader.toLowerCase().includes('nginx')) tech.push('nginx');
          if (serverHeader.toLowerCase().includes('apache')) tech.push('Apache');
          if (serverHeader.toLowerCase().includes('cloudflare')) tech.push('Cloudflare');
          if (serverHeader.toLowerCase().includes('iis')) tech.push('IIS');
          if (serverHeader.toLowerCase().includes('litespeed')) tech.push('LiteSpeed');
        }
        if (poweredBy) {
          if (poweredBy.toLowerCase().includes('php')) tech.push('PHP');
          if (poweredBy.toLowerCase().includes('asp')) tech.push('ASP.NET');
          if (poweredBy.toLowerCase().includes('express')) tech.push('Express.js');
          if (poweredBy.toLowerCase().includes('next')) tech.push('Next.js');
        }

        // Check for common CDNs/services
        const cfRay = response.headers.get('cf-ray');
        if (cfRay) tech.push('Cloudflare');
        const xAmzn = response.headers.get('x-amz-cf-id');
        if (xAmzn) tech.push('Amazon CloudFront');
        const xCache = response.headers.get('x-cache');
        if (xCache && xCache.toLowerCase().includes('varnish')) tech.push('Varnish');

        results.tech = [...new Set(tech)]; // Remove duplicates

        // 2. Get SSL certificate info (basic - from URL inspection)
        if (finalUrl.startsWith('https://')) {
          const parsedUrl = new URL(finalUrl);
          results.ssl = {
            valid: true, // If we got here via HTTPS, cert is valid
            issuer: 'Certificate validated by browser',
            subject: parsedUrl.hostname,
            validFrom: null,
            validTo: null,
            daysUntilExpiry: null,
            altNames: []
          };

          // Try to get more cert details via a secondary endpoint
          try {
            const sslCheckUrl = `https://ssl-checker.io/api/v1/check/${domain}`;
            const sslResponse = await fetch(sslCheckUrl, {
              signal: AbortSignal.timeout(5000)
            });
            if (sslResponse.ok) {
              const sslData = await sslResponse.json();
              if (sslData.result) {
                results.ssl.issuer = sslData.result.issuer || results.ssl.issuer;
                results.ssl.validFrom = sslData.result.valid_from;
                results.ssl.validTo = sslData.result.valid_till;
                results.ssl.daysUntilExpiry = sslData.result.days_left;
                results.ssl.altNames = sslData.result.san || [];
              }
            }
          } catch (sslError) {
            console.log('SSL check API not available, using basic info');
            // Calculate rough days until expiry (assume 90 days from now for LE certs)
            results.ssl.daysUntilExpiry = 90;
          }
        }

        // 3. Get hosting/IP info using ip-api.com
        try {
          const ipResponse = await fetch(`http://ip-api.com/json/${domain}?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,org,as,query,reverse`, {
            signal: AbortSignal.timeout(5000)
          });
          const ipData = await ipResponse.json();

          if (ipData.status === 'success') {
            results.hosting = {
              ip: ipData.query,
              hostname: ipData.reverse,
              org: ipData.org,
              isp: ipData.isp,
              as: ipData.as,
              country: ipData.country,
              region: ipData.regionName,
              city: ipData.city
            };
          }
        } catch (ipError) {
          console.error('IP lookup error:', ipError);
        }

        // 4. Get DNS records using Google DNS API
        try {
          const dnsResults = { a: [], aaaa: [], mx: [], ns: [], txt: [] };

          // A records
          try {
            const aResponse = await fetch(`https://dns.google/resolve?name=${domain}&type=A`, {
              signal: AbortSignal.timeout(3000)
            });
            const aData = await aResponse.json();
            if (aData.Answer) {
              dnsResults.a = aData.Answer.filter(r => r.type === 1).map(r => r.data);
            }
          } catch (e) { console.log('A record lookup failed'); }

          // AAAA records
          try {
            const aaaaResponse = await fetch(`https://dns.google/resolve?name=${domain}&type=AAAA`, {
              signal: AbortSignal.timeout(3000)
            });
            const aaaaData = await aaaaResponse.json();
            if (aaaaData.Answer) {
              dnsResults.aaaa = aaaaData.Answer.filter(r => r.type === 28).map(r => r.data);
            }
          } catch (e) { console.log('AAAA record lookup failed'); }

          // MX records
          try {
            const mxResponse = await fetch(`https://dns.google/resolve?name=${domain}&type=MX`, {
              signal: AbortSignal.timeout(3000)
            });
            const mxData = await mxResponse.json();
            if (mxData.Answer) {
              dnsResults.mx = mxData.Answer.filter(r => r.type === 15).map(r => r.data);
            }
          } catch (e) { console.log('MX record lookup failed'); }

          // NS records
          try {
            const nsResponse = await fetch(`https://dns.google/resolve?name=${domain}&type=NS`, {
              signal: AbortSignal.timeout(3000)
            });
            const nsData = await nsResponse.json();
            if (nsData.Answer) {
              dnsResults.ns = nsData.Answer.filter(r => r.type === 2).map(r => r.data);
            }
          } catch (e) { console.log('NS record lookup failed'); }

          // TXT records
          try {
            const txtResponse = await fetch(`https://dns.google/resolve?name=${domain}&type=TXT`, {
              signal: AbortSignal.timeout(3000)
            });
            const txtData = await txtResponse.json();
            if (txtData.Answer) {
              dnsResults.txt = txtData.Answer.filter(r => r.type === 16).map(r => r.data.replace(/"/g, ''));
            }
          } catch (e) { console.log('TXT record lookup failed'); }

          results.dns = dnsResults;
        } catch (dnsError) {
          console.error('DNS lookup error:', dnsError);
        }

        sendResponse({ success: true, data: results });
      } catch (error) {
        console.error('Website health check error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle stock quotes fetching (Yahoo Finance - no API key required)
  if (message.type === "fetchStockQuotes") {
    (async () => {
      const { symbols } = message;

      if (!symbols || symbols.length === 0) {
        sendResponse({ success: false, error: 'No symbols provided' });
        return;
      }

      try {
        const quotes = {};

        // Fetch quotes for each symbol using Yahoo Finance
        for (const symbol of symbols) {
          try {
            // Use Yahoo Finance chart API to get current quote
            const response = await fetch(
              `https://query1.finance.yahoo.com/v8/finance/chart/${encodeURIComponent(symbol)}?interval=1d&range=1d`,
              {
                signal: AbortSignal.timeout(8000),
                headers: {
                  'User-Agent': 'Mozilla/5.0'
                }
              }
            );

            if (response.ok) {
              const data = await response.json();
              const result = data.chart?.result?.[0];
              if (result) {
                const meta = result.meta;
                const quote = result.indicators?.quote?.[0];
                const prevClose = meta.previousClose || meta.chartPreviousClose;
                const currentPrice = meta.regularMarketPrice;
                const change = currentPrice - prevClose;
                const changePercent = (change / prevClose) * 100;

                quotes[symbol] = {
                  c: currentPrice,                    // current price
                  d: change,                          // change
                  dp: changePercent,                  // change percent
                  h: meta.regularMarketDayHigh || quote?.high?.[0],   // high
                  l: meta.regularMarketDayLow || quote?.low?.[0],     // low
                  o: quote?.open?.[0] || meta.regularMarketOpen,      // open
                  pc: prevClose,                      // previous close
                  currency: meta.currency,
                  exchange: meta.exchangeName,
                  name: meta.longName || meta.shortName || symbol
                };
              }
            } else if (response.status === 404) {
              quotes[symbol] = { error: 'Symbol not found' };
            }
          } catch (e) {
            console.error(`Error fetching quote for ${symbol}:`, e);
            quotes[symbol] = { error: e.message };
          }

          // Small delay between requests
          if (symbols.indexOf(symbol) < symbols.length - 1) {
            await new Promise(resolve => setTimeout(resolve, 100));
          }
        }

        sendResponse({ success: true, data: quotes });
      } catch (error) {
        console.error('Stock quotes error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle stock symbol search (Yahoo Finance)
  if (message.type === "searchStockSymbol") {
    (async () => {
      const { query } = message;

      try {
        const response = await fetch(
          `https://query1.finance.yahoo.com/v1/finance/search?q=${encodeURIComponent(query)}&quotesCount=15&newsCount=0&enableFuzzyQuery=false`,
          {
            signal: AbortSignal.timeout(5000),
            headers: {
              'User-Agent': 'Mozilla/5.0'
            }
          }
        );

        if (response.ok) {
          const data = await response.json();
          // Filter to stocks and ETFs, include international
          const results = (data.quotes || [])
            .filter(r => r.quoteType === 'EQUITY' || r.quoteType === 'ETF')
            .slice(0, 12)
            .map(r => ({
              symbol: r.symbol,
              name: r.longname || r.shortname || r.symbol,
              exchange: r.exchange,
              type: r.quoteType
            }));
          sendResponse({ success: true, data: results });
        } else {
          sendResponse({ success: false, error: `API error: ${response.status}` });
        }
      } catch (error) {
        console.error('Stock search error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle stock candle data (historical prices - Yahoo Finance)
  if (message.type === "fetchStockCandles") {
    (async () => {
      const { symbol, range, interval } = message;

      try {
        // Yahoo Finance range options: 1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, 10y, ytd, max
        // Interval options: 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, 1d, 5d, 1wk, 1mo, 3mo
        const response = await fetch(
          `https://query1.finance.yahoo.com/v8/finance/chart/${encodeURIComponent(symbol)}?interval=${interval || '1d'}&range=${range || '1mo'}`,
          {
            signal: AbortSignal.timeout(10000),
            headers: {
              'User-Agent': 'Mozilla/5.0'
            }
          }
        );

        if (response.ok) {
          const data = await response.json();
          const result = data.chart?.result?.[0];

          if (result && result.timestamp) {
            const timestamps = result.timestamp;
            const quote = result.indicators?.quote?.[0];

            if (quote) {
              const candles = timestamps.map((time, i) => ({
                time: time * 1000,
                open: quote.open?.[i],
                high: quote.high?.[i],
                low: quote.low?.[i],
                close: quote.close?.[i],
                volume: quote.volume?.[i]
              })).filter(c => c.close !== null && c.close !== undefined);

              sendResponse({ success: true, data: candles });
            } else {
              sendResponse({ success: false, error: 'No price data available' });
            }
          } else {
            sendResponse({ success: false, error: 'No data available for this symbol' });
          }
        } else if (response.status === 404) {
          sendResponse({ success: false, error: 'Symbol not found' });
        } else {
          sendResponse({ success: false, error: `API error: ${response.status}` });
        }
      } catch (error) {
        console.error('Stock candles error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle company profile fetch (Yahoo Finance)
  if (message.type === "fetchCompanyProfile") {
    (async () => {
      const { symbol } = message;

      try {
        // Use quoteSummary for detailed company info
        const response = await fetch(
          `https://query1.finance.yahoo.com/v10/finance/quoteSummary/${encodeURIComponent(symbol)}?modules=summaryProfile,price`,
          {
            signal: AbortSignal.timeout(5000),
            headers: {
              'User-Agent': 'Mozilla/5.0'
            }
          }
        );

        if (response.ok) {
          const data = await response.json();
          const result = data.quoteSummary?.result?.[0];

          if (result) {
            const profile = result.summaryProfile || {};
            const price = result.price || {};

            sendResponse({
              success: true,
              data: {
                name: price.longName || price.shortName,
                finnhubIndustry: profile.industry,
                sector: profile.sector,
                country: profile.country,
                weburl: profile.website,
                exchange: price.exchangeName,
                marketCapitalization: price.marketCap?.raw,
                currency: price.currency,
                description: profile.longBusinessSummary
              }
            });
          } else {
            sendResponse({ success: true, data: {} });
          }
        } else {
          sendResponse({ success: false, error: `API error: ${response.status}` });
        }
      } catch (error) {
        console.error('Company profile error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle opening plot in new tab
  if (message.type === "openPlotTab") {
    (async () => {
      try {
        const { plotConfig } = message;
        // Store plot data first
        await chrome.storage.local.set({ calculatorPlot: plotConfig });
        // Open the plot page in a new tab
        const plotUrl = chrome.runtime.getURL('plot.html');
        const tab = await chrome.tabs.create({ url: plotUrl });
        sendResponse({ success: true, tabId: tab.id });
      } catch (error) {
        console.error('Error opening plot tab:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle YouTube video metadata fetching
  if (message.type === "fetchYouTubeMetadata") {
    (async () => {
      const { videoId } = message;

      if (!videoId) {
        sendResponse({ success: false, error: 'No video ID provided' });
        return;
      }

      try {
        console.log('Fetching YouTube metadata for:', videoId);

        // Fetch the YouTube video page to extract metadata
        const videoPageResponse = await fetch(
          `https://www.youtube.com/watch?v=${videoId}`,
          {
            signal: AbortSignal.timeout(15000),
            headers: {
              'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
              'Accept-Language': 'en-US,en;q=0.9'
            }
          }
        );

        if (!videoPageResponse.ok) {
          throw new Error('Could not access video page');
        }

        const pageText = await videoPageResponse.text();
        const metadata = {};

        // Extract title
        const titleMatch = pageText.match(/"title":\s*"([^"]+)"/);
        if (titleMatch) metadata.title = titleMatch[1].replace(/\\u0026/g, '&');

        // Extract author/channel
        const authorMatch = pageText.match(/"author":\s*"([^"]+)"/);
        if (authorMatch) metadata.author = authorMatch[1];

        // Extract channel URL
        const channelMatch = pageText.match(/"ownerProfileUrl":\s*"([^"]+)"/);
        if (channelMatch) metadata.channelUrl = channelMatch[1];

        // Extract view count
        const viewMatch = pageText.match(/"viewCount":\s*"(\d+)"/);
        if (viewMatch) metadata.viewCount = parseInt(viewMatch[1]);

        // Extract publish date
        const publishMatch = pageText.match(/"publishDate":\s*"([^"]+)"/);
        if (publishMatch) metadata.publishDate = publishMatch[1];

        // Extract duration
        const durationMatch = pageText.match(/"lengthSeconds":\s*"(\d+)"/);
        if (durationMatch) {
          const seconds = parseInt(durationMatch[1]);
          const hours = Math.floor(seconds / 3600);
          const minutes = Math.floor((seconds % 3600) / 60);
          const secs = seconds % 60;
          metadata.duration = hours > 0
            ? `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`
            : `${minutes}:${secs.toString().padStart(2, '0')}`;
          metadata.durationSeconds = seconds;
        }

        // Extract description (first 500 chars)
        const descMatch = pageText.match(/"shortDescription":\s*"([^"]{0,500})/);
        if (descMatch) metadata.description = descMatch[1].replace(/\\n/g, '\n').replace(/\\u0026/g, '&');

        // Extract like count (if available)
        const likeMatch = pageText.match(/"likeCount":\s*(\d+)/);
        if (likeMatch) metadata.likeCount = parseInt(likeMatch[1]);

        // Extract category
        const categoryMatch = pageText.match(/"category":\s*"([^"]+)"/);
        if (categoryMatch) metadata.category = categoryMatch[1];

        // Extract keywords
        const keywordsMatch = pageText.match(/"keywords":\s*\[([^\]]+)\]/);
        if (keywordsMatch) {
          try {
            metadata.keywords = JSON.parse('[' + keywordsMatch[1] + ']').slice(0, 10);
          } catch (e) {}
        }

        // Thumbnail
        metadata.thumbnail = `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`;
        metadata.videoId = videoId;

        console.log('Metadata extracted:', Object.keys(metadata));
        sendResponse({ success: true, metadata });

      } catch (error) {
        console.error('YouTube metadata fetch error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle YouTube transcript fetching (from service worker to bypass CORS)
  if (message.type === "fetchYouTubeTranscript") {
    (async () => {
      const { videoId } = message;

      if (!videoId) {
        sendResponse({ success: false, error: 'No video ID provided' });
        return;
      }

      try {
        console.log('Fetching YouTube transcript for:', videoId);

        // Fetch the YouTube video page
        const videoPageResponse = await fetch(
          `https://www.youtube.com/watch?v=${videoId}`,
          {
            signal: AbortSignal.timeout(15000),
            headers: {
              'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
            }
          }
        );

        if (!videoPageResponse.ok) {
          throw new Error('Could not access video page');
        }

        const pageText = await videoPageResponse.text();

        // Look for captionTracks in the page
        const captionTracksMatch = pageText.match(/"captionTracks":\s*(\[.*?\])/);

        let captionUrl = null;

        if (captionTracksMatch) {
          try {
            // Fix JSON escaping and parse
            const captionTracksJson = captionTracksMatch[1]
              .replace(/\\"/g, '"')
              .replace(/\\\\/g, '\\');
            const captionTracks = JSON.parse(captionTracksJson);

            // Find English captions first, then any available
            const englishTrack = captionTracks.find(t =>
              t.languageCode === 'en' || t.vssId === '.en' || t.vssId === 'a.en'
            );
            const track = englishTrack || captionTracks[0];

            if (track && track.baseUrl) {
              captionUrl = track.baseUrl.replace(/\\u0026/g, '&');
            }
          } catch (parseError) {
            console.log('Caption tracks parse error:', parseError);
          }
        }

        // Try alternative: extract timedtext URL directly
        if (!captionUrl) {
          const timedTextMatch = pageText.match(/"baseUrl":\s*"(https:\/\/www\.youtube\.com\/api\/timedtext[^"]+)"/);
          if (timedTextMatch) {
            captionUrl = timedTextMatch[1].replace(/\\u0026/g, '&');
          }
        }

        if (!captionUrl) {
          // Try the direct timedtext API as fallback
          captionUrl = `https://www.youtube.com/api/timedtext?v=${videoId}&lang=en&fmt=json3`;
        }

        console.log('Caption URL:', captionUrl);

        // Fetch the captions
        let transcriptText = '';

        // Try JSON3 format first
        try {
          const captionResponse = await fetch(captionUrl, {
            signal: AbortSignal.timeout(10000),
            headers: {
              'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
            }
          });

          if (captionResponse.ok) {
            const contentType = captionResponse.headers.get('content-type') || '';

            if (contentType.includes('application/json')) {
              const captionData = await captionResponse.json();

              if (captionData.events) {
                for (const event of captionData.events) {
                  if (event.segs) {
                    for (const seg of event.segs) {
                      if (seg.utf8) {
                        transcriptText += seg.utf8;
                      }
                    }
                  }
                }
              }
            } else {
              // Assume XML format
              const xmlText = await captionResponse.text();
              const textMatches = xmlText.matchAll(/<text[^>]*>([^<]*)<\/text>/g);
              for (const match of textMatches) {
                transcriptText += match[1] + ' ';
              }
            }
          }
        } catch (captionError) {
          console.log('JSON3 caption fetch failed, trying XML:', captionError);
        }

        // Try XML format if JSON3 didn't work
        if (!transcriptText.trim()) {
          try {
            const xmlUrl = `https://www.youtube.com/api/timedtext?v=${videoId}&lang=en`;
            const xmlResponse = await fetch(xmlUrl, {
              signal: AbortSignal.timeout(10000),
              headers: {
                'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
              }
            });

            if (xmlResponse.ok) {
              const xmlText = await xmlResponse.text();
              const textMatches = xmlText.matchAll(/<text[^>]*>([^<]*)<\/text>/g);
              for (const match of textMatches) {
                transcriptText += match[1] + ' ';
              }
            }
          } catch (e) {
            console.log('XML caption fetch failed:', e);
          }
        }

        if (!transcriptText.trim()) {
          sendResponse({
            success: false,
            error: 'No captions available for this video. The video may not have captions, or they may be auto-generated and restricted.'
          });
          return;
        }

        // Decode HTML entities
        transcriptText = transcriptText
          .replace(/&amp;/g, '&')
          .replace(/&lt;/g, '<')
          .replace(/&gt;/g, '>')
          .replace(/&#39;/g, "'")
          .replace(/&quot;/g, '"')
          .replace(/\n/g, ' ')
          .replace(/\s+/g, ' ')
          .trim();

        console.log('Transcript length:', transcriptText.length);
        sendResponse({ success: true, transcript: transcriptText });

      } catch (error) {
        console.error('YouTube transcript fetch error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle YouTube audio extraction via Cobalt API
  if (message.type === "fetchYouTubeAudio") {
    (async () => {
      const { videoId } = message;

      if (!videoId) {
        sendResponse({ success: false, error: 'No video ID provided' });
        return;
      }

      try {
        console.log('Fetching YouTube audio via Cobalt for:', videoId);

        const youtubeUrl = `https://www.youtube.com/watch?v=${videoId}`;

        // Try multiple Cobalt instances (public instances - higher score first)
        const cobaltInstances = [
          'https://cobalt-backend.canine.tools',
          'https://cobalt-api.meowing.de',
          'https://capi.3kh0.net',
          'https://kityune.imput.net',
          'https://blossom.imput.net',
          'https://downloadapi.stuff.solutions'
        ];

        let cobaltData = null;
        let lastError = null;

        for (const instanceUrl of cobaltInstances) {
          try {
            console.log('Trying Cobalt instance:', instanceUrl);
            const response = await fetch(instanceUrl, {
              method: 'POST',
              headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
              },
              body: JSON.stringify({
                url: youtubeUrl,
                downloadMode: 'audio',
                audioFormat: 'mp3'
              }),
              signal: AbortSignal.timeout(30000)
            });

            // Check if response is JSON
            const contentType = response.headers.get('content-type') || '';
            if (!contentType.includes('application/json')) {
              console.log('Instance returned non-JSON response:', instanceUrl);
              lastError = new Error('Server returned HTML instead of JSON');
              continue;
            }

            const data = await response.json();
            console.log('Cobalt response from', instanceUrl, ':', JSON.stringify(data).substring(0, 200));

            // Check for successful response with URL
            if (data.url) {
              console.log('Cobalt instance succeeded:', instanceUrl);
              cobaltData = data;
              break;
            } else if (data.status === 'error' || data.error) {
              const errorMsg = data.error?.code || data.error || data.text || 'Unknown error';
              console.log('Cobalt error:', errorMsg);
              lastError = new Error(errorMsg);
            } else {
              console.log('Cobalt returned unexpected response:', data);
              lastError = new Error('Unexpected response format');
            }
          } catch (err) {
            console.log('Cobalt instance failed:', instanceUrl, err.message);
            lastError = err;
          }
        }

        if (!cobaltData || !cobaltData.url) {
          throw new Error(lastError?.message || 'All Cobalt instances failed. Audio transcription not available.');
        }

        // Use the data we already parsed
        const audioUrl = cobaltData.url;
        console.log('Got audio URL:', audioUrl);

        sendResponse({ success: true, audioUrl });

      } catch (error) {
        console.error('YouTube audio fetch error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }

  // Handle YouTube transcription via local transcription service
  if (message.type === "transcribeYouTubeLocal") {
    (async () => {
      const { videoId, serviceUrl } = message;

      // Helper to extract error message properly
      const getErrorMessage = (err) => {
        if (!err) return 'Unknown error';
        if (typeof err === 'string') return err;
        if (err.message) return err.message;
        if (err.name) return `${err.name}: ${err.toString()}`;
        return String(err);
      };

      // Try multiple URLs (127.0.0.1 often works better than localhost in extensions)
      const urlsToTry = serviceUrl
        ? [serviceUrl]
        : ['http://127.0.0.1:8000', 'http://localhost:8000'];

      if (!videoId) {
        sendResponse({ success: false, error: 'No video ID provided' });
        return;
      }

      let lastError = null;
      let serviceReachable = false;

      for (const baseUrl of urlsToTry) {
        try {
          console.log('Trying transcription service at:', baseUrl);

          // First check if service is reachable with a quick health check
          try {
            const healthController = new AbortController();
            const healthTimeoutId = setTimeout(() => healthController.abort(), 5000);
            const healthResponse = await fetch(`${baseUrl}/health`, {
              signal: healthController.signal
            });
            clearTimeout(healthTimeoutId);
            if (healthResponse.ok) {
              serviceReachable = true;
            }
          } catch (healthErr) {
            console.log('Health check failed for', baseUrl);
            continue; // Try next URL
          }

          const controller = new AbortController();
          const timeoutId = setTimeout(() => controller.abort(), 180000); // 3 min for long videos

          let response;
          try {
            response = await fetch(`${baseUrl}/transcribe/youtube`, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json'
              },
              body: JSON.stringify({
                url: `https://www.youtube.com/watch?v=${videoId}`,
                include_srt: false
              }),
              signal: controller.signal
            });
          } catch (fetchErr) {
            clearTimeout(timeoutId);
            if (fetchErr.name === 'AbortError') {
              throw new Error('Transcription timed out (3min)');
            }
            throw new Error(`Connection failed: ${getErrorMessage(fetchErr)}`);
          }
          clearTimeout(timeoutId);

          if (!response.ok) {
            const errorData = await response.json().catch(() => ({}));
            const detail = errorData.detail || `HTTP ${response.status}`;
            // Make yt-dlp errors more user-friendly
            if (detail.includes('empty') || detail.includes('Empty')) {
              throw new Error('YouTube blocked the download. Try a different video.');
            }
            throw new Error(detail);
          }

          const data = await response.json();
          console.log('Transcription success from', baseUrl, '- length:', data.text?.length);

          sendResponse({ success: true, transcript: data.text });
          return;

        } catch (error) {
          const errorMsg = getErrorMessage(error);
          console.log('Failed with', baseUrl, ':', errorMsg);
          lastError = errorMsg;
        }
      }

      // Provide helpful error message
      let finalError = lastError || 'Service unavailable';
      if (!serviceReachable) {
        finalError = 'Service not running. Start it with: python -m uvicorn main:app --port 8000';
      }

      console.error('All transcription endpoints failed:', finalError);
      sendResponse({ success: false, error: finalError });
    })();
    return true;
  }

  // Handle transcription of audio from URL using Groq Whisper
  if (message.type === "transcribeAudioUrl") {
    (async () => {
      const { audioUrl, apiKey } = message;

      if (!apiKey) {
        sendResponse({ success: false, error: 'Groq API key not provided' });
        return;
      }

      if (!audioUrl) {
        sendResponse({ success: false, error: 'No audio URL provided' });
        return;
      }

      // Helper to extract error message properly
      const getErrorMessage = (err) => {
        if (!err) return 'Unknown error';
        if (typeof err === 'string') return err;
        if (err.message) return err.message;
        if (err.name) return `${err.name}: ${err.toString()}`;
        return String(err);
      };

      try {
        console.log('Downloading audio from URL...');

        // Create AbortController for download
        const downloadController = new AbortController();
        const downloadTimeoutId = setTimeout(() => downloadController.abort(), 90000);

        let audioResponse;
        try {
          audioResponse = await fetch(audioUrl, {
            signal: downloadController.signal
          });
        } catch (fetchErr) {
          clearTimeout(downloadTimeoutId);
          if (fetchErr.name === 'AbortError') {
            throw new Error('Audio download timed out (90s)');
          }
          throw new Error(`Download failed: ${getErrorMessage(fetchErr)}`);
        }
        clearTimeout(downloadTimeoutId);

        if (!audioResponse.ok) {
          throw new Error(`Download failed: HTTP ${audioResponse.status}`);
        }

        const audioBlob = await audioResponse.blob();
        console.log('Audio downloaded, size:', audioBlob.size, 'type:', audioBlob.type);

        // Check file size (Groq limit is 25MB)
        if (audioBlob.size > 25 * 1024 * 1024) {
          throw new Error('Audio too large (max 25MB). Try a shorter video.');
        }

        if (audioBlob.size < 1000) {
          throw new Error('Audio file too small or empty');
        }

        // Determine file extension
        let extension = 'mp3';
        const mimeType = audioBlob.type || '';
        if (mimeType.includes('mp4')) extension = 'mp4';
        else if (mimeType.includes('webm')) extension = 'webm';
        else if (mimeType.includes('ogg')) extension = 'ogg';
        else if (mimeType.includes('wav')) extension = 'wav';

        // Create form data for Groq Whisper API
        const formData = new FormData();
        formData.append('file', audioBlob, `audio.${extension}`);
        formData.append('model', 'whisper-large-v3-turbo');

        console.log('Calling Groq Whisper API...');

        // Create AbortController for transcription
        const transcribeController = new AbortController();
        const transcribeTimeoutId = setTimeout(() => transcribeController.abort(), 180000);

        let response;
        try {
          response = await fetch('https://api.groq.com/openai/v1/audio/transcriptions', {
            method: 'POST',
            headers: {
              'Authorization': `Bearer ${apiKey}`
            },
            body: formData,
            signal: transcribeController.signal
          });
        } catch (fetchErr) {
          clearTimeout(transcribeTimeoutId);
          if (fetchErr.name === 'AbortError') {
            throw new Error('Transcription timed out (3min)');
          }
          throw new Error(`Whisper API failed: ${getErrorMessage(fetchErr)}`);
        }
        clearTimeout(transcribeTimeoutId);

        console.log('Groq API response status:', response.status);

        if (!response.ok) {
          const errorData = await response.json().catch(() => ({}));
          console.error('Groq API error response:', JSON.stringify(errorData));
          throw new Error(errorData.error?.message || `Whisper API HTTP ${response.status}`);
        }

        const data = await response.json();
        console.log('Transcription complete, length:', data.text?.length);
        sendResponse({ success: true, transcript: data.text });

      } catch (error) {
        const errorMsg = getErrorMessage(error);
        console.error('Audio transcription error:', errorMsg);
        sendResponse({ success: false, error: errorMsg });
      }
    })();
    return true;
  }

  // Handle calculator AI translation (natural language to math.js)
  if (message.type === "calculatorAITranslate") {
    (async () => {
      const { input, apiKey } = message;

      if (!apiKey) {
        sendResponse({ success: false, error: 'No API key provided' });
        return;
      }

      try {
        const systemPrompt = `You are a math expression translator. Your ONLY job is to convert natural language (English or Dutch) into math.js syntax.

OUTPUT FORMAT:
- For calculations: Return ONLY the math.js expression, nothing else. No quotes, no explanation.
- For plots/functions: Return JSON exactly like this: {"isPlot":true,"expression":"x^2","variable":"x","start":-10,"end":10}

MATH.JS SYNTAX RULES:
- Trigonometry: sin(x), cos(x), tan(x), asin(x), acos(x), atan(x)
- For degrees: sin(45 deg) - add " deg" after the number
- Roots: sqrt(x), cbrt(x), nthRoot(x, n)
- Logarithms: log(x) for natural log, log10(x) for base 10, log2(x) for base 2
- Powers: use ^ symbol, e.g., 2^3 means 2 to the power of 3
- Constants: pi (3.14159...), e (2.71828...)
- Factorial: factorial(5) or 5!
- Percentage: convert "X% of Y" to (X/100) * Y

DUTCH TERMS (translate to math.js):
- "sinus" → sin
- "cosinus" → cos
- "tangens" → tan
- "vierkantswortel" → sqrt
- "wortel" → sqrt
- "derdemachtswortel" → cbrt
- "tot de macht" or "tot de Xde macht" → ^
- "tot de tweede macht" or "kwadraat" → ^2
- "tot de derde macht" → ^3
- "procent van" → percentage calculation
- "absolute waarde" → abs
- "faculteit" → factorial
- "logaritme" → log10
- "natuurlijke logaritme" → log
- "graden" → deg

FUNCTION NOTATION:
- "f(x) = x^2" → plot x^2
- "y = sin(x)" → plot sin(x)
- "f(x) = ..." → treat as plot request

EXAMPLES:
Input: "sine of 45 degrees" → Output: sin(45 deg)
Input: "sinus van 45 graden" → Output: sin(45 deg)
Input: "cosinus van pi" → Output: cos(pi)
Input: "vierkantswortel van 144" → Output: sqrt(144)
Input: "square root of 144" → Output: sqrt(144)
Input: "5 tot de tweede macht" → Output: 5^2
Input: "3 tot de macht 4" → Output: 3^4
Input: "2 to the power of 8" → Output: 2^8
Input: "20 procent van 150" → Output: (20/100) * 150
Input: "15% of 80" → Output: (15/100) * 80
Input: "f(x) = x^2" → Output: {"isPlot":true,"expression":"x^2","variable":"x","start":-10,"end":10}
Input: "f(x) = sin(x)" → Output: {"isPlot":true,"expression":"sin(x)","variable":"x","start":-10,"end":10}
Input: "plot sinus van x" → Output: {"isPlot":true,"expression":"sin(x)","variable":"x","start":-10,"end":10}
Input: "teken x kwadraat" → Output: {"isPlot":true,"expression":"x^2","variable":"x","start":-10,"end":10}
Input: "graph x squared from -5 to 5" → Output: {"isPlot":true,"expression":"x^2","variable":"x","start":-5,"end":5}

IMPORTANT: Output ONLY the math expression or JSON. No other text.`;

        const response = await fetch('https://api.openai.com/v1/chat/completions', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${apiKey}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            model: 'gpt-3.5-turbo',
            messages: [
              { role: 'system', content: systemPrompt },
              { role: 'user', content: input }
            ],
            temperature: 0,
            max_tokens: 150
          }),
          signal: AbortSignal.timeout(15000)
        });

        if (!response.ok) {
          const errorData = await response.json();
          console.error('OpenAI API error:', errorData);
          throw new Error(errorData.error?.message || `API error: ${response.status}`);
        }

        const data = await response.json();
        let result = data.choices?.[0]?.message?.content?.trim();

        console.log('AI raw response:', result);

        if (!result) {
          sendResponse({ success: false, error: 'No response from AI' });
          return;
        }

        // Clean up the result - remove markdown code blocks if present
        result = result.replace(/^```json?\s*/i, '').replace(/\s*```$/i, '').trim();
        result = result.replace(/^`+|`+$/g, '').trim();

        // Check if it's a plot request (JSON response)
        if (result.startsWith('{')) {
          try {
            const plotData = JSON.parse(result);
            if (plotData.isPlot) {
              console.log('Parsed plot request:', plotData);
              sendResponse({
                success: true,
                mathExpr: plotData.expression,
                isPlot: true,
                plotParams: {
                  expression: plotData.expression,
                  variable: plotData.variable || 'x',
                  start: parseFloat(plotData.start) || -10,
                  end: parseFloat(plotData.end) || 10
                }
              });
              return;
            }
          } catch (parseError) {
            console.log('JSON parse failed, treating as expression:', result);
          }
        }

        // Regular math expression
        console.log('Returning math expression:', result);
        sendResponse({ success: true, mathExpr: result, isPlot: false });

      } catch (error) {
        console.error('Calculator AI translation error:', error);
        sendResponse({ success: false, error: error.message });
      }
    })();
    return true;
  }
});

