const STORAGE_KEY = "auth_jwt";
const BACKEND_URL = "http://localhost:5173"; // TODO: Backend URL konfigurieren

const PARSE_TIMEOUT_MS = 15000; // 15 seconds
const SCAN_TIMEOUT_MS = 45000; // 45 seconds (listing with _ipg=240 can be slow)
const activeParseRequests = new Map(); // Map<tabId, { timeout, originalSender, resolve }>
const activeScanRequests = new Map(); // Map<tabId, { timeout, sendResponse }>;

/** Aktueller Scan-Fortschritt für GET_SCAN_PROGRESS (Polling durch Web-App) */
let currentScanProgress = null;

// Messages from content script (der von der Web-App kommt)
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
  // Auth messages vom Content Script
  if (msg?.type === "AUTH_JWT" && msg.jwt) {
    chrome.storage.local.set({ [STORAGE_KEY]: msg.jwt }).then(() => {
      sendResponse({ ok: true });
    });
    return true; // async
  }

  if (msg?.type === "AUTH_CLEARED") {
    chrome.storage.local.remove(STORAGE_KEY).then(() => {
      sendResponse({ ok: true });
    });
    return true;
  }

  // API calls vom Popup
  if (msg?.type === "GET_JWT") {
    getJwt().then(jwt => {
      sendResponse({ jwt });
    });
    return true;
  }

  if (msg?.type === "CALL_API" && msg.path) {
    callProtectedApi(msg.path, msg.payload).then(data => {
      sendResponse({ ok: true, data });
    }).catch(err => {
      sendResponse({ ok: false, error: err.message });
    });
    return true;
  }

  // eBay Parsing Request (from Web App via content script or directly)
  if (msg?.action === "PARSE_URL" && msg.url) {
    handleParseRequest(msg.url, sendResponse);
    return true; // async
  }

  // eBay Parsing Response (from eBay content script)
  if (msg?.action === "PARSE_COMPLETE") {
    handleParseComplete(sender.tab?.id, msg.data);
    return true;
  }
});

// Handle messages from external web apps (externally_connectable)
chrome.runtime.onMessageExternal.addListener((msg, sender, sendResponse) => {
  if (msg?.action === "PARSE_URL" && msg.url) {
    handleParseRequest(msg.url, sendResponse);
    return true; // async
  }
  
  if (msg?.action === "SCAN_PRODUCTS" && msg.url && msg.accountId) {
    handleScanProductsRequest(msg.url, msg.accountId, sendResponse);
    return true; // async
  }

  if (msg?.action === "GET_SCAN_PROGRESS") {
    sendResponse(currentScanProgress ?? { percent: 0, phase: "idle", total: 0, current: 0, complete: false });
    return false;
  }
});

/**
 * Handles eBay URL parsing request
 * Creates a hidden tab, waits for load, sends parse message to content script
 */
async function handleParseRequest(url, sendResponse) {
  try {
    // Validate URL
    if (!url || typeof url !== 'string' || !url.toLowerCase().includes('ebay.')) {
      sendResponse({ ok: false, error: "Invalid eBay URL" });
      return;
    }

    console.log("[BACKGROUND] Creating tab for parse request:", url);

    // Create hidden tab
    const tab = await chrome.tabs.create({
      url: url,
      active: false
    });

    const tabId = tab.id;
    console.log("[BACKGROUND] Tab created:", tabId);

    // Set up timeout
    const timeoutId = setTimeout(() => {
      cleanupParseRequest(tabId, null, { ok: false, error: "timeout" });
    }, PARSE_TIMEOUT_MS);

    // Store request info
    activeParseRequests.set(tabId, {
      timeout: timeoutId,
      sendResponse: sendResponse
    });

    // Wait for tab to load, then send parse message
    const checkTabLoaded = (updatedTabId, changeInfo, updatedTab) => {
      if (updatedTabId !== tabId) return;
      
      // Tab is fully loaded
      if (changeInfo.status === 'complete' && updatedTab.url) {
        chrome.tabs.onUpdated.removeListener(checkTabLoaded);
        console.log("[BACKGROUND] Tab loaded, sending parse message:", updatedTab.url);
        
        // Wait longer for content script to auto-load (if it does)
        // Content scripts from manifest.json might need more time in hidden tabs
        setTimeout(() => {
          sendParseMessageWithRetry(tabId, 0);
        }, 2000); // 2 seconds delay - give content script time to auto-load
      }
    };

    chrome.tabs.onUpdated.addListener(checkTabLoaded);

  } catch (error) {
    console.error("Error in handleParseRequest:", error);
    sendResponse({ ok: false, error: error.message || "Unknown error" });
  }
}

