"use strict";
var BrowserAgentAgent;
(function (BrowserAgentAgent) {
    BrowserAgentAgent.DEFAULT_SOURCE_SHEET_NAME = "Test2SourceData";
    BrowserAgentAgent.DEFAULT_SOURCE_COLUMNS = {
        searchTerm: "Src Nom Prénom",
        location: "Location",
        extracted: "Extracted",
        lastName: "Nom",
        firstName: "Prénom"
    };
    BrowserAgentAgent.DEFAULT_EXTRACT_SKILL_SLUG = "pagesjaunes-extract-listings";
    class JobManager {
        constructor(config, onLog, onEvent) {
            this.config = config;
            this.isRunning = false;
            this.currentTabId = null;
            this.onLog = console.log;
            this.onEvent = () => undefined;
            this.skillJs = null;
            if (onLog)
                this.onLog = onLog;
            if (onEvent)
                this.onEvent = onEvent;
        }
        async start() {
            if (this.isRunning)
                return;
            this.isRunning = true;
            this.onLog("Starting Job Manager...");
            try {
                const skillSlug = this.config.extractSkillSlug || BrowserAgentAgent.DEFAULT_EXTRACT_SKILL_SLUG;
                this.onLog(`Loading extraction skill: ${skillSlug}`);
                const skill = await BrowserAgentAgent.SkillsService.fetchSkill(skillSlug);
                if (!skill?.injectedJs) {
                    throw new Error(`Could not load skill "${skillSlug}" from backend.`);
                }
                this.skillJs = skill.injectedJs;
                this.onLog(`Ensuring master sheet structure for "${this.config.sourceSheetName}"...`);
                await BrowserAgentAgent.GoogleSheetsService.setupMasterSheet(this.config.spreadsheetId, this.config.sourceSheetName);
                this.onLog("Creating new run sheet...");
                const runSheet = await BrowserAgentAgent.GoogleSheetsService.createRunSheet(this.config.spreadsheetId);
                if (runSheet) {
                    this.config.resultSheetName = runSheet.sheetTitle;
                    this.config.resultSheetId = runSheet.sheetId;
                    this.onLog(`Run sheet: ${runSheet.sheetTitle}`);
                }
                await this.processQueue();
            }
            catch (error) {
                this.onLog(`Error in Job Manager: ${error instanceof Error ? error.message : String(error)}`);
            }
            finally {
                this.isRunning = false;
                this.onLog("Job Manager stopped.");
                this.cleanupTab();
                if (this.config.resultSheetName && typeof this.config.resultSheetId === "number") {
                    this.onEvent({
                        kind: "run.completed",
                        sheetTitle: this.config.resultSheetName,
                        sheetId: this.config.resultSheetId,
                        sheetUrl: buildSheetUrl(this.config.spreadsheetId, this.config.resultSheetId)
                    });
                }
            }
        }
        stop() {
            this.isRunning = false;
            this.onLog("Stop requested by user.");
            this.cleanupTab();
        }
        async processQueue() {
            const { spreadsheetId, sourceSheetName, resultSheetName, columns, batchSize } = this.config;
            this.onLog(`Fetching data from sheet: ${sourceSheetName}`);
            const rows = await BrowserAgentAgent.GoogleSheetsService.readSourceRows(spreadsheetId, sourceSheetName);
            if (!rows || rows.length === 0) {
                this.onLog("No data rows found in the source sheet.");
                return;
            }
            let processedCount = 0;
            const interRowDelay = this.config.interRowDelayMs ?? 2000;
            for (const row of rows) {
                if (!this.isRunning || processedCount >= batchSize)
                    break;
                const searchTerm = String(row[columns.searchTerm] ?? "").trim();
                const location = String(row[columns.location] ?? "").trim();
                const processedFlag = row[columns.extracted];
                const rowNumber = Number(row._rowNumber);
                if (!shouldProcess(processedFlag))
                    continue;
                if (!searchTerm)
                    continue;
                if (!Number.isFinite(rowNumber) || rowNumber < 2) {
                    this.onLog(`Skipping row with missing _rowNumber: ${searchTerm}`);
                    continue;
                }
                const isRetry = isErrorFlag(processedFlag);
                this.onLog(`Processing row ${rowNumber}: ${searchTerm}${isRetry ? " (retry)" : ""}`);
                const outcome = await this.scrapeTarget(searchTerm, location);
                if (!outcome.ok) {
                    this.onLog(`Error scraping ${searchTerm}: ${outcome.error || "Unknown error"}${outcome.cloudflare ? " (Cloudflare challenge — open the tab and solve it)" : ""}`);
                    await BrowserAgentAgent.GoogleSheetsService.updateSourceRow(spreadsheetId, sourceSheetName, rowNumber, {
                        extracted: "ERROR"
                    });
                    processedCount++;
                    continue;
                }
                if (outcome.rows.length === 0) {
                    this.onLog(`No results found for ${searchTerm}`);
                    await BrowserAgentAgent.GoogleSheetsService.updateSourceRow(spreadsheetId, sourceSheetName, rowNumber, {
                        extracted: "0"
                    });
                }
                else {
                    this.onLog(`Found ${outcome.rows.length} result(s) for ${searchTerm}`);
                    await BrowserAgentAgent.GoogleSheetsService.appendResultRows(spreadsheetId, resultSheetName, outcome.rows.map((r) => ({
                        searchTerm,
                        name: r.name || "",
                        address: r.address || "",
                        phone: r.phone || "",
                        exactMatch: r.exactMatch
                    })));
                    const top = outcome.rows[0];
                    const { lastName, firstName } = splitName(top.name || "");
                    await BrowserAgentAgent.GoogleSheetsService.updateSourceRow(spreadsheetId, sourceSheetName, rowNumber, {
                        extracted: outcome.rows.length,
                        lastName,
                        firstName
                    });
                }
                processedCount++;
                if (interRowDelay > 0)
                    await wait(interRowDelay);
            }
            this.onLog(`Batch complete. Processed ${processedCount} rows.`);
        }
        async scrapeTarget(searchTerm, location) {
            if (!this.skillJs) {
                return { ok: false, rows: [], error: "Extraction skill is not loaded." };
            }
            const params = new URLSearchParams({ quoiqui: searchTerm });
            if (location)
                params.set("ou", location);
            const url = `https://www.pagesjaunes.fr/pagesblanches/recherche?${params.toString()}`;
            const timeoutMs = this.config.scrapeTimeoutMs || 30000;
            const cloudflareGraceMs = this.config.cloudflareGraceMs ?? 300000;
            // Outer ceiling must accommodate the in-page wait + the Cloudflare
            // grace period, since the user may need to solve a challenge.
            const overallMs = timeoutMs + cloudflareGraceMs + 10000;
            const skillJs = this.skillJs;
            const skillParameters = { searchTerm, timeoutMs, cloudflareGraceMs };
            return new Promise((resolve) => {
                chrome.tabs.create({ url, active: false }, (tab) => {
                    if (!tab || !tab.id) {
                        resolve({ ok: false, rows: [], error: "Failed to create tab" });
                        return;
                    }
                    this.currentTabId = tab.id;
                    const tabId = tab.id;
                    let settled = false;
                    const settle = (outcome) => {
                        if (settled)
                            return;
                        settled = true;
                        this.cleanupTab();
                        resolve(outcome);
                    };
                    const overall = setTimeout(() => {
                        settle({ ok: false, rows: [], error: `Timeout waiting for tab to load after ${overallMs}ms` });
                    }, overallMs);
                    const listener = (updatedTabId, info) => {
                        if (updatedTabId !== tabId || info.status !== "complete")
                            return;
                        chrome.tabs.onUpdated.removeListener(listener);
                        chrome.tabs.sendMessage(tabId, { type: "EXECUTE_SKILL", payload: { js: skillJs, parameters: skillParameters } }, (response) => {
                            clearTimeout(overall);
                            if (chrome.runtime.lastError) {
                                settle({ ok: false, rows: [], error: chrome.runtime.lastError.message });
                                return;
                            }
                            if (!response) {
                                settle({ ok: false, rows: [], error: "Content script returned no response." });
                                return;
                            }
                            if (!response.ok) {
                                settle({
                                    ok: false,
                                    rows: [],
                                    error: response.error?.message || response.result?.error?.message || "Skill execution failed."
                                });
                                return;
                            }
                            const skillData = response.result?.data;
                            if (!skillData || skillData.ok === false) {
                                settle({
                                    ok: false,
                                    rows: [],
                                    cloudflare: skillData?.cloudflare,
                                    error: skillData?.error || "Skill returned an error."
                                });
                                return;
                            }
                            settle({ ok: true, rows: skillData.results || [] });
                        });
                    };
                    chrome.tabs.onUpdated.addListener(listener);
                });
            });
        }
        cleanupTab() {
            if (this.currentTabId !== null) {
                chrome.tabs.remove(this.currentTabId).catch(() => undefined);
                this.currentTabId = null;
            }
        }
    }
    BrowserAgentAgent.JobManager = JobManager;
    // ── Pure helpers (exported for tests) ────────────────────────────────────────
    function shouldProcess(flag) {
        if (flag === undefined || flag === null)
            return true;
        const text = String(flag).trim();
        if (text === "")
            return true;
        if (text.toUpperCase() === "ERROR")
            return true;
        return false;
    }
    BrowserAgentAgent.shouldProcess = shouldProcess;
    function isErrorFlag(flag) {
        return typeof flag !== "undefined" && flag !== null && String(flag).trim().toUpperCase() === "ERROR";
    }
    BrowserAgentAgent.isErrorFlag = isErrorFlag;
    function splitName(fullName) {
        const trimmed = (fullName || "").trim();
        if (!trimmed)
            return { lastName: "", firstName: "" };
        const parts = trimmed.split(/\s+/);
        const lastName = parts.shift() || "";
        const firstName = parts.join(" ");
        return { lastName, firstName };
    }
    BrowserAgentAgent.splitName = splitName;
    function wait(ms) {
        return new Promise((resolve) => setTimeout(resolve, ms));
    }
    function buildSheetUrl(spreadsheetId, sheetId) {
        return `https://docs.google.com/spreadsheets/d/${encodeURIComponent(spreadsheetId)}/edit#gid=${sheetId}`;
    }
    BrowserAgentAgent.buildSheetUrl = buildSheetUrl;
})(BrowserAgentAgent || (BrowserAgentAgent = {}));
globalThis.BrowserAgentAgent = BrowserAgentAgent;
//# sourceMappingURL=JobManager.js.map