/**
 * Checks if a URL is an eBay URL
 * @param {string} url - URL to check
 * @returns {boolean} - true if URL is an eBay URL
 */
function isEbayUrl(url) {
  if (!url) return false;
  const urlLower = url.toLowerCase();
  return urlLower.includes('ebay.');
}

/**
 * Injects the eBay content script manually if it's not already loaded
 * @param {number} tabId - Tab ID
 * @returns {Promise<boolean>} - true if injection successful or already loaded
 */
async function ensureContentScriptInjected(tabId) {
  try {
    // First, check if tab URL is an eBay URL
    const tab = await chrome.tabs.get(tabId);
    
    if (!isEbayUrl(tab.url)) {
      console.log("[BACKGROUND] Tab is not an eBay URL:", tab.url);
      return false;
    }
    
    // Check if content script is already loaded by sending a ping
    try {
      await chrome.tabs.sendMessage(tabId, { action: "PING" });
      console.log("[BACKGROUND] Content script already loaded");
      
      return true;
    } catch (pingError) {
      // Content script not loaded, try to inject it manually
      console.log("[BACKGROUND] Content script not found, injecting manually...");
      
      try {
        // Get tab info to check frames
        const tabInfo = await chrome.tabs.get(tabId);
        // Inject full ebay-content-script.js so PING, PARSE_EBAY and PARSE_PRODUCT_LIST are all handled.
        // The previous inline injection only handled PING+PARSE_EBAY; SCAN sends PARSE_PRODUCT_LIST.
        try {
          await chrome.scripting.executeScript({
            target: { tabId: tabId, frameIds: [0] },
            files: ["ebay-content-script.js"]
          });
          console.log("[BACKGROUND] ebay-content-script.js injected successfully");
        } catch (injectError) {
          console.error("[BACKGROUND] Failed to inject ebay-content-script:", injectError);
          throw injectError;
        }
        
        // Also try injecting a simple test script to verify injection works
        try {
          await chrome.scripting.executeScript({
            target: { tabId: tabId, frameIds: [0] },
            func: () => {
              console.log("[TEST-INJECTION] Test script executed in main frame");
            }
          });
          
          } catch (testError) {
          }
        
        // Wait longer for the script to fully initialize and register message listeners
        // Retry PING to verify the script is ready
        // Also try sending to frameId 0 explicitly
        for (let pingAttempt = 0; pingAttempt < 10; pingAttempt++) {
          await new Promise(resolve => setTimeout(resolve, 300)); // 300ms between pings
          
          try {
            // Try sending to main frame explicitly
            try {
              await chrome.tabs.sendMessage(tabId, { action: "PING" }, { frameId: 0 });
            } catch (frameErr) {
              // Fallback: try without frameId
              await chrome.tabs.sendMessage(tabId, { action: "PING" });
            }
            
            console.log("[BACKGROUND] Content script ready after injection");
            return true;
          } catch (pingErr) {
            // Continue to next attempt
            }
        }
        
        // Even if PING failed, return true - the script might still work
        console.warn("[BACKGROUND] Content script injected but PING not responding");
        return true;
      } catch (injectError) {
        console.error("[BACKGROUND] Failed to inject content script:", injectError);
        
        return false;
      }
    }
  } catch (error) {
    console.error("[BACKGROUND] Error checking/injecting content script:", error);
    
    return false;
  }
}

/**
 * Sends parse message to content script with retry mechanism
 * @param {number} tabId - Tab ID
 * @param {number} attempt - Current attempt number (0-based)
 */
async function sendParseMessageWithRetry(tabId, attempt) {
  const maxAttempts = 3;
  const retryDelay = 500; // 500ms between retries
  
  try {
    console.log(`[BACKGROUND] Sending parse message (attempt ${attempt + 1}/${maxAttempts}) to tab:`, tabId);
    
    // On first attempt, ensure content script is injected
    if (attempt === 0) {
      const injected = await ensureContentScriptInjected(tabId);
      
      if (!injected) {
        throw new Error("Could not inject content script");
      }
    }
    
    // Check if content script is injected by trying to send a ping
    // If this fails, the content script might not be loaded yet
    // Try sending to main frame explicitly first
    let response;
    try {
      response = await chrome.tabs.sendMessage(tabId, { action: "PARSE_EBAY" }, { frameId: 0 });
    } catch (frameErr) {
      // Fallback: try without frameId
      response = await chrome.tabs.sendMessage(tabId, { action: "PARSE_EBAY" });
    }
    
    console.log("[BACKGROUND] Parse response received:", response);
    
    if (response && response.ok && response.data) {
      handleParseComplete(tabId, response.data);
    } else {
      cleanupParseRequest(tabId, null, { 
        ok: false, 
        error: response?.error || "Parsing failed" 
      });
    }
  } catch (err) {
    // Check if error is due to content script not being ready
    const runtimeError = chrome.runtime.lastError?.message || "";
    const isContentScriptError = 
      err.message?.includes("Could not establish connection") ||
      err.message?.includes("Receiving end does not exist") ||
      err.message?.includes("Could not inject content script") ||
      runtimeError.includes("Receiving end does not exist") ||
      runtimeError.includes("Could not establish connection");
    
    console.log(`[BACKGROUND] Error sending parse message (attempt ${attempt + 1}):`, {
      error: err.message,
      runtimeError: runtimeError,
      isContentScriptError: isContentScriptError
    });
    
    if (isContentScriptError && attempt < maxAttempts) {
      // Content script not ready yet, retry after delay
      console.log(`[BACKGROUND] Content script not ready, retrying in ${retryDelay}ms...`);
      setTimeout(() => {
        sendParseMessageWithRetry(tabId, attempt + 1);
      }, retryDelay);
    } else {
      // Max retries reached or different error
      const errorMessage = isContentScriptError 
        ? "Content script konnte nicht geladen werden. Bitte Extension neu laden."
        : (err.message || runtimeError || "Content script error");
      cleanupParseRequest(tabId, null, { 
        ok: false, 
        error: errorMessage 
      });
    }
  }
}

/**
 * Handles parse complete response from content script
 */
function handleParseComplete(tabId, data) {
  cleanupParseRequest(tabId, data, null);
}

/**
 * Cleans up parse request: closes tab, clears timeout, sends response
 */
async function cleanupParseRequest(tabId, data, error) {
  const request = activeParseRequests.get(tabId);
  if (!request) return;

  // Clear timeout
  if (request.timeout) {
    clearTimeout(request.timeout);
  }

  // Remove from active requests
  activeParseRequests.delete(tabId);

  // Close tab
  try {
    await chrome.tabs.remove(tabId);
  } catch (err) {
    // Tab might already be closed
    console.warn("Could not close tab:", err);
  }

  // Send response
  if (request.sendResponse) {
    if (error) {
      request.sendResponse(error);
    } else if (data) {
      request.sendResponse({ ok: true, data: data });
    } else {
      request.sendResponse({ ok: false, error: "Unknown error" });
    }
  }
}

/**
 * Transform account URL (e.g. /str/topmons) to listing URL with 240 items per page.
 * Example: https://www.ebay.de/str/topmons?... → https://www.ebay.de/sch/i.html?_ssn=topmons&store_name=topmons&_ipg=240
 */
function transformAccountUrlTo240ItemsUrl(accountUrl) {
  try {
    const url = new URL(accountUrl);
    const pathname = url.pathname;

    const strMatch = pathname.match(/\/str\/([^\/\?]+)/);
    if (!strMatch || !strMatch[1]) {
      throw new Error("Could not extract account name from URL (expected /str/{name})");
    }

    const accountName = strMatch[1];
    const baseUrl = `${url.protocol}//${url.hostname}`;
    const listingUrl = `${baseUrl}/sch/i.html?_ssn=${encodeURIComponent(accountName)}&store_name=${encodeURIComponent(accountName)}&_ipg=240`;
    return listingUrl;
  } catch (e) {
    throw new Error(`Failed to transform account URL: ${e.message}`);
  }
}

/**
 * Handles eBay product scan request
 * Transforms account URL to listing URL with _ipg=240, creates hidden tab, parses, then loads each item in separate tab.
 */
async function handleScanProductsRequest(url, accountId, sendResponse) {
  try {
    // Validate URL
    if (!url || typeof url !== 'string' || !url.toLowerCase().includes('ebay.')) {
      sendResponse({ ok: false, error: "Invalid eBay URL" });
      return;
    }

    let targetUrl = url;
    try {
      targetUrl = transformAccountUrlTo240ItemsUrl(url);
      console.log("[BACKGROUND] Scan using listing URL (240 items):", targetUrl);
    } catch (transformErr) {
      console.warn("[BACKGROUND] URL transform failed, using original URL:", transformErr.message);
      // Fallback: use original URL (e.g. already /sch/ or non-/str/)
    }

    // Create hidden tab
    const tab = await chrome.tabs.create({
      url: targetUrl,
      active: false
    });

    const tabId = tab.id;

    // Set up timeout
    const timeoutId = setTimeout(() => {
      cleanupScanRequest(tabId, null, { ok: false, error: "timeout" });
    }, SCAN_TIMEOUT_MS);

    // Store request info
    activeScanRequests.set(tabId, {
      timeout: timeoutId,
      sendResponse: sendResponse
    });

    currentScanProgress = { percent: 0, phase: "listing", total: 0, current: 0, complete: false };

    // Wait for tab to load, then send parse message
    const checkTabLoaded = (updatedTabId, changeInfo, updatedTab) => {
      if (updatedTabId !== tabId) return;
      // Tab is fully loaded
      if (changeInfo.status === 'complete' && updatedTab.url) {
        chrome.tabs.onUpdated.removeListener(checkTabLoaded);
        // Small delay to ensure DOM is ready
        setTimeout(() => {
          sendScanMessageWithRetry(tabId, 0);
        }, 1000); // 1 second delay for DOM ready
      }
    };

    chrome.tabs.onUpdated.addListener(checkTabLoaded);

  } catch (error) {
    console.error("Error in handleScanProductsRequest:", error);
    sendResponse({ ok: false, error: error.message || "Unknown error" });
  }
}

/**
 * Sends scan message to content script with retry mechanism
 * @param {number} tabId - Tab ID
 * @param {number} attempt - Current attempt number (0-based)
 */
async function sendScanMessageWithRetry(tabId, attempt) {
  const maxAttempts = 3;
  const retryDelay = 500; // 500ms between retries
  
  try {
    console.log(`[BACKGROUND] Sending scan message (attempt ${attempt + 1}/${maxAttempts}) to tab:`, tabId);
    
    // On first attempt, ensure content script is injected
    if (attempt === 0) {
      const injected = await ensureContentScriptInjected(tabId);
      if (!injected) {
        throw new Error("Could not inject content script");
      }
    }
    
    const response = await chrome.tabs.sendMessage(tabId, { action: "PARSE_PRODUCT_LIST" });
    
    if (response && response.ok) {
      // Prüfe ob items vorhanden und nicht leer
      const items = response.items || response.data?.items || [];
      const meta = response.meta || response.data?.meta || {};
      
      // Log meta für Debugging
      console.log("[SCAN meta]", meta);
      
      if (items.length === 0) {
        // Leere items: behandele als Fehler mit meta
        cleanupScanRequest(tabId, null, { 
          ok: false, 
          error: "empty_items",
          meta: meta
        });
      } else {
        if (currentScanProgress) {
          currentScanProgress.phase = "details";
          currentScanProgress.total = items.length;
          currentScanProgress.percent = 10;
        }
        await handleScanComplete(tabId, { items, meta });
      }
    } else {
      // Fehler: sende error + meta
      const meta = response?.meta || {};
      console.log("[SCAN meta]", meta);
      cleanupScanRequest(tabId, null, { 
        ok: false, 
        error: response?.error || "Parsing failed",
        meta: meta
      });
    }
  } catch (err) {
    // Check if error is due to content script not being ready
    const runtimeError = chrome.runtime.lastError?.message || "";
    const isContentScriptError = 
      err.message?.includes("Could not establish connection") ||
      err.message?.includes("Receiving end does not exist") ||
      err.message?.includes("Could not inject content script") ||
      runtimeError.includes("Receiving end does not exist") ||
      runtimeError.includes("Could not establish connection");
    
    console.log(`[BACKGROUND] Error sending scan message (attempt ${attempt + 1}):`, {
      error: err.message,
      runtimeError: runtimeError,
      isContentScriptError: isContentScriptError
    });
    
    if (isContentScriptError && attempt < maxAttempts) {
      // Content script not ready yet, retry after delay
      console.log(`[BACKGROUND] Content script not ready, retrying in ${retryDelay}ms...`);
      setTimeout(() => {
        sendScanMessageWithRetry(tabId, attempt + 1);
      }, retryDelay);
    } else {
      // Max retries reached or different error
      const errorMessage = isContentScriptError 
        ? "Content script konnte nicht geladen werden. Bitte Extension neu laden."
        : (err.message || runtimeError || "Content script error");
      cleanupScanRequest(tabId, null, { 
        ok: false, 
        error: errorMessage,
        meta: {
          pageType: "unknown",
          finalUrl: "",
          attempts: attempt + 1,
          reason: "content_script_error"
        }
      });
    }
  }
}

const ITEM_TAB_LOAD_TIMEOUT_MS = 3000;

/**
 * Opens each item URL in a separate background tab, waits for load, parses detail page
 * (title, price, currency, category, condition), merges into item, then closes tab.
 */
async function loadAndParseEachItemTab(items) {
  if (!Array.isArray(items) || items.length === 0) return;

  const total = items.length;
  for (let i = 0; i < total; i++) {
    const item = items[i];
    if (!item || !item.url) continue;

    try {
      const tab = await chrome.tabs.create({
        url: item.url,
        active: false
      });

      await new Promise((resolve) => {
        const listener = (tabId, changeInfo) => {
          if (tabId === tab.id && changeInfo.status === "complete") {
            chrome.tabs.onUpdated.removeListener(listener);
            resolve();
          }
        };
        chrome.tabs.onUpdated.addListener(listener);

        setTimeout(() => {
          chrome.tabs.onUpdated.removeListener(listener);
          resolve();
        }, ITEM_TAB_LOAD_TIMEOUT_MS);
      });

      const injected = await ensureContentScriptInjected(tab.id);
      if (!injected) {
        console.warn(`[BACKGROUND] Content script not ready in item tab ${i + 1}, skipping detail parse`);
      }

      try {
        const response = injected
          ? await chrome.tabs.sendMessage(tab.id, { action: "PARSE_ITEM_DETAIL" })
          : null;
        if (response?.ok && response?.data) {
          const d = response.data;
          if (d.title != null && d.title !== "") item.title = d.title;
          if (d.price != null) item.price = d.price;
          if (d.currency != null && d.currency !== "") item.currency = d.currency;
          if (d.category != null && d.category !== "") item.category = d.category;
          if (d.condition != null && d.condition !== "") item.condition = d.condition;
          if (d.quantityAvailable != null) item.quantityAvailable = d.quantityAvailable;
          if (d.quantitySold != null) item.quantitySold = d.quantitySold;
          if (d.watchCount != null) item.watchCount = d.watchCount;
          if (d.inCartsCount != null) item.inCartsCount = d.inCartsCount;
        }
      } catch (parseErr) {
        console.warn(`[BACKGROUND] PARSE_ITEM_DETAIL failed for ${item.url}:`, parseErr);
      }

      if (currentScanProgress && total > 0) {
        currentScanProgress.current = i + 1;
        currentScanProgress.percent = Math.min(99, 10 + Math.round((90 * (i + 1)) / total));
      }

      try {
        await chrome.tabs.remove(tab.id);
      } catch (removeErr) {
        console.warn(`[BACKGROUND] Could not close item tab:`, removeErr);
      }
    } catch (e) {
      console.warn(`[BACKGROUND] Failed to load/parse item tab ${i + 1}/${total}:`, item.url, e);
    }
  }
}

/**
 * Handles scan complete response from content script
 */
async function handleScanComplete(tabId, data) {
  const request = activeScanRequests.get(tabId);
  if (request && request.timeout) {
    clearTimeout(request.timeout);
    request.timeout = null;
  }

  const items = data?.items || [];
  if (items.length > 0) {
    await loadAndParseEachItemTab(items);
  }

  if (currentScanProgress) {
    currentScanProgress.percent = 100;
    currentScanProgress.complete = true;
    currentScanProgress.current = items.length;
  }

  await cleanupScanRequest(tabId, data, null);
}

/**
 * Cleans up scan request: closes tab, clears timeout, sends response
 */
async function cleanupScanRequest(tabId, data, error) {
  const request = activeScanRequests.get(tabId);
  if (!request) return;

  // Clear timeout
  if (request.timeout) {
    clearTimeout(request.timeout);
  }

  // Remove from active requests
  activeScanRequests.delete(tabId);

  currentScanProgress = null;

  // Close tab (always, even on error)
  try {
    await chrome.tabs.remove(tabId);
  } catch (err) {
    // Tab might already be closed
    console.warn("Could not close tab:", err);
  }

  // Send response
  if (request.sendResponse) {
    if (error) {
      // error kann bereits meta enthalten
      request.sendResponse(error);
    } else if (data) {
      // data kann items + meta enthalten
      request.sendResponse({ ok: true, data: data });
    } else {
      request.sendResponse({ ok: false, error: "Unknown error" });
    }
  }
}


export async function getJwt() {
  const data = await chrome.storage.local.get(STORAGE_KEY);
  return data[STORAGE_KEY] || "";
}

export async function callProtectedApi(path, payload) {
  const jwt = await getJwt();
  if (!jwt) throw new Error("Not authed");

  const res = await fetch(`${BACKEND_URL}${path}`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      "authorization": `Bearer ${jwt}`
    },
    body: JSON.stringify(payload || {})
  });

  if (!res.ok) throw new Error(`API error: ${res.status}`);
  return await res.json();
}
