// painel.js — VFSQuickBot Versão Final (UNIFICADA - 3 ETAPAS)
(() => {




    // Mapeia os passos principais do fluxo
const FLOW_STEPS = {
    // 1. Início e Taxas
    START: "START", 
    API_7_GET_FEE: "API_7_GET_FEE",         // Obter taxas (antigo API_7)
    
    // 2. Facial ID / Token
    API_8_GET_IDNFY_TOKEN: "API_8_GET_IDNFY_TOKEN", // Gerar o link do Facial ID (antigo API_8)
    
    // 3. OTP
    OTP_INIT: "OTP_INIT",                  // Iniciar e validar o OTP
    
    // 4. Slots
    SLOT_ANALYSIS: "SLOT_ANALYSIS",        // Buscar calendário de vagas
   
    // 5. Confirmação e Verificação
   // AWAITING_CONFIRMATION: "AWAITING_CONFIRMATION", // Esperar pelo clique do usuário (UI)
   // AWAITING_FACIAL_VERIFICATION: "AWAITING_FACIAL_VERIFICATION", // Verificar status Facial ID (API)
    
    // 6. Submissão Final
    SUBMIT_AGENDAMENTO_FINAL: "SUBMIT_AGENDAMENTO_FINAL", // Submeter reserva e pagamento final
    
    // 7. Fim
    COMPLETE: "COMPLETE"
};

    // ==========================================================
    // 1. VARIÁVEIS GLOBAIS E CONSTANTES
    // ==========================================================
    const CONTEXT_STORAGE_KEY = 'VFS_CONTEXT_UNIFIED'; // 🔑 CHAVE ÚNICA DE CONSOLIDAÇÃO
    const STORAGE_KEY = "vfs_applicants_v2";
    // Nota: O LOGO_PATH ('icons/icon48.png') deve ser configurado no manifest.json
    const LOGO_PATH = "icons/icon48.png"; 
    const MAX_APPLICANTS = 5;
    const MAX_SELECTED = 5;
    let globalCycleTimer = null;
// Assumindo suas outras constantes:
// const RETRY_INTERVAL_MS = 600; 
// const MAX_ATTEMPTS = 1;
// ...
//const FIXED_CATEGORY_CODE = 'PLLS'; // Código do "Visto Nacional" (Baseado na API 3)
//const FIXED_SUBCATEGORY_CODE = 'LONG'; // Código do Visto Longa Duração (Assumindo que este é o 'LONG')



// 🚨 IMPORTANTE: Substitua 'CHAVE_REAL_DO_IP_NO_STORAGE' pelo nome da chave no sessionStorage
// Exemplo: Se você viu a chave 'VFS_CLIENT_IP', use 'VFS_CLIENT_IP' aqui.
const VFS_IP_KEY = "ip"; 

// 🚨 IMPORTANTE: Substitua 'IPAddress' pelo nome exato do campo que a API 6 espera na payload
const API_6_IP_FIELD = "105.172.247.103";
// ...
    // --- Parâmetros fixos para o Turnstile VFS Global ---
const CF_TURNSTILE_SITEKEY = "0x4AAAAAABhlz7Ei4byodYjs";
const CF_TURNSTILE_URL_VALIDACAO = 'https://challenges.cloudflare.com/cdn-cgi/challenge-platform/h/g/turnstile/f/ov2/av0/rch/jpo0o/0x4AAAAAABhlz7Ei4byodYjs/auto/fbE/new/normal?lang=auto';

// --- Headers mínimos para a requisição de liberação ---
const CF_HEADERS_POST = {
    "Content-Type": "application/json",
    // Os headers de origem/referer são CRUCIAIS para a CF
    "Origin": "https://challenges.cloudflare.com",
    "Referer": CF_TURNSTILE_URL_VALIDACAO,
    "sec-fetch-dest": "empty",
    "sec-fetch-mode": "cors",
    "sec-fetch-site": "same-origin",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
};
                            
   const RETRY_INTERVAL_MS = 300000; // 5 minutos
const COUNTRY_CODE = "AGO"; // 🛑 CORRIGIDO: Definida aqui para o escopo
const MISSION_CODE = "prt";
const VAC_CODE = "POLU";

 
    // --- VARIÁVEIS DE CONTROLE DO LOOP (UNIFICADAS) ---
// 15 segundos entre checagens
    
    let globalVfsContext = null;    // Contexto VFS Unificado 
    let globalAutomationRunning = false; 
    let globalBookingTimer = null;  // Variável para controlar o loop 
    let globalApplicantId = null;   // Guarda o ID do requerente criado
    let globalHourlyScheduler = null;
    let globalStopScheduler = null;
    let  globalContinuousAlertRunning = false;
    let globalContinuousAlertTimeout = null;


    // --- CONFIGURAÇÕES DAS API VFS ---
   
    const VFS_APPLICANT_CREATE_URL = 'https://lift-api.vfsglobal.com/appointment/applicants';
    
    
    const VFS_BASE_URL = "https://visa.vfsglobal.com";
// ==========================================================
// 1. VARIÁVEIS GLOBAIS DE CONTEXTO (Angola -> Portugal)
// ==========================================================
let currentStep = "Início do Ciclo";
const mission = 'prt';  // Portugal (País de Destino)
const country = 'AGO';  // Angola (País de Aplicação)
const vac = 'POLU';     // Luanda (Código ISO do Centro de Vistos)
let categoryCode = '';  // O código do visto selecionado pelo usuário (Ex: 'PLLS')
let subCategoryCode = ''; // O código da subcategoria selecionada (Ex: 'LONG')
const STORAGE_KEYS = {
    API_FIXED: 'vfs_context_api_fixed',
    PAYMENT: 'vfs_context_payment',
    CATEGORIES: 'vfs_context_categories'
};




const INJECTED_API_RESPONSE_KEY = 'vfs_injected_api_response';

// 🛑 ADICIONE ESTA LINHA:
const CUSTOM_EVENT_TRIGGER = 'VFS_INJECTED_CALL';





    const FIELD_MAP = [
        { name: "firstName", label: "First Name", site: "#mat-input-0", type: "text", placeholder: "Enter your first name", maxlength: 60 },
        { name: "lastName", label: "Last Name", site: "#mat-input-1", type: "text", placeholder: "Please enter last name.", maxlength: 60 },
        { name: "gender", label: "Gender", site: "#mat-select-value-3", type: "select", placeholder: "Select" },
        { name: "dateOfBirth", label: "Date Of Birth", site: "#dateOfBirth", type: "date", placeholder: "Please select the date" },
        { name: "nationality", label: "Current Nationality", site: "#mat-select-value-4", type: "select", placeholder: "Select" },
        { name: "passportNumber", label: "Passport Number", site: "#mat-input-2", type: "text", placeholder: "Enter passport number", maxlength: 30 },
        { name: "passportExpiryDate", label: "Passport Expiry Date", site: "#passportExpirtyDate", type: "date", placeholder: "Please select the date" },
        { name: "contactCode", label: "Contact code", site: "#mat-input-3", type: "text", placeholder: "44", maxlength: 3 },
        { name: "contactNumber", label: "Contact Number", site: "#mat-input-4", type: "text", placeholder: "Contact number", minlength: 7, maxlength: 15 },
        { name: "email", label: "Email", site: "#mat-input-5", type: "email", placeholder: "Enter Email Address", maxlength: 50 }
    ];






const NACIONALIDADES_DATA_COMPLETA = [
    "AFGHANISTAN", "ALBANIA", "ALGERIA", "ANGOLA", "ANGUILLA", "ANTIGUA AND BARBUDA", 
    "ARGENTINA", "ARMENIA", "ARUBA", "AUSTRALIA", "AUSTRIA", "AZERBAIJAN", "BAHAMAS", 
    "BAHRAIN", "BANGLADESH", "BARBADOS", "BELARUS", "BELGIUM", "BELIZE", "BENIN", 
    "BERMUDA", "BHUTAN", "BOLIVIA", "BOSNIA AND HERZEGOVINA", "BOTSWANA", "BRAZIL", 
    "BRITISH VIRGIN ISLANDS", "BRUNEI DARUSSALAM", "BULGARIA", "BURKINA FASO", "BURUNDI", 
    "CAMBODIA", "CAMEROON", "CANADA", "CAPE VERDE", "CAYMAN ISLANDS", "CENTRAL AFRICAN REPUBLIC", 
    "CHAD", "CHILE", "CHINA", "CHRISTMAS ISLAND", "COCOS (KEELING) ISLANDS", "COLOMBIA", 
    "COMOROS", "CONGO", "COOK ISLANDS", "COSTA RICA", "COTE D'IVOIRE", "CROATIA", "CUBA", 
    "CYPRUS", "CZECH REPUBLIC", "DEMOCRATIC REPUBLIC OF CONGO", "DENMARK", "DJIBOUTI", 
    "DOMINICA", "DOMINICAN REPUBLIC", "ECUADOR", "EGYPT", "EL SALVADOR", "EQUATORIAL GUINEA", 
    "ERITREA", "ESTONIA", "ETHIOPIA", "FALKLAND ISLANDS", "FAROE ISLANDS", "FIJI", 
    "FINLAND", "FRANCE", "GABON", "GAMBIA", "GEORGIA", "GERMANY", "GHANA", "GIBRALTAR", 
    "GREECE", "GREENLAND", "GRENADA", "GUATEMALA", "GUINEA", "GUINEA-BISSAU", "GUYANA", 
    "HAITI", "HOLY SEE", "HONDURAS", "HONG KONG", "HUNGARY", "ICELAND", "INDIA", 
    "INDONESIA", "IRAN", "IRAQ", "IRELAND", "ISRAEL", "ITALY", "IVORY COAST", "JAMAICA", 
    "JAPAN", "JORDAN", "KAZAKHSTAN", "KENYA", "KIRIBATI", "KOREA, DEMOCRATIC PEOPLES REP", 
    "KOSOVO", "KUWAIT", "KYRGYZSTAN", "LAOS", "LATVIA", "LEBANON", "LESOTHO", "LIBERIA", 
    "LIBYA", "LIECHTENSTEIN", "LITHUANIA", "LUXEMBOURG", "MACAU", "MACEDONIA", "MADAGASCAR", 
    "MALAWI", "MALAYSIA", "MALDIVES", "MALI", "MALTA", "MARSHALL ISLANDS", "MAURITANIA", 
    "MAURITIUS", "MEXICO", "MICRONESIA", "MOLDOVA", "MONACO", "MONGOLIA", "MONTENEGRO", 
    "MONTSERRAT", "MOROCCO", "MOZAMBIQUE", "MYANMAR, BURMA", "NAMIBIA", "NAURU", "NEPAL", 
    "NETHERLANDS", "NETHERLANDS ANTILLES", "NEW ZEALAND", "NICARAGUA", "NIGER", "NIGERIA", 
    "NORWAY", "OMAN", "PAKISTAN", "PALAU", "PALESTINE", "PANAMA", "PAPUA NEW GUINEA", 
    "PARAGUAY", "PERU", "PHILIPPINES", "PITCAIRN ISLAND", "POLAND", "PORTUGAL", "QATAR", 
    "REPUBLIC OF KOREA", "REPUBLIC OF MOLDOVA", "REPUBLIC OF SERBIA", "ROMANIA", 
    "RUSSIAN FEDERATION", "RWANDA", "SAINT KITTS AND NEVIS", "SAINT LUCIA", 
    "SAINT VINCENT AND THE GRENADINES", "SAMOA", "SAN MARINO", "SAO TOME AND PRINCIPE", 
    "SAUDI ARABIA", "SENEGAL", "SEYCHELLES", "SIERRA LEONE", "SINGAPORE", "SLOVAKIA", 
    "SLOVENIA", "SOLOMON ISLANDS", "SOMALIA", "SOUTH AFRICA", "SOUTH KOREA", 
    "SOUTH SUDAN", "SPAIN", "SRI LANKA", "St. KITTS & NEVIS", "STATELESS", "SUDAN", 
    "SURINAM", "SURINAME", "SWAZILAND", "SWEDEN", "SWITZERLAND", "SYRIA", 
    "Syria, Syrian Arab Republic", "SYRIAN ARAB REPUBLIC", "TAIWAN", "TAJIKISTAN", 
    "TANZANIA", "THAILAND", "TIBET", "TIMOR-LESTE (EAST TIMOR)", "TOGO", "TONGA", 
    "TRINIDAD AND TOBAGO", "TUNISIA", "Turkiye", "TURKMENISTAN", "TURKS AND CAICOS ISLANDS", 
    "TUVALU", "UGANDA", "UK BRITISH NATIONAL(OVERSEES)", "UK BRITISH SUBJECT", "UKRAINE", 
    "UNITED ARAB EMIRATES", "UNITED KINGDOM", "UNITED NATIONS ORGANIZATION", "UNITED STATES", 
    "URUGUAY", "UZBEKISTAN", "VANUATU", "VATICAN CITY", "VENEZUELA", "VIETNAM", 
    "VIRGIN ISLANDS (BRITISH)", "YEMEN", "ZAMBIA", "ZIMBABWE"
];



    // --- FUNÇÕES AUXILIARES NO ESCOPO GLOBAL/MÓDULO ---

/**
 * Retorno padronizado para todas as APIs e fluxos. Garante que messageCode seja sempre definido.
 * @param {boolean} success - Indica sucesso ou falha.
 * @param {object} [data={}] - Dados adicionais do resultado.
 * @param {string} [messageCode='NA'] - Código de mensagem para rastreamento.
 */
const defaultReturn = (success, data = {}, messageCode = 'NA') => ({
    success,
    ...data,
    messageCode: messageCode || 'NA'
});

// Content Script (painel.js / inject.js) - Gerenciador de Rotação

if (!window.location.host.includes('vfsglobal.com')) {
    console.log("Bot VFS: Script abortado. Não está no domínio da VFS.");
} else {
    console.log("✅ Gerenciador de Rotação VFS Ativado.");

    /**
     * Busca um ClientSource novo do Pool e força a rotação no Service Worker.
     * @returns {Promise<string>} O token único para esta requisição.
     */
    async function getRotatedClientsource(timeoutMs = 20000) {
        const startTime = Date.now();
        
        while (Date.now() - startTime < timeoutMs) {
            // Pedimos ao Service Worker para buscar E ROTACIONAR o próximo token
            const response = await new Promise((resolve) => {
                chrome.runtime.sendMessage({ action: "GET_CLIENTSOURCE" }, resolve);
            });

            if (response?.clientsource) {
                console.log(`🔄 Token Rotacionado obtido com sucesso.`);
                return response.clientsource;
            }

            // Se o pool estiver vazio, esperamos um pouco para ver se algo é capturado
            console.warn("⚠️ Pool vazio. Aguardando captura de tráfego...");
            await new Promise(r => setTimeout(r, 1000));
        }

        throw new Error("Timeout: Não foi possível obter um ClientSource rotacionado.");
    }

    window.getRotatedClientsource = getRotatedClientsource;

    /**
     * FLUXO PRINCIPAL: Executa a chamada da API garantindo um token novo.
     */
    async function executeVfsTask(payload) {
        try {
            // 1. Obtém o token "fresco" da vez (Rotação acontece aqui)
            const currentCS = await getRotatedClientsource();

            // 2. Aplica na sua função de fetch (exemplo)
            console.log("🚀 Disparando tarefa com ClientSource Único...");
            
            // Aqui entra a sua lógica de fetch passando o currentCS no header
            // const result = await suaFuncaoDeFetch(payload, currentCS);
            
            return true;
        } catch (error) {
            console.error(`❌ Erro no fluxo: ${error.message}`);
            return false;
        }
    }

    window.executeVfsTask = executeVfsTask;

    // ==========================================================
    // ESCUTADOR PARA O SEU PAINEL (UI)
    // ==========================================================
    window.addEventListener('message', async (event) => {
        if (event.data.type === 'VFS_START_AUTOMATION') {
            console.log("🤖 Automação iniciada pelo usuário.");
            await executeVfsTask(event.data.payload);
        }
    });
}

/**
 * Nota para o Valdemiro:
 * No seu Service Worker (background.js), certifique-se que o case "GET_CLIENTSOURCE"
 * use o pool.shift() para que cada vez que o Content Script chamar essa função,
 * o token anterior seja descartado e o próximo seja entregue.
 */


// Variável para garantir que o script de injeção só seja carregado uma vez
let scriptInjected = false;

/**
 * Garante que o arquivo injector.js seja carregado no escopo da página.
 * Resolve o problema do Content Security Policy (CSP).
 */
/**
 * Garante que o arquivo injector.js seja carregado no escopo da página.
 * Resolve o problema do Content Security Policy (CSP).
 */
function ensureScriptInjected() {
    if (scriptInjected) return Promise.resolve();

    return new Promise((resolve) => {
        const script = document.createElement('script');
        script.src = chrome.runtime.getURL('injector.js');

        script.onload = () => {
            scriptInjected = true;
            console.log("CONTENT: injector.js carregado com sucesso.");
            resolve();
        };
        
        // Adicionando um log de erro no script
        script.onerror = (e) => {
            console.error("CONTENT: Falha ao carregar injector.js. Verifique o manifest.json.", e);
            scriptInjected = true; // Força a continuação para evitar loop infinito
            resolve();
        };

        (document.head || document.documentElement).appendChild(script);
    });
}
// Controle global para evitar duplicar a MESMA etapa ao mesmo tempo
const activeVfsSteps = new Set();

async function fetchDataInjected(url, stepName, headers, method, body) {
    
    // --- 0. PROTEÇÃO CONTRA DUPLICAÇÃO POR ETAPA ---
    // Se a etapa "CENTERS" já está rodando, não deixa disparar outra igual
    if (activeVfsSteps.has(stepName)) {
        console.warn(`⏳ [FETCH] Etapa ${stepName} já está em curso. Ignorando duplicata.`);
        return; // Ou você pode lançar um erro
    }
    activeVfsSteps.add(stepName);

    try {
        // --- 1. ROTAÇÃO AUTOMÁTICA ---
        const freshCS = await getRotatedClientsource();
        headers = headers || {};
        headers['clientsource'] = freshCS;
        
    } catch (e) {
        console.error(`[ROTATOR] Erro no token para ${stepName}: ${e.message}`);
        // Se falhar o token, removemos do set para permitir tentar de novo depois
        activeVfsSteps.delete(stepName);
        throw e; 
    }

    return new Promise((resolve, reject) => {
        const callId = stepName + '_' + Date.now() + '_' + Math.random().toString(36).substring(2, 9);
        
        const listener = (event) => {
            if (event.data.key === INJECTED_API_RESPONSE_KEY && event.data.callId === callId) {
                // LIMPEZA IMEDIATA
                window.removeEventListener('message', listener); 
                activeVfsSteps.delete(stepName); // Libera a etapa para futuras chamadas
                
                const responsePayload = event.data.payload;
                const errorData = responsePayload.error || "";

                if (responsePayload.status === 403 || errorData.includes("cloudflare")) {
                    const cfError = new Error("CLOUDFLARE_DETECTED");
                    cfError.status = 403;
                    reject(cfError);
                    return;
                }

                if (responsePayload.success) {
                    resolve(responsePayload.data); 
                } else {
                    const error = new Error(`Falha na API: ${responsePayload.status}`);
                    error.status = responsePayload.status;
                    reject(error);
                }
            }
        };

        window.addEventListener('message', listener, false);

        // --- 2. DISPARO ---
        const eventTrigger = new CustomEvent(CUSTOM_EVENT_TRIGGER, {
            detail: {
                args: JSON.stringify({
                    url, stepName, headers, method, body, callId 
                })
            }
        });
        window.dispatchEvent(eventTrigger);
        
        // Timeout de segurança: Se a API não responder em 30s, libera a trava
        setTimeout(() => {
            if (activeVfsSteps.has(stepName)) {
                activeVfsSteps.delete(stepName);
                // Opcional: reject(new Error("TIMEOUT"));
            }
        }, 30000);
    });
}
 ensureScriptInjected()

/**
 * Trata falhas críticas no fluxo, injeta controles de usuário e retorna um objeto de falha consistente.
 */
async function handleFailure(
    applicantName, 
    lastSuccessfulStep, 
    errorName, 
    errorObject, 
    applicantData, 
    globalVfsContext, 
    executarFluxoComRetry // Função de retry
) {
    // Loga a falha
    apiLog(`[${applicantName}] ❌ FALHA em ${lastSuccessfulStep}. Motivo: ${errorName}`, true, 'error');
    
    // Define o callback de retry
    const retryCallback = () => executarFluxoComRetry( applicantData, globalVfsContext, lastSuccessfulStep);
    
    // Define o callback de cancelamento
    const cancelCallback = () => apiLog(`[${applicantName}] Processo cancelado e ciclos limpos.`, true); 

    // Injeta os botões na interface do usuário
    // Assume que window.criarBotaoDeControleDeFalha está no escopo
    window.criarBotaoDeControleDeFalha(lastSuccessfulStep, retryCallback, cancelCallback);
    
    // Extrai o código de erro, garantindo um fallback.
    // Usamos o operador '?' para verificar se errorObject existe antes de tentar ler messageCode.
    const finalMessageCode = errorObject?.messageCode || 'FALHA_NAO_MAPEADA';
    
    // 🔑 CORREÇÃO CRÍTICA: Construímos o objeto de dados extra (data) 
    //    incluindo explicitamente o objeto de erro completo (errorObject).
    const extraData = {
        lastSuccessfulStep: lastSuccessfulStep,
        // Incluímos o errorObject para satisfazer o consumidor que espera a propriedade '.error'
        error: errorObject
    };

    // Retorna o resultado final de falha usando o defaultReturn global.
    return defaultReturn(
        false, 
        extraData, // Agora inclui { error: errorObject }
        finalMessageCode 
    ); 
}




window.apiLog = function(msg, isSlotLog = false, silent = false) {
    // 1. Tratamento de SILENT
    if (silent) {
        if (!isSlotLog) console.log(`VFS Bot (Sistema Silencioso): ${msg}`);
        return;
    }

    // 2. UNIFICAÇÃO DO ID
    const logEl = document.getElementById("fullLog");
    if (!logEl) return;

    // 3. Determinação da COR e DESTAQUE (Regras do usuário)
    let lineColor = "#e6e6e6"; 
    let applyHighlight = false;

    if (isSlotLog) {
        // --- LOGS DE VAGA ---
        applyHighlight = true;
        if (msg.includes('SUCESSO') || msg.includes('ABERTO') || msg.includes('ARN')) {
            lineColor = "#9fff9f"; // VERDE
        } else {
            lineColor = "#e04b4b"; // VERMELHO
        }
    } else {
        // --- LOGS DE SISTEMA/API ---
        if (msg.includes('❌') || msg.includes('ERRO STATUS') || msg.includes('PARSE_FAIL') || msg.includes('NETWORK_ERROR')) {
            lineColor = "#ffe066"; // AMARELO (Erro de Sistema)
            applyHighlight = true;
        } else if (msg.includes('INICIANDO') || msg.includes('AGENDADA') || msg.includes('SUCESSO')) {
            lineColor = "#d4af37"; // Dourado (Destaque do ciclo)
            applyHighlight = true;
        } 
        // Senão, usa o padrão: #e6e6e6 (Branco)
    }

    // 4. Criação da Mensagem e Elemento
    const now = new Date().toLocaleTimeString(); 
    const logLine = document.createElement('div');
    logLine.className = 'log-line'; 
    
    // 🛑 CORREÇÃO APLICADA AQUI: Separa o timestamp em uma tag <span class="log-timestamp">
    logLine.innerHTML = `
        <span style="color: ${lineColor}; font-weight: ${applyHighlight ? 'bold' : 'normal'}">
            <span class="log-timestamp">${now} | </span>
            <span class="log-content">${msg}</span>
        </span>
    `;

    // 5. Aplica Destaque e Insere no FIM (appendChild)
    if (applyHighlight) {
        logLine.classList.add('log-highlight');
    }
    
    logEl.appendChild(logLine);
    
    // 6. GARANTIR VISIBILIDADE: Força o scroll para a última mensagem inserida.
    logEl.scrollTop = logEl.scrollHeight;
    
    // ⭐️ PASSO DE PERSISTÊNCIA 1: Salva o conteúdo completo no Session Storage 
    sessionStorage.setItem('vfs_automacao_log', logEl.innerHTML);
};

const apiLog = window.apiLog || ((msg, important = false, level = 'info') => {
///Isto garante que apiLog é SEMPRE uma função, evitando o 'TypeError'
   console.log(`[${level.toUpperCase()}] ${msg}`);
});

// Usamos a função global (agora definida) no escopo do módulo


// ----------------------------------------------------------------------

/**
 * A função flashMessage está correta, pois ela chama apiLog (que agora está corrigida)
 * e depois injeta um elemento DIV temporário, o que é um comportamento separado do log principal.
 */
function flashMessage(panelRoot, msg) {
    apiLog(`(FLASH) ${msg}`, false); 
    
    const logEl = document.createElement("div");
    logEl.style.color = msg.includes('❌') ? "#e04b4b" : "#d4af37";
    logEl.style.marginTop = "6px";
    logEl.textContent = msg;
    const formArea = panelRoot.querySelector("#formArea");
    if (formArea) formArea.appendChild(logEl);
    setTimeout(() => logEl.remove(), 2500);

    }

/**
 * Limpa todos os campos de entrada na área de formulário e reinicia o estado de edição.
 * Assume-se que 'FIELD_MAP' está definida globalmente.
 * @param {HTMLElement} panelRoot O elemento raiz do painel VFS.
 */
function clearForm(panelRoot) {
    // Assumindo que FIELD_MAP está definido e acessível
    FIELD_MAP.forEach(f => {
        const el = panelRoot.querySelector("#panel_" + f.name);
        if (el) {
            // REGRA PARA O EMAIL: Não limpa, mantém o fixo
            if (f.name === "email") {
                el.value = "ceo@advence.com.ao";
            } 
            // PARA OS OUTROS: Limpa normalmente
            else {
                el.value = "";
            }
        }
    });
    
    // Reseta o estado de edição
    if (panelRoot && panelRoot.dataset) {
        panelRoot.dataset.editingId = "";
        const saveBtn = panelRoot.querySelector("#btnSave");
        if (saveBtn) saveBtn.textContent = "💾 Salvar";
    }
}
  
/**
 * Gerencia o armazenamento e recuperação do contexto VFS no sessionStorage.
 * @param {Object} context Objeto de contexto completo da API (opcional, para salvar).
 * @param {string} keyName Nome da chave a ser recuperada (opcional, para recuperar).
 * @returns {Object|null} O objeto recuperado, ou null se não for encontrado.
 */
function handleContextStorage(context = null, keyName = null) {
    
    // --- MODO DE RECUPERAÇÃO ---
    if (keyName && STORAGE_KEYS[keyName]) {
        try {
            const stored = sessionStorage.getItem(STORAGE_KEYS[keyName]);
            return stored ? JSON.parse(stored) : null;
        } catch (e) {
            console.error(`Erro ao recuperar contexto da chave ${STORAGE_KEYS[keyName]}:`, e);
            return null;
        }
    }

    // --- MODO DE SALVAMENTO ---
    if (context && context.isLoaded) {
        try {
            // 1. Contexto Fixo (Configurações/API Base)
            const apiFixed = {
                email: context.email,
                // 🔑 CORREÇÃO CRÍTICA: Adicionar o token aqui
                token: context.token || context.jwtToken, // Use o nome correto do campo
                officeId: context.officeId,
                officeName: context.officeName,
                missionId: context.missionId,
                missionName: context.missionCode,
                countryCode: context.countryCode 
            };
            sessionStorage.setItem(STORAGE_KEYS.API_FIXED, JSON.stringify(apiFixed));

            // 2. Detalhes de Pagamento (Configurações)
            const paymentDetails = {
                paymentTypeId: context.paymentTypeId,
                paymentName:context.paymentName
                // Adicione outros detalhes de pagamento aqui
            };
            sessionStorage.setItem(STORAGE_KEYS.PAYMENT, JSON.stringify(paymentDetails));

            // 3. Categorias (Gerenciar Requerentes/Pop-up)
            const categories = {
                visaSubCategory: context.visaSubCategory, // Categoria Base da sessão
                visaSubCategoryName: context.visaSubCategoryName,
                visaSubCategories: context.visaSubCategories // Lista completa
            };
            sessionStorage.setItem(STORAGE_KEYS.CATEGORIES, JSON.stringify(categories));

            return true;
        } catch (e) {
            console.error("Erro ao salvar contexto VFS no sessionStorage:", e);
            return false;
        }
    }
    return false; // Retorno padrão para caso não tenha salvo nada
}

    // ==========================================================
    // 4. FUNÇÕES DE AUTENTICAÇÃO E CONTEXTO VFS (API 1-4)
    // ==========================================================
  function apiDetectAuth() { 
    let token = null;
    let IP = null;
    let email = null;
    
    try {
        // --- 1. JWT (TOKEN) ---
        // Busca nos locais padrão de armazenamento
        token = sessionStorage.getItem('JWT') || localStorage.getItem('JWT');
        
        // Validação mínima de tamanho para evitar strings vazias ou lixo
        if (token && token.length < 50) token = null; 
        
        // Se não achou, tenta no contexto global do site ou na chave 'token' simples
        if (!token && window.liftContext && window.liftContext.jwt) {
            token = window.liftContext.jwt;
        } else if (!token && localStorage.getItem('token')) {
            token = localStorage.getItem('token');
        }

        // --- 2. IP ---
        // Busca o IP armazenado
        IP = sessionStorage.getItem('ip') || localStorage.getItem('ip');
        
        // Validação de IP (mínimo de caracteres para um IP válido)
        if (IP && IP.length < 7) IP = null; 

        // Fallback para IP no liftContext ou localStorage
        if (!IP && window.liftContext && window.liftContext.ip) {
            IP = window.liftContext.ip;
        } else if (!IP && localStorage.getItem('ip')) {
            IP = localStorage.getItem('ip');
        }

    } catch(e) { /* ignore */ }
    
    try {
        // --- 3. EMAIL ---
        
        // A. Busca no DOM (Elementos da página)
        // Adicionei seletores comuns da VFS que contém o email no topo
        const emailElement = document.querySelector('.profile-name') || 
                             document.querySelector('div.dropdown-menu span.profile-name') ||
                             document.querySelector('.user-profile-email');

        if (emailElement) { 
            const text = emailElement.textContent.trim();
            // Verifica se o texto realmente parece um email (contém @)
            if (text.includes('@')) {
                email = text;
            }
        }

        // B. Busca no Storage (Corrigindo a chave 'logged_email' que você mencionou)
        if (!email) { 
            // Tenta primeiro a chave correta que você confirmou, depois a secundária
            email = sessionStorage.getItem('logged_email') || 
                    localStorage.getItem('logged_email') ||
                    sessionStorage.getItem('logged_emailS'); // Mantido por segurança
        }

        // C. Fallback final no liftContext
        if (!email && window.liftContext && window.liftContext.email) { 
            email = window.liftContext.email; 
        }

    } catch(e) { /* ignore */ }

    // Limpeza final: garante que se o email for capturado de um objeto, seja apenas texto
    return { 
        token: token, 
        IP: IP, 
        email: email ? email.toLowerCase().trim() : null 
    };
}

   function updatePanelContext(context) {
    const summaryDiv = document.getElementById('sessionSummary');
    const emailDiv = document.getElementById('emailDisplay');
    
    if (!summaryDiv || !emailDiv) return;

    // --- 1. Exibir Email/Status de Login ---
    if (context && context.email) {
        emailDiv.innerHTML = `
            <div style="font-size:12px; color:#f0f0f0; margin-bottom:10px; padding:6px 10px; background:#1e1e1e; border-radius:6px;">
                Logado como: <strong style="color:#d4af37;">${context.email}</strong>
            </div>
        `;

    } else {
        // Status de Email "Não Logado"
        emailDiv.innerHTML = `
            <div style="font-size:12px; color:#ff4d4d; margin-bottom:10px; padding:6px 10px; background:#1e1e1e; border-radius:6px;">
                Email não detectado. Assegure-se de estar logado.
            </div>
        `;
        // Define o estado inicial da summaryDiv
        summaryDiv.innerHTML = `<div style="color: #c7c7c7; font-size: 14px;">Aguardando login ou contexto de sessão...</div>`;
        summaryDiv.style.color = '#c7c7c7';
        return; // Retorna se não houver email, evitando tentar carregar o resumo VFS.
    }


    // --- 2. Exibir Resumo de Sessão (Contexto VFS Detalhado) ---
    // Esta seção só é executada se o 'email' estiver presente (passou pela checagem acima).
    
    // NOTA: Os nomes das chaves ('centerName', 'vacCode', etc.) são assumidos a partir da API.
    const centerDisplay = context.officeName || 'N/A'; // Usando officeName como Center Name
    const categoryNameDisplay = context.visaCategoryName || 'N/A';
    const vacCodeDisplay = context.vacCode || 'N/A';
    const missionCodeDisplay = context.missionId || 'N/A'; // Usando missionId como Mission Code
    
    // Novos campos de pagamento
    const paymentNameDisplay = context.paymentName || 'N/A';
    const paymentCodeDisplay = context.paymentTypeId || 'N/A'; // Usando paymentTypeId como Payment Code

    // Verifica se o contexto mínimo VFS (Centro e Categoria Base) está presente
    if (context.officeName && context.visaSubCategoryName) { 
        
        summaryDiv.innerHTML = `
            <strong style="color:#d4af37">Contexto de Sessão Detectado:</strong><br>
            
            <span style="font-weight:600;">📍 Centro:</span> ${context.officeName}<br>
            <span style="font-weight:600;">📄 Missão:</span> ${context.missionName} &rarr; ${context.visaSubCategoryName}<br>
            <span style="font-weight:600;">💳 Pagamento:</span> ${paymentContext.paymentName} 
            
            <div style="font-size:11px; color:#9f9686; margin-top:5px;">
                (Mission ID: ${missionCodeDisplay} / Payment ID: ${context.paymentTypeId})
            </div>
        `;
        summaryDiv.style.color = '#efe7db';
        
    } else {
        // Email está logado, mas as APIs de Contexto VFS não retornaram dados completos.
        summaryDiv.innerHTML = 'Email logado, mas contexto VFS (Centro/Visto) pendente.';
        summaryDiv.style.color = '#d4af37';
    }
}


/**
 * Atualiza a aba Configurações com detalhes fixos de API (Centro/Missão) e Pagamento,
 * lidos diretamente do armazenamento de sessão.
 * @param {HTMLElement} settingsContainer O elemento raiz da aba de configurações (#settings).
 */
function updateSettingsContext(settingsContainer) {
    // Note: apiDetectAuth() deve ser uma função síncrona ou este bloco deve ser async.
    const { email: detectedEmail } = apiDetectAuth(); 
    
    // Recupera os contextos do armazenamento de sessão
    const apiFixed = handleContextStorage(null, 'API_FIXED');
    const paymentDetails = handleContextStorage(null, 'PAYMENT');
    const categoriesContext = handleContextStorage(null, 'CATEGORIES');
    
    // Checagem primária: se o contexto fixo não estiver carregado, não podemos prosseguir.
    if (!apiFixed) {
        const detailsDiv = settingsContainer.querySelector("#settingsContextDetails");
        if (detailsDiv) {
            detailsDiv.innerHTML = '⚠️ **Contexto VFS não carregado.** Assegure-se de estar logado e que o bot tenha extraído os dados da sessão.';
        }
        return;
    }

    const detailsDiv = settingsContainer.querySelector("#settingsContextDetails");
    if (detailsDiv) {
        
        // ✅ CORREÇÃO CRÍTICA: 'apiFixed' usado em vez de 'context' e encadeamento opcional para segurança
        
        detailsDiv.innerHTML = `
            <strong style="color:#efe7db; border-bottom: 1px dashed rgba(212,175,55,0.2); padding-bottom: 3px; display: block; margin-bottom: 8px;">Detalhes Fixos da Sessão:</strong>
            
            <span style="font-weight:600;">📧 Email de Sessão:</span> ${ detectedEmail || 'N/A'}<br>
            <span style="font-weight:600;">🌍 País/Missão:</span> ${ apiFixed?.countryCode || 'N/A'} / ${apiFixed?.missionName || 'N/A'}<br>
            <span style="font-weight:600;">📍 CENTRO DE VISTOS:</span> ${apiFixed?.officeName || 'N/A'} ( ${apiFixed?.officeId || 'N/A'})<br>
            
            <hr style="border: none; border-top: 1px solid rgba(212,175,55,0.1); margin: 10px 0;">
            
            <strong style="color:#efe7db; border-bottom: 1px dashed rgba(212,175,55,0.2); padding-bottom: 3px; display: block; margin-bottom: 8px;">Contexto de Visto e Pagamento:</strong>

            <!-- Confirma se as categorias foram carregadas -->
            <span style="font-weight:600;">📄 Categorias Visto Disponíveis:</span> ${categoriesContext?.visaSubCategories?.length || 0}  ✅ Carregadas<br>
            
            <!-- ✅ CORREÇÃO: Acessa as propriedades diretamente em paymentDetails -->
            <span style="font-weight:600;">💳 EMPRESA RESPONSÁVEL DAS TRANSAÇÕES:</span> ${paymentDetails?.paymentTypeId || 'N/A'}<br>
            <span style="font-weight:600;">💳 METÓDO DE PAGAMENTO:</span> ${paymentDetails?.paymentName || 'N/A'}<br>

            <div style="font-size:12px; color:#d4af37; margin-top: 10px;">
                Total de Subcategorias carregadas: <strong>${categoriesContext?.visaSubCategories?.length || 0}</strong>
            </div>
        `;
    }
}
// --- Funções Auxiliares de Extração (PLACEHOLDERS) ---

// Você deve implementar estas funções para extrair as variáveis do DOM/Globals
function extractApiFixedContext() {
    // Exemplo: window.VfsGlobal.OfficeId
    return { 
        officeId: window.VfsGlobalConfig?.OfficeId || null,
        officeName: window.VfsGlobalConfig?.OfficeName || 'N/A',
        missionId: window.VfsGlobalConfig?.MissionId || null,
        missionName: 'N/A',
        countryCode: 'N/A'
    };
}

function extractCategoriesContext() {
    // Exemplo: window.VfsBooking.CategoriesList
    const rawCategories = window.VfsBooking?.CategoriesList || [];
    
    return {
        visaSubCategory: window.VfsBooking?.SelectedCategoryId || null,
        visaSubCategoryName: window.VfsBooking?.SelectedCategoryName || 'N/A',
        visaSubCategories: rawCategories.map(c => ({ id: c.Id, name: c.Name }))
    };
}

function extractPaymentContext() {
    // Exemplo: window.VfsPayment.SelectedType
    return { 
        paymentTypeId: window.VfsPayment?.SelectedTypeId || null,
        paymentName: window.VfsPayment?.SelectedTypeName || 'N/A'
    };
}

/**
 * Orquestra a extração de todo o contexto VFS (Autenticação, Centro, Categoria, Pagamento).
 * @returns {Promise<Object|null>} Objeto de contexto VFS completo, ou null se não estiver logado.
 */
async function loadVfsContext() {
    const authData = apiDetectAuth();
     if (!authData.token || !authData.IP|| !authData.email) {
        apiLog("Autenticação não detectada. Não é possível carregar o contexto VFS.");
        return null;
    }

    // 🛑 ESTE PASSO É OBRIGATÓRIO E CRÍTICO
    await ensureScriptInjected();
   
    // 1. 🚨 DECLARAÇÃO E OBTENÇÃO DO CLIENTSOURCE (NO TOPO)
 

   // 2. CRIAÇÃO DO OBJETO CONTEXTO (DEPOIS DA OBTENÇÃO DO TOKEN)
    const context = {
        email: authData.email,
        IP: authData.IP,
        jwtToken: authData.token,
        isLoaded: false,
     
    };
    
    
    
    try {
        // 1. EXTRAÇÃO INICIAL (API 1-3)
         const browserContext = await fetchVfsContextInBrowser(
            authData.token, 
            authData.email, 
            
           
        ); 
        Object.assign(context, browserContext);

        // 2. CORREÇÃO E PADRONIZAÇÃO DAS CHAVES CRÍTICAS
        context.missionCode = (context.missionCode || "prt").toLowerCase(); 
        context.countryCode = (context.countryCode || "AGO").toUpperCase(); 
        context.vacCode = context.vacCode || currentVACCode; 

        // 3. CONSOLIDAÇÃO CRÍTICA DAS SUBCATEGORIAS (API 4)
        if (context.missionCode && context.vacCode && context.primaryCategories?.length > 0) {
            
          //  apiLog("DEBUG: Iniciando consolidação das Subcategorias.", false);
            
            const allSubcategories = await fetchAllSubcategories(
                context.jwtToken,
                context.missionCode, 
                context.countryCode, 
                context.vacCode, 
                context.primaryCategories,
             
            );
            
            context.visaSubCategories = allSubcategories; 
         //   apiLog(`DEBUG: Consolidação de Subcategorias concluída. Total: ${allSubcategories.length}.`, false);

            // --- LÓGICA DE SELEÇÃO E ATRIBUIÇÃO CORRETA ---
            
            // 3.1. Achar o código da Categoria Principal (Ex: 'PLLS')
            const nationalCategory = context.primaryCategories.find(c => c.name.includes("Nacional"));
            const baseCode = nationalCategory?.code || context.primaryCategories[0]?.code || "0001";
            context.baseVisaCode = baseCode; // Salva o PLLS

            // 3.2. Achar a Subcategoria correspondente (LONG, JOS, ou CHE)
            const selectedSubcategory = context.visaSubCategories.find(sc => sc.primaryCode === baseCode) || context.visaSubCategories[0];

            // 3.3. ATRIBUIÇÃO: O CÓDIGO USADO PARA O PAGAMENTO (DEVE SER LONG/CHE/JOS)
            const subCategoryCodeForPayment = selectedSubcategory?.code || 'LONG'; 

            // Atribuição das IDs corretas para o agendamento e pagamento
            context.visaSubCategory = selectedSubcategory?.id || null; // ID para agendamento (Ex: 5956)
            context.visaSubCategoryName = selectedSubcategory?.name || 'Visto Nacional'; // Nome para display
            context.subCategoryCodeForPayment = subCategoryCodeForPayment; // LONG (ou outro)

            // 4. EXTRAI MODO DE PAGAMENTO (ÚNICA CHAMADA)
            ////apiLog(`DEBUG: Iniciando Extração do Modo de Pagamento com código: ${subCategoryCodeForPayment}.`, false);

            const paymentMode = await fetchAndSetPaymentMode(
                context.jwtToken,
                 context.subCategoryCodeForPayment,
            );
            Object.assign(context, paymentMode || {});

            // 5. MARCA COMO CARREGADO E RETORNA
         //   apiLog("DEBUG: loadVfsContext concluído com sucesso.", false);
            context.isLoaded = true;
            return context;

        } else {
            apiLog("⚠️ Falha crítica: Dados de Centro/Missão ou Categorias Primárias ausentes.");
            return null;
        }

    } catch (e) {
        // Loga o erro exato que derrubou o processo para o usuário
        apiLog(`❌ Erro FATAL no orquestrador loadVfsContext: ${e.message}`, true);
        console.error("Erro no loadVfsContext:", e);
        return null; 
    }
}



// --- INICIALIZAÇÃO AUTOMÁTICA DO CONTEXTO VFS ---
// Chamado imediatamente quando o script/painel é injetado.
// --- INICIALIZAÇÃO AUTOMÁTICA DO CONTEXTO VFS (Fluxo de Carregamento) ---
// 🛑 Este bloco de código executa IMEDIATAMENTE.
(async function initializeContext() {
  //  apiLog("Iniciando pré-carga do Contexto VFS...", false);
    
    // ⬅️ Passa 'true' para permitir a execução das APIs 1-4, caso não haja contexto.
    const context = await loadVfsContextOrchestrator(true); 
    
    if (context && context.isLoaded) {
      //  apiLog("Pré-carga do Contexto VFS concluída. O sistema está pronto.", true);
        // Opcional: Ativar botão "Iniciar"
    } else {
     //   apiLog("⚠️ Alerta: O Login/Contexto VFS não pôde ser carregado. Por favor, reinicie a página ou verifique suas credenciais.", true);
    }
})();
// --- FIM DA INICIALIZAÇÃO IMEDIATA ---
function saveVfsContext(context) {
    // ⚠️ ATENÇÃO: Verifique se o campo de token é 'token' ou 'jwtToken' no objeto 'context'
    // E ajuste a checagem de validação
    if (!context || !context.missionCode || !context.token) { // Use 'context.token' se for o nome correto
     //   apiLog("⚠️ Não foi possível salvar: Contexto incompleto.", false);
        return;
    }
    
    const criticalData = {
       languageCode: context.cultureCode,
        email: context.email,
        token: context.token, // Ajuste este campo para o nome correto
        IP : context.IP,
        missionCode: context.missionCode,
        countryCode: context.countryCode,
        vacCode: context.vacCode,
        officeId: context.officeId || null,
        primaryCategories: context.primaryCategories,
        // Inclua as categorias e pagamentos para persistência do painel
        visaSubCategories: context.visaSubCategories || null, 
        paymentTypes: context.paymentTypes || null,
    };

    try {
        sessionStorage.setItem(CONTEXT_STORAGE_KEY, JSON.stringify(criticalData));
       // apiLog(`✅ Contexto VFS UNIFICADO salvo na chave: ${CONTEXT_STORAGE_KEY}.`, false);
    } catch (e) {
      //  apiLog(`❌ Erro ao salvar contexto unificado: ${e.message}`, true);
    }
}

async function loadVfsContextOrchestrator(isInitialization = false) {
    // 1. Tenta carregar da memória (mais rápido)
    if (globalVfsContext && globalVfsContext.isLoaded) {
        return globalVfsContext;
    }
    
    let context = null;

    // 2. Tenta carregar do Storage UNIFICADO (rápido)
    try {
        const unifiedContextStr = sessionStorage.getItem(CONTEXT_STORAGE_KEY);
        if (unifiedContextStr) {
            context = JSON.parse(unifiedContextStr);
            if (context && context.missionCode && context.jwtToken) {
                context.isLoaded = true;
                globalVfsContext = context;
              //  apiLog(`✅ Contexto VFS UNIFICADO carregado do Storage.`, true);
                return context;
            }
        }
    } catch (e) {
    apiLog("⚠️ Erro ao carregar contexto unificado do Storage.", false);
    }
    
    // 🛑 PONTO DE INTERRUPÇÃO CRÍTICO
    // Se o contexto não for encontrado no Storage e NÃO estivermos na inicialização,
    // paramos aqui para evitar a execução repetida das APIs 1-4.
    if (!isInitialization) {
      //  apiLog("❌ Contexto ausente. Não forçando novo Login/Orquestração durante o ciclo.", true);
        return null;
    }

    // 3. SOMENTE SE isInitialization for TRUE: Executa a orquestração COMPLETA (APIs 1-4)
   apiLog("Contexto não encontrado. Iniciando Orquestração de API (APIs 1-4) na inicialização...", true);
    context = await loadVfsContext(); // ⬅️ SUA FUNÇÃO ORIGINAL DE LOGIN/APIs 1-4

    if (context && context.isLoaded) {
        // 4. SUCESSO: SALVA E RETORNA
        globalVfsContext = context;
        saveVfsContext(context); 
    //    apiLog(`✅ Contexto VFS Orquestrado, Carregado e Salvo com sucesso.`, true);
        return context;
    }
    
    apiLog("❌ LoadVfsContext falhou ou retornou contexto inválido na inicialização.", true);
    return null;
}
async function initialContextLoad(panelRoot) {
    let contextToLoad = null;
    let contextSuccess = false; 
    
    // 1. Detecta credenciais básicas da página atual
    const { token: detectedToken, email: detectedEmail } = apiDetectAuth(); 
    
    // --- 2. TENTAR CARREGAR DO STORAGE (Retrocompatibilidade) ---
    const storedApiFixed = handleContextStorage(null, 'API_FIXED');
    const storedCategories = handleContextStorage(null, 'CATEGORIES');
    
    // Verificamos se temos dados salvos e se o e-mail ainda é o mesmo do login atual
    if (storedApiFixed && storedCategories && storedApiFixed.token) { 
        contextToLoad = {
            ...storedApiFixed,
            ...handleContextStorage(null, 'PAYMENT'), 
            ...storedCategories,
            isLoaded: true,
            fromStorage: true 
        };
        
        // Se o e-mail detectado agora for diferente do salvo, limpamos o contexto (troca de conta)
        if (detectedEmail && contextToLoad.email !== detectedEmail) {
            console.log("🔄 Troca de usuário detectada, ignorando cache...");
            contextToLoad = null;
        } else {
           // flashMessage(panelRoot, "✅ Contexto VFS recuperado do cache.");
            contextSuccess = true;
        }
    }

    // --- 3. SE NÃO HOUVER CACHE VÁLIDO, CHAMA A API/SCRAPING ---
    if (!contextSuccess) {
        try {
          //  apiLog("Buscando novo contexto VFS na página...");
            const contextFromApi = await loadVfsContext(); 
            
            console.log("DEBUG Contexto:", contextFromApi);
            console.log("DEBUG Token:", detectedToken);
            console.log("DEBUG Token:", detectedEmail);
            if (contextFromApi && (contextFromApi.token || contextFromApi.jwtToken || detectedToken)) { 
                contextToLoad = { 
                    ...contextFromApi, 
                    // Normalização: garante que sempre exista a propriedade 'token'
                    token: contextFromApi.token || contextFromApi.jwtToken || detectedToken, 
                    email: detectedEmail || contextFromApi.email || contextFromApi.loginUser, 
                    isLoaded: true 
                };
                
                // SALVAMENTO IMEDIATO (Fragmentado e Unificado)
                handleContextStorage(contextToLoad); 
                sessionStorage.setItem(CONTEXT_STORAGE_KEY, JSON.stringify(contextToLoad)); 
                
                contextSuccess = true;
            } else {
                flashMessage(panelRoot, "⚠️ VFS: Faça login para carregar o contexto.");
            }
        } catch (e) {
            console.error("❌ Erro ao carregar contexto VFS:", e);
            flashMessage(panelRoot, "❌ Falha ao extrair dados da VFS.");
        }
    }
    
    // --- 4. DISTRIBUIÇÃO E ATUALIZAÇÃO DA UI ---
    if (contextToLoad && contextSuccess) {
        // Garante que o objeto global seja preenchido
        window.globalVfsContext = contextToLoad; 
        
        // Atualiza elementos visuais do painel (como o e-mail no topo)
        updatePanelContext(contextToLoad); 
        
        const settingsTab = panelRoot.querySelector("#settings");
        if (settingsTab) {
            updateSettingsContext(settingsTab);
        }
    }
    
    // --- 5. AVISO DE CONCLUSÃO ---
    // Importante para habilitar os botões do seu painel
    if (window.contextLoadedCallback) {
        window.contextLoadedCallback(contextSuccess);
    }
}

class PainelManagerContínuo {
    constructor() {
        this.monitores = new Map();
        this.observer = null;
        this.globalAutomationRunning = false; 
    }
    
    // MÉTODO 'adicionarMonitor' que faltava na definição
    adicionarMonitor(seletor, funcoes) {
        if (this.monitores.has(seletor)) {
            console.error(`Monitor para ${seletor} já existe.`);
            return;
        }

        this.monitores.set(seletor, {
            ativo: false,
            ativar: funcoes.ativar,
            desativar: funcoes.desativar,
        });
    }

    // A lógica crucial de checagem contínua
    handleMutation = function handleMutation(mutationsList, observer) {
        this.monitores.forEach((monitor, seletor) => {
            const elemento = document.querySelector(seletor);
            const elementoExiste = !!elemento;

            // Lógica de Ativação
            if (elementoExiste && !monitor.ativo) {
                if (seletor === 'app-dashboard' && this.globalAutomationRunning) {
                    return; 
                }

                monitor.ativar();
                monitor.ativo = true;
                console.log(`✅ [${seletor}] ATIVADO.`);
            } 
            // Lógica de Fechamento (Logout/Desaparecimento)
            else if (!elementoExiste && monitor.ativo) {
                monitor.desativar();
                monitor.ativo = false;
                console.log(`❌ [${seletor}] DESATIVADO (Logout/Desaparecimento).`);

                if (seletor === 'app-dashboard') {
                    this.globalAutomationRunning = false; 
                    console.log(`   -> [app-dashboard] globalAutomationRunning resetado.`);
                }
            }
        });
    }
    
    // Método 'iniciarMonitoramento' que faltava na definição
    iniciarMonitoramento() {
        if (this.monitores.size === 0) {
            console.warn("Nenhum painel configurado para monitoramento.");
            return;
        }

        const targetNode = document.body;
        const config = { childList: true, subtree: true };

        this.observer = new MutationObserver(this.handleMutation.bind(this));
        this.observer.observe(targetNode, config);
        console.log("Observer Global UNIFICADO (Contínuo) iniciado.");
        
        // Chamada inicial
        this.handleMutation([], this.observer); 
    }
}


// ==========================================================
// 2. FUNÇÃO PRINCIPAL DE INICIALIZAÇÃO (SEU CÓDIGO)
// ==========================================================

function iniciarGerenciamentoDePainel() {
    console.log("Iniciando Bot VFS e Gerenciador de Painéis...");
    
    // O delay será de 15 segundos (15000 milissegundos)
    const DELAY_MS = 3; 

    // Agora o construtor PainelManagerContínuo É CONHECIDO
    const painelGlobalManager = new PainelManagerContínuo(); 
    
    // ------------------- MONITOR 1: PAINEL DE LOGIN -------------------
    painelGlobalManager.adicionarMonitor( 
        'h1.fs-21.fs-sm-24.mb-10', 
        {
            ativar: () => { 
                // Delay de 15 segundos para injetar o painel
                setTimeout(() => {
                    injectPanel();
                    injectOpenButton()
                    console.log('-> LOGIN PANEL: Mostrar...');
                }, DELAY_MS);
            },
            desativar: () => { 
                // 🛑 CHAMADA À FUNÇÃO DE REMOÇÃO
                removeLoginPanel();
                removeVfsOpenPanelButton();
                console.log('-> LOGIN PANEL: Painel desativado e removido.');
            }
        }
    );


    
    // ------------------- MONITOR 2: PAINEL DASHBOARD -------------------
    painelGlobalManager.adicionarMonitor( // O método existe aqui!
        'app-dashboard', 
        {
            ativar: async () => {
                const panelRoot = document.getElementById("vfs_quickbot_panel");
                
                if (painelGlobalManager.globalAutomationRunning) {
                    console.log('-> DASHBOARD: Já rodando, apenas mantenha o painel ativo.');
                    return;
                }

                if (panelRoot) {
                    console.log("   -> Painel Aberto. Atualizando UI...");
                    await renderAll(panelRoot);
                    await iniciarBookingZone(panelRoot);
                    iniciarPainelAoAbrir()
                } else {
                    console.log("   -> Painel Fechado. Iniciando Painel...");
                    await iniciarPainel();
                    showOpenButton();
                }

                console.log("   -> UI Pronta. Ativando Agendamento Automático.");
                painelGlobalManager.globalAutomationRunning = true; 
            },
            desativar: () => {
                console.log('-> DASHBOARD PANEL: Logout detectado. Fechando/Resetando...');
                showOpenButton();
            }
        }
    );

    // Inicia o monitoramento de AMBOS os painéis (Login e Dashboard)
    painelGlobalManager.iniciarMonitoramento();
    
    // Lógica de Fallback de Injeção Síncrona
   const hostname = window.location.hostname;
const pathname = window.location.pathname;

// 🛑 CONDIÇÃO 1: Excluir Domínios de Autenticação Externos
if (hostname.includes('idnvui.vfsglobal.com')) {
    console.log("Página de Autenticação externa detectada. Ocultando botão flutuante.");
    return;
}

// 🛑 CONDIÇÃO 2: Excluir Página de Login Interna
if (pathname.includes('/login')) {
    console.log("Página de Login interna detectada. Ocultando botão flutuante.");
    return;
}


// Lógica para mostrar o botão em páginas específicas ou quando o contexto é desconhecido
if (pathname.includes('/application-detail') || pathname === '/') {
    // NOTA: Se o '/' for a página de login, esta lógica pode precisar de ser revista.
    // Se a página de login for APENAS '/', a Condição 2 acima (se usar pathname === '/login') 
    // não funcionará para o caso '/'.
    
    // Se a página inicial (root '/') for onde o contexto de login ainda não está pronto, 
    // e o seu código NÃO estiver a lidar com isso acima, mantenha:
    console.log("Página de Detalhes/Root detectada. Mostrando botão flutuante.");
    showOpenButton();
    removeVfsOpenPanelButton()
} else if (!document.querySelector('app-dashboard')) {
    console.log("Contexto desconhecido (fora do Dashboard). Mostrando botão flutuante.");
    showOpenButton();
    removeVfsOpenPanelButton()
} else {
    console.log("No Dashboard. Botão flutuante oculto por padrão.");
}
}

document.addEventListener("DOMContentLoaded", iniciarGerenciamentoDePainel);
    // ==========================================================
    // 2. FUNÇÕES DE STORAGE (localStorage)
    // =========================================================





/**
 * Salva a lista de candidatos (processos) no armazenamento local da extensão.
 * @param {Array<Object>} list - A lista de candidatos a ser salva.
 * @returns {Promise<void>} Uma Promessa que resolve quando o salvamento é concluído.
 */
async function saveApplicants(list) {
    try {
        // Objeto que será salvo: { 'STORAGE_KEY': list }
        const dataToStore = { [STORAGE_KEY]: list };
        
        // Usa chrome.storage.local.set() - é assíncrono (retorna uma Promise)
        await chrome.storage.local.set(dataToStore);
        
        console.log("Candidatos salvos com sucesso no storage local da extensão.");
    } catch (err) {
        // A API chrome.storage.local.set() geralmente não falha desta forma, 
        // mas é bom manter o try/catch ou o tratamento de erro da Promise.
        console.error("Erro ao salvar candidatos no storage da extensão:", err);
    }
}

/**
 * Carrega a lista de candidatos (processos) do armazenamento local da extensão.
 * @returns {Promise<Array<Object>>} Uma Promessa que resolve com a lista de candidatos ou um array vazio.
 */
async function loadApplicants() {
    // Certifique-se de que a constante STORAGE_KEY está definida no seu escopo.
    // Ex: const STORAGE_KEY = "vfs_applicants_v2";
    
    try {
        // chrome.storage.local.get() retorna um objeto como { [STORAGE_KEY]: [...] }
        const result = await chrome.storage.local.get(STORAGE_KEY);
        
        // Retorna a lista salva ou, se for undefined/null, retorna um array vazio.
        return result[STORAGE_KEY] || [];
        
    } catch (error) {
        // Captura o erro, exibe-o no console para debug, mas não quebra a aplicação.
        // É aqui que a sua mensagem de erro aparece.
        console.error("Erro ao carregar dados do Chrome Storage:", error); 
        
        // CRUCIAL: Retorna uma lista vazia para que o restante do código continue a funcionar.
        return [];
    }
}

// 1. Variável de controle fora da função para persistir entre chamadas
let isVfsFetchingActive = false;

async function fetchVfsContextInBrowser(token, email, currentVACCode) {
    // 2. Trava de Segurança: Se já estiver carregando, aborta a nova chamada
    if (isVfsFetchingActive) {
        console.warn("⚠️ [VFS-BOT] Carga já em curso. Ignorando chamada duplicada.");
        return globalVfsContext; 
    }

    // Ativa o bloqueio
    isVfsFetchingActive = true;

    try {
        // Identificação dinâmica
        const pathParts = window.location.pathname.split('/').filter(p => p.length > 0);
        const country = (pathParts[0] || 'AGO').toUpperCase();
        const lang = pathParts[1] || 'en';
        const mission = (pathParts[2] || 'bra').toLowerCase();
        const apiLang = lang === 'pt' ? 'pt-PT' : 'en-US';

        const BASE_API = "https://lift-api.vfsglobal.com";
        let headers = { 
            'Authorization': `Bearer ${token}`, 
            'Accept': 'application/json',
            'clientsource': '' 
        };

        let context = { email, primaryCategories: [] };

        // --- ETAPA 1: CENTERS ---
        const urlCenters = `${BASE_API}/master/center/${mission}/${country}/${apiLang}`;
        console.log("🚀 [FETCH] Iniciando API 1 - Centers");

        const centersData = await fetchDataInjected(urlCenters, "CENTERS", headers, 'GET', null);
        
        if (!centersData || !Array.isArray(centersData)) throw new Error("API Centers retornou dados inválidos.");

        const center = centersData.find(c => c.isoCode === currentVACCode) || centersData[0];

        // Atribuição dos objetos
        context.officeId = center.id;
        context.officeName = center.centerName;
        context.vacCode = center.isoCode;
        context.missionCode = (center.missionCode || mission).toLowerCase();
        context.countryCode = center.countryCode || country;
        context.cultureCode = center.cultureCode;
        // --- ETAPA 2: CONFIG FIELDS ---
        const urlConfig = `${BASE_API}/configuration/fields/${context.missionCode}/${context.countryCode}/${context.vacCode}`;
        
        if (!urlConfig.includes("null") && !urlConfig.includes("undefined")) {
            console.log("🚀 [FETCH] Iniciando API 2 - Config");
            await fetchDataInjected(urlConfig, "CONFIG", headers, 'GET', null);
        }

    const urlCategories = `${BASE_API}/master/visacategory/${context.missionCode}/${context.countryCode}/${context.vacCode}/${apiLang}`;
      console.log("🚀 [FETCH] Iniciando API 3 - Categories");

const categoriesData = await fetchDataInjected(urlCategories, "CATEGORIES", headers, 'GET', null);

if (Array.isArray(categoriesData)) {
    context.primaryCategories = categoriesData.map(c => ({ 
        id: c.id, 
        name: c.name, 
        code: c.code,
        cultureCode: c.culturecode // ⬅️ Adicionado aqui (conforme o JSON oficial: culturecode em minúsculas)
    }));
        }

        globalVfsContext = context;
        console.log("🏆 [SUCCESS] Contexto carregado:", context.officeName);
        return context;

    } catch (e) {
        console.error("🛑 [ERROR] Falha no carregamento:", e.message);
        // Opcional: limpa o contexto global em caso de erro crítico
        globalVfsContext = null;
        return null;
    } finally {
        // 3. LIBERAÇÃO DA TRAVA: Aconteça o que acontecer, permite carregar de novo após o fim
        isVfsFetchingActive = false;
        console.log("🔓 [VFS-BOT] Fluxo finalizado. Trava liberada.");
    }
}

async function fetchAllSubcategories(token, missionCode, countryCode, vacCode, primaryCategories, clientsourceToken) {
    // 1. Identificação de Idioma nativa
    const lang = window.location.pathname.split('/')[2] === 'pt' ? 'pt-PT' : 'en-US';
    const BASE_API = "https://lift-api.vfsglobal.com";
    
    let allSubcategories = [];
    const headers = { 
        'Authorization': `Bearer ${token}`,
        'clientsource': clientsourceToken || '' 
    };

    for (const category of primaryCategories) {
        const catCode = category.code;
        
        // 2. CONSTRUÇÃO DINÂMICA MANUAL (Substituindo o uso de urls.GET_SUBCATEGORIES)
        const url = `${BASE_API}/master/subvisacategory/${missionCode.toLowerCase()}/${countryCode.toUpperCase()}/${vacCode}/${catCode}/${lang}`;
        
        try {
            const data = await fetchDataInjected(url, `SUBCATEGORIES: ${catCode}`, headers, 'GET', null);
            
            if (Array.isArray(data)) {
                const mapped = data.map(sc => ({ 
                    id: sc.id, 
                    name: sc.name, 
                    primaryCode: catCode, 
                    code: sc.code 
                }));
                allSubcategories.push(...mapped);
            }
        } catch (e) {
            console.error(`❌ Falha na subcategoria ${catCode}:`, e.message);
            // Continua para a próxima categoria mesmo se uma falhar
        }
    }
    return allSubcategories;
}

async function fetchAndSetPaymentMode(token, subCategoryCodeToUse) {
    // 1. Validação de segurança imediata
    if (!subCategoryCodeToUse || subCategoryCodeToUse === 'PLLS') {
        throw new Error(`❌ Código inválido para pagamento: ${subCategoryCodeToUse}`);
    }

    // 2. Extração dinâmica do contexto da URL da página
    const pathParts = window.location.pathname.split('/').filter(p => p.length > 0);
    const country = pathParts[0].toUpperCase();
    const mission = pathParts[2].toLowerCase();
    
    // O vacCode buscamos do contexto global já salvo anteriormente
    const vac = globalVfsContext?.vacCode;

    if (!vac) throw new Error("VAC Code não encontrado no contexto global.");

    const BASE_API = "https://lift-api.vfsglobal.com";
    const headers = { 'Authorization': `Bearer ${token}`, 'clientsource': '' };
    
    // 3. CONSTRUÇÃO DINÂMICA DA URL
    const url = `${BASE_API}/master/paymentmode/${mission}/${country}/${vac}?subVisaCode=${subCategoryCodeToUse}&culturCode=en-US`;

    try {
        const paymentModesData = await fetchDataInjected(url, "PAYMENT MODE", headers, 'GET', null);
        
        if (!paymentModesData || paymentModesData.length === 0) {
            return { paymentTypeId: null, paymentName: 'N/A' };
        }
        
        const selected = paymentModesData[0]; 
        return {
            paymentTypeId: selected.code, 
            paymentName: selected.name
        };

    } catch (e) {
        console.error("❌ Erro ao carregar Pagamento:", e.message);
        return { paymentTypeId: null, paymentName: 'N/A' };
    }
}

  /**
 * Funcao que simula a submissao do token Turnstile para obter o cf_clearance.
 * Usa fetch com 'credentials: include' para garantir que o cookie cf_clearance
 * seja definido no armazenamento de cookies do navegador.
 * * @param {string} secondaryToken - O token obtido do seu CAPTCHA solver.
 * @returns {Promise<boolean>} Retorna true se o POST para a CF for bem-sucedido.
 */
async function bypassCloudflareTurnstile(secondaryToken) {
    apiLog("🔒 Tentando bypass Cloudflare (Turnstile)...", false);

    if (!secondaryToken || secondaryToken.length < 50) {
         apiLog("❌ Token Vazio ou Inválido recebido do Solver.", false);
         return false;
    }

    const payload = JSON.stringify({
        sitekey: CF_TURNSTILE_SITEKEY,
        secondaryToken: secondaryToken
    });

    try {
        const response = await fetch(CF_TURNSTILE_URL_VALIDACAO, {
            method: 'POST',
            headers: CF_HEADERS_POST,
            body: payload,
            credentials: 'include' // FUNDAMENTAL: Garante que a CF defina o cookie no navegador
        });

        // O POST de sucesso da CF é geralmente 200/202, mas o importante é a definição do cookie
        if (response.ok || response.status === 200 || response.status === 202 || response.status === 302) {
           // apiLog("✅ Sucesso. O cookie 'cf_clearance' deve ter sido definido.", false);
            // Neste ponto, o cookie está no navegador e será enviado em todas as chamadas 'fetch'
            return true; 
        } else {
            apiLog(`❌ Falha no POST Turnstile. Status: ${response.status}.`, false);
            return false;
        }

    } catch (e) {
        apiLog(`❌ Erro de Rede no Bypass CF: ${e.message}.`, false);
        return false;
    }
}

/**
 * Calcula o primeiro dia do mês seguinte no formato YYYY-MM-DD.
 * @returns {string} Data no formato YYYY-MM-DD.
 */
function getNextMonthFirstDay() {
    const today = new Date();
    
    // Obtém o primeiro dia do MÊS SEGUINTE.
    const nextMonth = today.getMonth() + 1;
    const nextDate = new Date(today.getFullYear(), nextMonth, 1);

    const day = String(nextDate.getDate()).padStart(2, '0');
    // nextDate.getMonth() retorna o mês correto (ex: 0 para Janeiro), 
    // então precisamos de +1 para humanos, mas o date object já resolveu isso.
    // Usamos month + 1 para o formato YYYY-MM-DD
    const month = String(nextDate.getMonth() + 1).padStart(2, '0'); 
    const year = nextDate.getFullYear();

    return `${year}-${month}-${day}`;
}

/**
 * Atualiza o campo 'fromDate' no payload.
 * 1. Tenta usar a data do input manual (panel_fromDate).
 * 2. Se vazio, usa o dia 1 do mês atual.
 */
function atualizarFromDateEPayload(currentPayload) {
    let fromDate;
    
    // 1. Tenta obter a data do campo de input manual no painel
    const dateInput = document.getElementById('panel_fromDate');
    
    if (dateInput && dateInput.value) {
        // Formato do input (YYYY-MM-DD) para (DD/MM/YYYY)
        const [year, month, day] = dateInput.value.split('-');
        fromDate = `${day}/${month}/${year}`;
        console.log(`[Calendar] 🗓️ Usando data manual do painel: ${fromDate}`);
        
    } else {
        // 2. Se o painel estiver vazio, usa SEMPRE o dia 1 do MÊS ATUAL
        const today = new Date();
        const day = "01";
        const month = String(today.getMonth() + 1).padStart(2, '0');
        const year = today.getFullYear();

        fromDate = `${day}/${month}/${year}`;
        console.log(`[Calendar] 🗓️ Painel vazio. Usando dia 1 do mês atual: ${fromDate}`);
    }

    // --- Atualização do Payload ---
    let payloadObject;
    try {
        payloadObject = (typeof currentPayload === 'string') ? JSON.parse(currentPayload) : currentPayload;
    } catch (e) {
        payloadObject = currentPayload;
    }

    payloadObject.fromDate = fromDate;

    return JSON.stringify(payloadObject);
}


// --- UTILS: Função de Formatação de Data para Fala (D/M/Y) ---
// --- UTILS: Função de Formatação de Data para Fala (MM/DD/AAAA) ---
function formatDateToSpeech(dateString) {
    if (!dateString) return 'data não definida';
    
    // Extrai a parte M/D/YYYY (Assume formato Americano MM/DD/YYYY)
    const datePart = dateString.split(' ')[0];
    const parts = datePart.split('/'); 

    if (parts.length !== 3) return dateString;

    // 🔑 CORREÇÃO CRÍTICA: Inverte a ordem das partes
    const [month, day, year] = parts; // Agora Month é parts[0] e Day é parts[1]
    
    const monthNames = [
        "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho",
        "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"
    ];
    
    const monthIndex = parseInt(month, 10) - 1; // Mês (1-12) -> Índice (0-11)
    
    // Garante que o índice do mês é válido
    const monthName = (monthIndex >= 0 && monthIndex < 12) ? monthNames[monthIndex] : "mês inválido";
    
    // Formato final: "dia <numeral> de <mês por extenso> de <ano numeral>"
    // Usamos o 'day' extraído corretamente (parts[1])
    return `dia ${parseInt(day, 10)} de ${monthName} de ${year}`;
}

// --- UTILS: Função de Síntese de Fala (TTS) ---
function playAudioMessage(message) {
    // ... (A função playAudioMessage permanece a mesma) ...
}

// --- FUNÇÃO PRINCIPAL: Checagem de Vagas (API 5) ---
async function checkAvailableSlots(applicantData) {
    // ... (O restante da função checkAvailableSlots permanece o mesmo,
    //      pois ela apenas chama formatDateToSpeech com a string original) ...
    
    // 🔑 PONTO DE ATIVAÇÃO: DENTRO DE VAGAS ABERTAS
    if (data.earliestSlotLists && data.earliestSlotLists.length > 0) {
        const slotInfo = data.earliestSlotLists[0];
        
        const slotId = slotInfo.date; 
        // A função corrigida formatDateToSpeech agora lida com MM/DD/AAAA
        const spokenDate = formatDateToSpeech(slotId); 
        
        // ... (restante da lógica de log e áudio) ...
    }
    
    // ...
}

// --- UTILS: Função de Síntese de Fala (TTS) ---
function playAudioMessage(message) {
    if ('speechSynthesis' in window) {
        // Cancela fala anterior para garantir clareza
        window.speechSynthesis.cancel(); 
        
        const utterance = new SpeechSynthesisUtterance(message);
        
        // Configurações para clareza e tom de alerta
        utterance.lang = 'pt-PT'; // Português de Portugal para maior clareza
        utterance.rate = 1.0; 
        utterance.pitch = 1.1; // Ligeiramente mais alto para tom de alerta

        // Tenta encontrar uma voz em Português
        const voices = window.speechSynthesis.getVoices();
        const ptVoice = voices.find(voice => voice.lang === 'pt-PT' || voice.lang === 'pt-BR');
        
        if (ptVoice) {
            utterance.voice = ptVoice;
        }

        window.speechSynthesis.speak(utterance);
    } else {
        console.warn('API de Síntese de Fala não suportada neste navegador.');
    }
}

function stopContinuousAlert() {
    if (globalContinuousAlertTimeout) {
        clearTimeout(globalContinuousAlertTimeout);
    }
    // Apenas cancela a fala se houver algo falando
    if (window.speechSynthesis.speaking) {
         window.speechSynthesis.cancel();
    }
    globalContinuousAlertRunning = false;
    globalContinuousAlertMessage = '';
  //  apiLog("🔊 Alerta de áudio contínuo PARADO.", false);
}

function startContinuousAlert(message, repeatDelayMs = 5000) {
    // Evita iniciar um novo loop se já estiver rodando o mesmo alerta
    if (globalContinuousAlertRunning && globalContinuousAlertMessage === message) {
        return; 
    }
    
    stopContinuousAlert(); // Para qualquer alerta antigo
    globalContinuousAlertRunning = true;
    globalContinuousAlertMessage = message;
    
  //  apiLog("🔊 Iniciando Alerta de Áudio contínuo (loop de 5s + fala).", true);

    const speakAndRepeat = () => {
        // Verifica se a automação foi parada
        if (!globalContinuousAlertRunning) return; 
        
        const utterance = new SpeechSynthesisUtterance(globalContinuousAlertMessage);
        
        // Configurações de Voz para o Alerta
        utterance.lang = 'pt-PT'; 
        utterance.rate = 1.0; 
        utterance.pitch = 1.1; 
        
        // Re-obter voz
        const voices = window.speechSynthesis.getVoices();
        const ptVoice = voices.find(voice => voice.lang === 'pt-PT' || voice.lang === 'pt-BR');
        if (ptVoice) {
            utterance.voice = ptVoice;
        }
        
        // Define o callback para agendar a repetição
        utterance.onend = () => {
            // Agenda a próxima chamada 5 segundos após a fala terminar (Requisito do usuário)
            globalContinuousAlertTimeout = setTimeout(speakAndRepeat, repeatDelayMs); 
        };
        
        // Necessário limpar o timeout antes de falar para evitar conflito se houver cancelamento
        if (globalContinuousAlertTimeout) clearTimeout(globalContinuousAlertTimeout);

        window.speechSynthesis.speak(utterance);
    };
    
    // Inicia o primeiro ciclo
    speakAndRepeat();
}
/**
 * Checa a disponibilidade de vagas (API 5 - Slot Check).
 * Lida com slots abertos, fechados (código 1035) e erros.
 * @param {Object} applicantData Dados do requerente (subCategoryCode, paymentTypeId, visaSubCategoryName).
 * @param {Object} globalVfsContext Contexto global (token, codes, clientsourceToken, email).
 * @returns {Promise<Object>} Resultado da checagem { status: "OPEN"|"CLOSED"|"ERROR", slotId, message, ... }
 */
async function checkAvailableSlots(applicantData, globalVfsContext) {
   const VFS_SLOT_CHECK_URL = 'https://lift-api.vfsglobal.com/appointment/CheckIsSlotAvailable'; 
    const context = globalVfsContext
    const jwtToken = globalVfsContext.jwtToken; 
  const fullVisaName = applicantData?.visaSubCategoryName || "Requerente Desconhecido";

// Divide no '(' e limpa os espaços vazios nas pontas
const visaName = fullVisaName.split('(')[0].trim();
   
    // Dados para o corpo da requisição (payload)
    const visaCode = applicantData?.subCategoryCode; // Ex: LONG
    const payCode = applicantData?.paymentTypeId;    // Ex: EMIS
    const applicantName = applicantData?.visaSubCategoryName || "Requerente Desconhecido";

    // --- 1. PRÉ-VALIDAÇÃO ---
    if (!context?.jwtToken || !visaCode || !payCode) {
        apiLog("❌ Dados de contexto (token) ou requerente (visa/pagamento) incompletos para a API 5.", false);
       return { success: false, status: "ERROR", message: "Dados incompletos antes da API 5." };
    }
    
    let data = null; // Para armazenar o JSON de sucesso
    
    try {
        // --- 2. PREPARAÇÃO DA REQUISIÇÃO ---
        const requestBody = JSON.stringify({ 
            countryCode: context.countryCode, 
            missionCode: context.missionCode, 
            vacCode: context.vacCode, 
            visaCategoryCode: visaCode, 
            roleName: "Individual", 
            loginUser: context.email, 
            payCode: payCode 
        });
        
        const headers = { 
            'authorize': `${jwtToken}`, 
            'Content-Type': 'application/json;charset=UTF-8', 
            'Referer': 'https://visa.vfsglobal.com/',
            'Accept': 'application/json, text/plain, */*',
            'clientsource': '',
            'route': 'AGO/en/prt'
        };
        
       // apiLog(`DEBUG: Enviando API 5: ${VFS_SLOT_CHECK_URL}`, false);
       // apiLog(`DEBUG: Payload API 5: ${requestBody}`, false);
        
        // --- 3. CHAMADA API ---
        const stepName = "SLOT_CHECK";
        
        // fetchDataInjected deve retornar o JSON (data) se o status for 2xx, 
        // e lançar um erro (catch) em caso de falha HTTP (4xx/5xx) ou de rede.
        data = await fetchDataInjected( 
            VFS_SLOT_CHECK_URL, 
            stepName, 
            headers, 
            'POST', 
            requestBody 
        );
        
        // --- 4. TRATAMENTO DA RESPOSTA JSON (DATA) ---

   if (data && data.error && data.error.code === 1035) {
    const logMsg = `[${visaName}] Sem vagas disponíveis .`;
    apiLog(logMsg, false);
    playAudioMessage(`${visaName} fechado. `);

    // --- REPORTAR AO PAINEL (FECHADO) ---
    reportarVagasAoPainel("CLOSED", {
        pais: "Brasil",
        categoria: visaName,
        detalhes_vaga: {
            categoria: visaName,
            detalhes: "We are sorry but no appointment slots are currently available. New slots open at regular intervals, please try again later",
            modo: "MONITOR_API",
            data: new Date().toLocaleString('pt-PT')
        }
    });

    return {
        status: "CLOSED",
        success: true,
        availableSlotsCount: 0,
        message: logMsg,
        audioMessage: "Fechado"
    };
}

if (data.earliestSlotLists && data.earliestSlotLists.length > 0) {
    const slotInfo = data.earliestSlotLists[0];
    const slotId = slotInfo.date; 
    const spokenDate = formatDateToSpeech(slotId);

    const applicantsString = slotInfo.applicant || "";
    const numApplicants = applicantsString.split(',').length;
    const logMsg = 
        `✅ [${visaName}] VAGAS ABERTAS! ${spokenDate}.` +
        ` Slots para ${numApplicants} requerentes.`;
        
    apiLog(logMsg, true);
    playAudioMessage(`Brasil ABERTo!  ${visaName} ${spokenDate} Para  ${numApplicants} applicantes. .`);

    // --- REPORTAR AO PAINEL (ABERTO) ---
    const dataPura = slotId.split(' ')[0].replace(/\//g, '-'); // Formato DD-MM-YYYY para o PHP
    
    reportarVagasAoPainel("OPEN", {
        pais: "Brasil",
        categoria: visaName,
        detalhes_vaga: {
            categoria: visaName,
            detalhes: `Earliest available slot for ${numApplicants} Applicants is : ${dataPura}`,
            modo: "MONITOR_API",
            data: new Date().toLocaleString('pt-PT')
        }
    });

    return {
        status: "OPEN",
        success: true,                     
        availableSlotsCount: numApplicants, 
        slotId: slotId,
        message: logMsg,
        audioMessage: "Aberto",
        numApplicants: numApplicants 
    };
}
        
        // 🔑 4.3. OUTROS ERROS OU FORMATO DESCONHECIDO
        apiLog(`❌ API 5: Formato inesperado ou erro não mapeado: ${JSON.stringify(data)}`, true);
        
        startContinuousAlert(`Erro na checagem de vagas.`); 
        
       return { success: false, status: "ERROR", message: "Erro de formato inesperado na API 5.", audioMessage: "Erro" };


    } catch (e) {
        // --- 5. TRATAMENTO DE ERROS FATAIS (REDE OU HTTP/PARSE REJEITADO) ---
        apiLog(`❌ Erro FATAL na API de Checagem de Vagas (API 5): ${e.message}`, true);
        
        playAudioMessage(`Erro de rede ou bloqueio.`);
        
        return { success: false, status: "ERROR", message: `Erro de rede/HTTP na API 5: ${e.message}`, audioMessage: "Erro de rede" };
    }
}

async function reportarVagasAoPainel(status, payloadCompleto) {
    try {
        await fetch('https://advence.com.ao/api/vagas.php', {
            method: 'POST',
            mode: 'cors',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payloadCompleto) // Enviamos o objeto inteiro que montamos acima
        });
        console.log(`📡 [API EXTERNA] Reportado como ${status}`);
    } catch (e) {
        console.error("❌ Erro ao enviar para advence.com.ao:", e);
    }
}

async function createApplicantSimple(applicantData, globalVfsContext, slotCheckResult) {
    const VFS_BASE_URL = "https://visa.vfsglobal.com";
    const VFS_APPLICANT_CREATE_URL = "https://lift-api.vfsglobal.com/appointment/applicants"; 
  
    const context = globalVfsContext;
    const jwtToken = globalVfsContext.jwtToken; 
    const visaCode = applicantData?.subCategoryCode || "03";
    const IP = globalVfsContext.IP || "105.168.219.125";
    
    if (!jwtToken || !context.missionCode || !slotCheckResult || !slotCheckResult.slotId) {
        apiLog(`❌ Erro em createApplicantSimple: JWT Token ou Slot ID ausente.`, true);
        return { success: false, error: { code: 'FALHA_CONTEXTO_CRITICO' } };
    }

    const loginEmail = context.email;

    const convertDate = (isoDate) => {
        if (!isoDate || typeof isoDate !== 'string') return null;
        const parts = isoDate.split('-');
        return parts.length === 3 ? `${parts[2]}/${parts[1]}/${parts[0]}` : isoDate;
    };

    const formattedDOB = convertDate(applicantData.dateOfBirth);
    const formattedExpiry = convertDate(applicantData.passportExpiryDate);

    // 1. OBJETO DO APLICANTE (Fiel ao Log do Site)
    const applicantPayload = {
        urn: "", 
        arn: "", 
        centerClassCode: null, 
        selectedSubvisaCategory: null, 
        Subclasscode: null,
        VisaToken:null,
        AdditionalRefNo: null,
        OfflineCClink: "",
        PVCanAllowRetry: true,
        PVRequestRefNumber: "",
        PVStatus: "",
        PVStatusDescription: "",
        PVisVerified: false,
        Retryleft: "",
        SpecialAssistance: "",
        addressline1: null,
        addressline2: null,
        applicantGroupId: 0,
        applicantType: 0,
        canDeleteVAF: false,
        canDownloadVAF: false,
        canEditVAF: false,
        canInitiateVAF: false,
        city: null,
        confirmPassportNumber: null,
        contactNumber: applicantData.contactNumber, 
        dateOfApplication: null,
        dateOfBirth: formattedDOB,
        dateOfDeparture: null,
        dialCode: applicantData.contactCode,
        eefRegistrationNumber: "",
        emailId: applicantData.email.toUpperCase(),
        employerContactNumber: "",
        employerDialCode: "",
        employerEmailId: "",
        employerFirstName: "",
        employerLastName: "",
        entryType: "",
        eoiVisaType: "",
        familyReunificationCerificateNumber: "",
        firstName: applicantData.firstName.toUpperCase(),
        gender: (applicantData.gender === "Male" || applicantData.gender === 1) ? 1 : 2,
        helloVerifyNumber: "",
        idenfystatuscheck: false,
        ipAddress: IP,
        isAutoRefresh: true,
        isEndorsedChild: false,
        juridictionCode: "",
        lastName: applicantData.lastName.toUpperCase(),
        loginUser: loginEmail,
        middleName: "",
        nationalId: null,
        nationalityCode: "AGO",
        parentPassportExpiry: "",
        parentPassportNumber: "",
        passportExpirtyDate: formattedExpiry,
        passportNumber: applicantData.passportNumber,
        passportType: "",
        pincode: null,
        referenceNumber: null,
        salutation: "",
        state: null,
        vafStatus: null,
        vfsReferenceNumber: "",
        vlnNumber: null
    };

    // 2. CORPO DA REQUISIÇÃO (Ordem e Campos Idênticos ao Site)
    const createBodyObj = {
        countryCode: "AGO",
        missionCode: "bra",
        centerCode: "LUA",
        loginUser: loginEmail,
        applicantList: [applicantPayload],
        centerCode: "LUA",
        countryCode: "AGO",
        feeEntryTypeCode: null,
        feeExemptionDetailsCode: null,
        feeExemptionTypeCode: null,
        isEdit: false,
        isWaitlist: false,
        juridictionCode: null,
        languageCode: "en-US",
        loginUser: loginEmail,
        missionCode: "bra",
        regionCode: null,
        visaCategoryCode: visaCode
    };

    const createBody = JSON.stringify(createBodyObj);

    // 3. HEADERS (Corrigidos para Missão Brasil)
    const headers = { 
        "authorize": `${jwtToken}`,
        "Content-Type": "application/json;charset=UTF-8",
        "accept": "application/json, text/plain, */*",
        "origin": VFS_BASE_URL,
        "referer": VFS_BASE_URL + "/appointment/select-vac",
        "Route" : "AGO/en/bra", // Rota correta para Angola/Brasil
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
    };

    try {
        const response = await fetchDataInjected(VFS_APPLICANT_CREATE_URL, "CREATE_APPLICANT", headers, 'POST', createBody);
        
        const actualArn = response.urn || (response.applicantList && response.applicantList[0] ? response.applicantList[0].arn : undefined);

        if (response.error && response.error.code) {
            const vfsCode = String(response.error.code);
            apiLog(`❌ Erro VFS: ${response.error.description}`, true);
            return { success: false, error: { code: vfsCode }, urn: actualArn || null }; 
        }

        if (actualArn) {
            apiLog(`✅${applicantData.firstName}   ${applicantData.passportNumber} Requerente criado com sucesso  ${actualArn}.`, false);
            return { ...response, urn: actualArn, success: true }; 
        } 
        
        return { success: false, error: { code: 'PARSE_FAIL' } };

    } catch (e) {
        const status = e.status || 0;
        apiLog(`❌ ERRO HTTP ${status}: ${e.message}`, true);
        return { success: false, error: { code: `HTTP_${status}` } };
    }
}


async function getFee(VFS_BASE_URL,currentContext) {
    const VFS_GET_FEE_URL ="https://lift-api.vfsglobal.com/appointment/fees";
    // const panelRoot = document.getElementById("vfs_quickbot_panel"); // Não usado, pode ser removido
    const { email } = apiDetectAuth(); // Email de login
   
    // 🔑 VERIFICAÇÃO CRÍTICA DO NOVO ARGUMENTO


    
    // 🔑 EXTRAÇÃO DO URN: O URN é a primeira parte se o ID for ARN/URN (Ex: XYZ123/1)
   const URN = currentContext.groupURN
    const centerCode = context.vacCode || 'LUA'; // Usar o VAC code do contexto, ou POLU como default.
    const loginUser = context.loginUser || email; // Usar o loginUser do contexto ou o email detectado.
   const jwtToken = currentContext.jwtToken
    // =========================================================================
    // 2. CRIAÇÃO E LOG DO PAYLOAD JSON
    // =========================================================================
    let feeBody;
    try {
        // CORRIGIDO: Usando as variáveis extraídas (urn, centerCode, loginUser)
        feeBody = JSON.stringify({
            missionCode:'bra',
            countryCode: 'ago',
            centerCode: centerCode, // Usando POLU do payload de exemplo, ou VAC code
            loginUser: loginUser,   // Usando o email de login (valdemirozakai02@outlook.pt)
            urn: URN,               // Usando o URN obtido
            languageCode: "en-US",
        });

    } catch (e) {
        apiLog(`❌ ERRO FATAL: JSON.stringify falhou. Causa: ${e.message}`, true);
        return null;
    }
    
    // ... O restante do código (EXECUÇÃO DA API) permanece o mesmo ...
    
    // =========================================================================
    // 3. EXECUÇÃO DA API (Fetch)
    // =========================================================================
    const headers = { 
        "authorize": jwtToken, 
        "Content-Type": "application/json;charset=UTF-8",
         'clientsource': '',
        "accept": "application/json, text/plain, */*",
        "origin": VFS_BASE_URL,
        "referer": VFS_BASE_URL + "/appointment/appointment-booking",
    };
        const stepName = "GET_FEE";
    try {
        const response = await fetchDataInjected(
        VFS_GET_FEE_URL, 
        stepName,                 // 2. stepName (String de identificação)
        headers,                  // 3. headers
        'POST',                   // 4. method (String 'POST')
        feeBody               // 5. body (String JSON preparada)
);const jsonResponse = await fetchDataInjected(VFS_GET_FEE_URL, stepName, headers, 'POST', feeBody);

        // 🛑 LÓGICA DE ERRO DE NEGÓCIO VFS (O foco é o campo 'error')

        // 1. Verifica se o campo 'error' NÃO é null.
        if (jsonResponse.error) {
            
            // Permite a passagem do ERRO 1003 (Se for o caso de teste)
            if (String(jsonResponse.error.code) === '1003') {
               apiLog(`⚠️ API 7 (getFee) OK com ERRO 1003 (Dados de teste). Prosseguindo...`, false);
               // Retorna o objeto com o erro 1003 para que o fluxo avance, se necessário
               return jsonResponse; 
            }
            
            // Falha para qualquer outro erro que não seja 1003
            apiLog(`❌ API 7 (getFee) ERRO DE NEGÓCIO (Status 200): ${jsonResponse.error.description} (Code: ${jsonResponse.error.code}).`, true);
            return null;
        }
        
        // 2. SUCESSO DE NEGÓCIO ('error' era null)
        
        const totalAmount = jsonResponse.totalamount;
        // Garantimos a extração dos valores
        const currency = jsonResponse.feeDetails && jsonResponse.feeDetails.length > 0 
                         ? jsonResponse.feeDetails[0].currency 
                         : 'N/A';
        
      //  apiLog(`✅ API 7 (getFee) SUCESSO. Valor total da taxa: ${totalAmount} ${currency}`, false);
        return jsonResponse; 

    } catch (e) {
        // Captura Falhas de REDE/EXCEÇÃO (Onde estava a dar o REDE/FATAL)
        const errorStatus = e.status || 'REDE/FATAL';
        apiLog(`❌ API 7 FALHA FATAL (Status: ${errorStatus}). Mensagem: ${e.message}`, true);
        return null;
    }
}

async function getIdnfyToken(VFS_BASE_URL,currentContext ) {
    const VFS_IDNFY_TOKEN_URL = "https://lift-api.vfsglobal.com/appointment/idnfytoken";
     const { email } = apiDetectAuth(); // Email de login
    // 🔑 1. VERIFICAÇÃO CRÍTICA DO CONTEXTO
    // 🔑 VERIFICAÇÃO CRÍTICA DO NOVO ARGUMENTO
    // 🔑 EXTRAÇÃO DO URN: O URN é a primeira parte se o ID for ARN/URN (Ex: XYZ123/1)
   const ARN = currentContext.individualARN
   const jwtToken = currentContext.jwtToken
    // ===============================================

    const loginUser = context.loginUser || email; // Email de login
    const centerCode = context.vacCode || 'LUA'; // Usar o VAC code real
    const cultureCode = currentContext.languageCode || "en-US";
    
    // 2. CRIAÇÃO DO PAYLOAD JSON
    let tokenBody;
    try {
        // Alinhado ao payload fornecido
        tokenBody = JSON.stringify({
            missionCode: 'bra',
            countryCode: context.countryCode.toLowerCase(),
            centerCode: centerCode, 
            loginUser:  loginUser, 
            aurn:ARN,
            cultureCode: cultureCode,
            languageCode: cultureCode.split('-')[0], // Ex: de 'en-US' para 'en'
            scanStatus: ""
        });
    } catch (e) {
        apiLog(`❌ ERRO FATAL na API 8: JSON.stringify falhou. Causa: ${e.message}`, true);
        return null;
    }
    
    // 3. EXECUÇÃO DA API (Fetch)
    const headers = { 
        "authorize": jwtToken, 
        "Content-Type": "application/json;charset=UTF-8",
         'clientsource': '',
        // Headers adicionais conforme solicitado e boas práticas
        "accept": "application/json, text/plain, */*",
        "accept-language": "en-US,en;q=0.9",
        "origin": VFS_BASE_URL,
        "referer": VFS_BASE_URL + "/appointment/appointment-booking", // Referer da página anterior
        "Route": 'AGO/en/bra',
        "sec-fetch-dest": "empty",
        "sec-fetch-mode": "cors",
        "sec-fetch-site": "same-site",
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
    };
const stepName = "IDNFY_TOKEN";
 ////   apiLog(`[API 8] Tentando obter IDNfy Token para ARN: ${applicantArn}...`, false); 

    try {
        const response = await fetchDataInjected(
        VFS_IDNFY_TOKEN_URL, 
        stepName,                 // 2. stepName (String de identificação)
         headers,                  // 3. headers
        'POST',                   // 4. method (String 'POST')
        tokenBody               // 5. body (String JSON preparada)
);

    // 1. TRATAMENTO DE ERRO DE NEGÓCIO
        if (response.error) {
            apiLog(`❌ API 8 ERRO DE NEGÓCIO (CÓDIGO ${response.error.code}): ${response.error.description}`, true);
            return null; 
        }

        // 2. EXTRAÇÃO E LOG COMPLETO (Sua exigência)
        const redirectUrl = response.redirectUrl;
        const requestRefNumber = response.requestRefNumber;
        
        if (!redirectUrl || !requestRefNumber) {
             apiLog(`❌ API 8 SUCESSO INCOMPLETO: 'redirectUrl' ou 'requestRefNumber' ausentes.`, true);
             return null;
        }

        let idnfyToken = 'N/A';
        try {
            const redirectUrlObj = new URL(redirectUrl);
            idnfyToken = redirectUrlObj.searchParams.get('token'); 
        } catch (e) {
            apiLog(`[${currentContext.applicantName || 'APPLICANT'}] ❌ Falha ao parsear 'redirectUrl'. Erro: ${e.message}`, true, 'error');
            // Não falha a Promise aqui, apenas retorna N/A para o token.
        }

        // 🔑 LOG COMPLETO PARA O PAINEL: Link e Referência
        apiLog(` ${redirectUrl}.`, false);
        apiLog(` ${redirectUrl}`, true, 'success');
        
        // 3. RETORNA APENAS OS DADOS CRÍTICOS PARA O CONSUMO
        return {
            redirectUrl: redirectUrl,
            requestRefNumber: requestRefNumber, // O valor que o retry vai monitorizar
            idnfyToken: idnfyToken,
        };

    } catch (e) {
        // 4. CAPTURA DE FALHAS DE REDE/HTTP (Onde estava o ReferenceError de URL)
        const errorStatus = e.status || 'REDE/FATAL';
        apiLog(`❌ API 8 FALHA FATAL (Status: ${errorStatus}). Mensagem: ${e.message}`, true);
        return null; 
    }
}








// ⚠️ ATUALIZE ESTAS CONSTANTES COM OS VALORES DINÂMICOS DA SUA SESSÃO ⚠️
// Eles provavelmente precisam ser capturados do Local Storage, Cookies, ou de um formulário da página.
async function solicitarOTP(VFS_BASE_URL, currentContext) {
    // 🔑 1. Extrai o token de sessão do contexto para o Authorization header
     const jwtToken = currentContext.jwtToken
    // 🔑 2. Dados necessários
    const { email } = apiDetectAuth(); 
    const VFS_SOLICITE_OTP_URL = "https://lift-api.vfsglobal.com/appointment/applicantotp";
    const cultureCode = currentContext.cultureCode || "en-US";
    const centerCode = currentContext.vacCode || 'LUA';
    const urn = currentContext.groupURN; // O ARN/URN está correto aqui

    // 🔑 3. O token deve ser verificado aqui antes da chamada
    
    const payload = JSON.stringify({
        OTP: "",
        captcha_api_key: "",
        captcha_version: "",
        centerCode: centerCode,
        countryCode: context.countryCode.toLowerCase(),
        languageCode: cultureCode.split('-')[0],
        loginUser: email, // Seu email
        missionCode: "bra",
        otpAction: "GENERATE",
        urn: urn, // ✅ URN CORRETO no payload
        userAction: null
    });

    // 🔑 4. EXECUÇÃO DA API
       const headers = {
    "Content-Type": "application/json;charset=UTF-8",
    
    // 🔑 CORREÇÃO CRÍTICA 1: Usar 'authorize' (minúsculo) conforme a API VFS
    // Removido 'Bearer' e o espaço extra.
    "authorize": jwtToken, 
    
    // CORREÇÃO: Limpando a vírgula extra no accept
    "accept": "application/json, text/plain, */*", 
      'clientsource': '',
    
    "accept-language": "pt,en;q=0.9",
    "datacenter": 'GERMANY',
    
    // Note: VFS_BASE_URL deve ser 'https://visa.vfsglobal.com' (o domínio principal, não o de login)
    "origin": VFS_BASE_URL, 
    "referer": VFS_BASE_URL + "/appointment/select-vac",
    
    // CORREÇÃO CRÍTICA 2: Garantir que a rota esteja correta
    "route" : 'ago/en/bra', 
    
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", // Use a versão mais recente
    
    // REMOVIDOS: :authority, :method, :path, content-length, sec-fetch-* (deixamos o navegador gerenciá-los)
};
    
    // ... (Sua lógica de chamada API fetch/axios aqui) ...
    // const response = await fetch(url, { method: 'POST', headers, body: JSON.stringify(payload) }); 
    // return response;


    const stepName = "SOLICITE_OTP";
    
    try {
        console.log(`[${new Date().toLocaleTimeString()}] 📤 Tentando solicitar OTP...`);
        
        const response = await fetchDataInjected(
        VFS_SOLICITE_OTP_URL ,
        stepName,                 // 2. stepName (String de identificação)
        headers,                  // 3. headers
        'POST',                   // 4. method (String 'POST')
        payload                // 5. body (String JSON preparada)
);
   if (response.error) {
        apiLog(`❌ AO SOLICITAR OTP (${response.error.code}): ${response.error.description}`, true);
        
        // Retorna um objeto de falha estruturado
        return { 
            success: false, 
            error: { 
                code: response.error.code || 'BUSINESS_ERROR', 
                description: response.error.description
            }
        };
    }

    // 2. Acesso Direto ao Campo:
    const isGenerated = response.isOTPGenerated;
    const remainingLimit = response.remainingOTPLimit || 0;

    if (isGenerated === true) {
       // apiLog("✅ OTP GERADO COM SUCESSO! Prosseguindo para a etapa de validação.", false, 'success');
        
        // 🎯 RETORNO DE SUCESSO COMPLETO
        return { 
            success: true, 
            isOTPGenerated: true,
            remainingLimit: remainingLimit,
            payload: response // Opcional: mantém o payload completo
        };
    } else {
        // Se a resposta retornar false (falha de lógica de negócio)
        apiLog(`⚠️ FALHA na geração do OTP. Limite restante: ${remainingLimit}.`, true, 'warning');
        
        // ❌ RETORNO DE FALHA ESTRUTURADA (mesmo que a comunicação tenha sido 200)
        return { 
            success: false, 
            isOTPGenerated: false,
            message: `OTP não gerado pelo servidor. Limite restante: ${remainingLimit}.`,
            messageCode: 'OTP_NOT_GENERATED'
        };
    }
    
// (Não é necessário o bloco catch aqui, a menos que ele abranja a chamada de rede)

} catch (e) {
    // 4. CAPTURA DE FALHAS DE REDE/HTTP/LÓGICA
    const errorStatus = e.status || 'REDE/FATAL';
    apiLog(`❌ API 8 FALHA FATAL (Status: ${errorStatus}). Mensagem: ${e.message}`, true);
    
    // 🛑 Retorna sempre um objeto de falha estruturado
    return { 
        success: false, 
        error: { 
            code: errorStatus, 
            description: e.message || "Erro desconhecido na API 8." 
        }
    };
}
}

// ⚠️ Use as mesmas constantes DYNAMIC_AUTHORIZE_TOKEN e DYNAMIC_CLIENT_SOURCE da requisição anterior.

// ⚠️ Estes tokens são DINÂMICOS e devem ser capturados em tempo real na sua extensão.
// Usamos os valores fornecidos como exemplo para a estrutura.


/**
 * Implementação da função 'validarOTP'.
 * Chama o endpoint /appointment/applicantotp para verificar o código OTP e autenticar a sessão.
 * * * CORREÇÃO DE PAYLOAD: Garante que apenas os dados necessários são enviados no corpo da requisição.
 * * CORREÇÃO DE AUTORIZAÇÃO: Utiliza o header "authorize" minúsculo com o token direto.
 * * @param {object} context Objeto de contexto global (com token, email, vacCode, etc.).
 * @param {string} otpCode O código de 6 dígitos inserido (manual ou automático).
 * @param {string} VFS_BASE_URL URL base da VFS (ex: 'https://visa.vfsglobal.com').
 * @param {object} currentContext Contexto de agendamento (não utilizado aqui).
 * @param {string} applicantArn ARN do solicitante.
 * @returns {object} { success: boolean, error: string }
 */
async function validarOTP(otpCode, VFS_BASE_URL, currentContext) {
     const jwtToken = currentContext.jwtToken 
    
    // 🔑 2. Dados necessários
    const { email } = apiDetectAuth(); 
    const VFS_OTP_VALIDATION_URL = "https://lift-api.vfsglobal.com/appointment/applicantotp";
    const cultureCode = currentContext.cultureCode || "en-US";
    const centerCode = currentContext.vacCode || 'LUA';
    const urn = currentContext.groupURN; // O ARN/URN está correto aqui

    // 🔑 3. O token deve ser verificado aqui antes da chamada
  

    const payload = JSON.stringify({
        OTP: otpCode,
        captcha_api_key: "",
        captcha_version: "",
        centerCode: centerCode,
        countryCode: currentContext.countryCode.toLowerCase(),
        languageCode: cultureCode.split('-')[0],
        loginUser: email, // Seu email
        missionCode: "bra",
        otpAction: "VALIDATE",
        urn: urn, // ✅ URN CORRETO no payload
        userAction: null
    });

    // 3. Headers Exatos
    const headers = {
        "Content-Type": "application/json;charset=UTF-8",
        
        // 🔑 CORREÇÃO CRÍTICA: Usar 'authorize' (minúsculo) conforme a API VFS, sem 'Bearer'
        "authorize": jwtToken, 
        
        "accept": "application/json, text/plain, */*", 
          'clientsource': '',
        
        "accept-language": "pt,en;q=0.9",
        "datacenter": 'GERMANY',
        
        "origin": VFS_BASE_URL, 
        "referer": VFS_BASE_URL ,
        
        // CORREÇÃO CRÍTICA: Rota correta para Angola/Português (ajustar se necessário)
        "route" : 'ago/en/bra', 
        
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", 
    };
    const stepName ="OTP_VALIDATION"
 
    // 4. EXECUÇÃO DA API
    try {
        console.log(`[${new Date().toLocaleTimeString()}] 📤 Tentando validar OTP: ${otpCode}...`);
        
    const  response = await fetchDataInjected(
    VFS_OTP_VALIDATION_URL, // 1. url
    stepName,                 // 2. stepName (String de identificação)
    headers,                  // 3. headers
    'POST',                   // 4. method (String 'POST')
    payload               // 5. body (String JSON preparada)
);

           if (response.error) {
        apiLog(`❌ AO SOLICITAR OTP (${response.error.code}): ${response.error.description}`, true);
        
        // Retorna um objeto de falha estruturado
        return { 
            success: false, 
            error: { 
                code: response.error.code || 'BUSINESS_ERROR', 
                description: response.error.description
            }
        };
    }

    // 2. Acesso Direto ao Campo:
    const isOTPValidated = response.isOTPValidated;
    const remainingLimit = response.remainingOTPLimit || 0;

    if (isOTPValidated === true) {
       apiLog("✅ OTP VALIDADO COM SUCESSO.", false, );
        
        // 🎯 RETORNO DE SUCESSO COMPLETO
        return { 
            success: true, 
            isOTPValidated: true,
            remainingLimit: remainingLimit,
            payload: response // Opcional: mantém o payload completo
        };
    } else {
        // Se a resposta retornar false (falha de lógica de negócio)
        apiLog(`⚠️ FALHA na geração do OTP. Limite restante: ${remainingLimit}.`, true, 'warning');
        
        // ❌ RETORNO DE FALHA ESTRUTURADA (mesmo que a comunicação tenha sido 200)
        return { 
            success: false, 
            isOTPGenerated: false,
            message: `OTP não gerado pelo servidor. Limite restante: ${remainingLimit}.`,
            messageCode: 'OTP_NOT_VALIDATED'
        };
    }
    
// (Não é necessário o bloco catch aqui, a menos que ele abranja a chamada de rede)

} catch (e) {
    // 4. CAPTURA DE FALHAS DE REDE/HTTP/LÓGICA
    const errorStatus = e.status || 'REDE/FATAL';
    apiLog(`❌ API 8 FALHA FATAL (Status: ${errorStatus}). Mensagem: ${e.message}`, true);
    
    // 🛑 Retorna sempre um objeto de falha estruturado
    return { 
        success: false, 
        error: { 
            code: errorStatus, 
            description: e.message || "Erro desconhecido na API 8." 
        }
    };
}
}





        
function extractStartTime(slotString) {
    if (typeof slotString !== 'string' || !slotString.includes('-')) {
        return "00:00"; // Fallback seguro
    }
    // Retorna a primeira parte do intervalo (ex: "13:00" de "13:00-13:15")
    return slotString.trim().split('-')[0];
}

// ⚠️ Use os tokens mais recentes após a validação do OTP
// Função auxiliar para criar um atraso (sleep) baseado em Promises
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

/**
 * Implementação da função 'validarOTP'.
 * Chama o endpoint /appointment/calendar para verificar o código OTP e autenticar a sessão.
 * * @param {string} VFS_BASE_URL URL base da VFS (ex: 'https://visa.vfsglobal.com').
 * @param {object} currentContext Contexto de agendamento.
 * @returns {object|null} Objeto com availableDates ou null em caso de falha.
 */
async function verificarCalendario( VFS_BASE_URL,currentContext) {
    const VFS_CALENDARIO_CHECK_URL = "https://lift-api.vfsglobal.com/appointment/calendar";
    const { email } = apiDetectAuth(); // Email de login
    const visaCode = currentContext.subCategoryCode;
    const payCode = currentContext.paymentTypeId;
    const jwtToken = currentContext.jwtToken;
    // 🔑 EXTRAÇÃO DO URN: O URN é a primeira parte se o ID for ARN/URN (Ex: XYZ123/1)
    const urn = currentContext.groupURN;
    const centerCode = currentContext.vacCode || 'LUA'; // Usar o VAC code do contexto, ou POLU como default.
    
    // 1. CONSTRUÇÃO DO PAYLOAD INICIAL (com fromDate placeholder)
    const initialPayloadObject = {
        centerCode: centerCode,
        countryCode: "AGO",
        fromDate: "01/12/2025", // Placeholder
        loginUser: email,
        missionCode: "bra",
        payCode: payCode, 
        urn: urn, 
        visaCategoryCode: visaCode,
    };
    
    const initialPayloadString = JSON.stringify(initialPayloadObject,);

    // 2. ATUALIZAÇÃO DO PAYLOAD COM A DATA ESCOLHIDA/CALCULADA
    // Chama a função anterior para ler o input manual ou calcular a data do próximo mês
    const updatedPayload = atualizarFromDateEPayload(initialPayloadString,currentContext,);
    console.log("Payload Atualizado:", updatedPayload);

    // ... (restante do código omisso para brevidade)
    
    const headers = {
        "Content-Type": "application/json;charset=UTF-8",
        "authorize": jwtToken ,
        "accept": "application/json, text/plain, */*", 
        'clientsource': '',
        "accept-language": "pt-BR,pt;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        "origin": VFS_BASE_URL, 
        "referer": VFS_BASE_URL ,
        "route" : 'ago/en/bra', 
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", 
    };

    // --- ATRASO DE 1.5 SEGUNDOS (1500ms) ---
    const delayTime = 0; // Se quiser 1.5s, use 1500
    console.log(`[${new Date().toLocaleTimeString()}] ⏳ Atrasando a requisição do calendário em ${delayTime / 1000}s...`);
    await delay(delayTime);
    console.log(`[${new Date().toLocaleTimeString()}] ✅ Atraso concluído. Preparando para enviar requisição...`);
    // --- FIM DO ATRASO ---

    try {
        const stepName = "CALENDARIO_CHECK";
        console.log(`[${new Date().toLocaleTimeString()}] 📤 Verificando disponibilidade no calendário...`);
        
        const response = await fetchDataInjected(
            VFS_CALENDARIO_CHECK_URL,
            stepName,
            headers,
            'POST',
            // 🚨 CORREÇÃO AQUI: USAR O PAYLOAD ATUALIZADO 
            updatedPayload // <-- AGORA ESTÁ ENVIANDO A DATA CORRETA
        );
        
        // 1. TRATAMENTO DE ERRO DE NEGÓCIO
        if (response.error) {
            apiLog(`❌ API 8 ERRO DE NEGÓCIO (CÓDIGO ${response.error.code}): ${response.error.description}`, true);
            return null; 
        }

        // 2. EXTRAÇÃO E LOG COMPLETO (Sua exigência)
        const availableDates = response.calendars;
        
        if (!availableDates || availableDates.length === 0) {
            apiLog(`❌ datas ausentes `, true);
            return null;
        }

        // 🔑 LOG COMPLETO PARA O PAINEL: Link e Referência
       // apiLog(`✅ API 8 SUCESSO. Ref #: ${availableDates.length}.`, false);
        
        // 3. RETORNA APENAS OS DADOS CRÍTICOS PARA O CONSUMO
        return {
            availableDates: availableDates,
        };

    } catch (e) {
        // 4. CAPTURA DE FALHAS DE REDE/HTTP 
        const errorStatus = e.status || 'REDE/FATAL';
        apiLog(`❌ API 8 FALHA FATAL (Status: ${errorStatus}). Mensagem: ${e.message}`, true);
        return null; 
    }
}



/**
 * Implementação da função 'validarOTP'.
 * Chama o endpoint /appointment/applicantotp para verificar o código OTP e autenticar a sessão.
 * * * CORREÇÃO DE PAYLOAD: Garante que apenas os dados necessários são enviados no corpo da requisição.
 * * CORREÇÃO DE AUTORIZAÇÃO: Utiliza o header "authorize" minúsculo com o token direto.
 * * @param {object} context Objeto de contexto global (com token, email, vacCode, etc.).
 * @param {string} VFS_BASE_URL URL base da VFS (ex: 'https://visa.vfsglobal.com').
 * @param {object} currentContext Contexto de agendamento (não utilizado aqui).
 * @param {string} applicantArn ARN do solicitante.
 * @returns {object} { success: boolean, error: string }
 * @param {string} selectedDate A data escolhida no formato "MM/DD/YYYY" (e.g., "12/12/2025").
 */
async function obterTimeSlots(selectedDate, VFS_BASE_URL,currentContext) {
    const VFS_HARARIO_URL = "https://lift-api.vfsglobal.com/appointment/timeslot";
    const { email } = apiDetectAuth(); // Email de login
    const visaCode = currentContext.subCategoryCode
    const jwtToken = currentContext.jwtToken 
    // 🔑 EXTRAÇÃO DO URN: O URN é a primeira parte se o ID for ARN/URN (Ex: XYZ123/1)
    const urn = currentContext.groupURN
    const centerCode = currentContext.vacCode || 'LUA'; // Usar o VAC code do contexto, ou POLU como default.
    const loginUser = currentContext.loginUser || email; // Usar o loginUser do contexto ou o email detectado

    const payload = JSON.stringify({
        centerCode:centerCode,
        countryCode: context.countryCode.toLowerCase(),
        loginUser: loginUser ,
        missionCode: "bra",
        slotDate: selectedDate, // Data dinâmica da requisição 3
        urn: urn,
        visaCategoryCode: visaCode,
    });


 const headers = {
        "Content-Type": "application/json;charset=UTF-8",
        "authorize": jwtToken ,
        "accept": "application/json, text/plain, */*", 
        'clientsource': '',
        "accept-language": "pt-BR,pt;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        "origin": VFS_BASE_URL, 
        "referer": VFS_BASE_URL ,
        "route" : 'ago/en/bra', 
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", 
    };

    // --- ATRASO DE 1.5 SEGUNDOS (1500ms) ---
    const delayTime = 500; // 1.5 segundos
   // console.log(`[${new Date().toLocaleTimeString()}] ⏳ Atrasando a requisição do calendário em ${delayTime / 1000}s...`);
    await delay(delayTime);
   // console.log(`[${new Date().toLocaleTimeString()}] ✅ Atraso concluído. Preparando para enviar requisição...`);
try {
        console.log(`[${new Date().toLocaleTimeString()}] 📤 Buscando slots disponíveis para ${selectedDate}...`);
        const stepName =  "HORARIO"
        const response = await fetchDataInjected(
        VFS_HARARIO_URL,
        stepName,                 // 2. stepName (String de identificação)
        headers,                  // 3. headers
        'POST',                   // 4. method (String 'POST')
        payload                // 5. body (String JSON preparada)
);

     
    // 1. TRATAMENTO DE ERRO DE NEGÓCIO
        if (response.error) {
            apiLog(`❌ API 8 ERRO DE NEGÓCIO (CÓDIGO ${response.error.code}): ${response.error.description}`, true);
            return null; 
        }

        // 2. EXTRAÇÃO E LOG COMPLETO (Sua exigência)
      const slots = response.slots;
        
        if (!slots || slots.length === 0) {
             apiLog(`❌ datas ausentes `, true);
             return null;
        }

        // 🔑 LOG COMPLETO PARA O PAINEL: Link e Referência
        apiLog(`✅data ${selectedDate} horarios disponiveis ${slots.length}.`, false)
        // 3. RETORNA APENAS OS DADOS CRÍTICOS PARA O CONSUMO
        return {
            slots: slots,
        };

    } catch (e) {
        // 4. CAPTURA DE FALHAS DE REDE/HTTP (Onde estava o ReferenceError de URL)
        const errorStatus = e.status || 'REDE/FATAL';
        apiLog(`❌ API 8 FALHA FATAL (Status: ${errorStatus}). Mensagem: ${e.message}`, true);
        return null; 
    }
}




/**
 * Implementação da função 'validarOTP'.
 * Chama o endpoint /appointment/applicantotp para verificar o código OTP e autenticar a sessão.
 * * * CORREÇÃO DE PAYLOAD: Garante que apenas os dados necessários são enviados no corpo da requisição.
 * * CORREÇÃO DE AUTORIZAÇÃO: Utiliza o header "authorize" minúsculo com o token direto.
 * * @param {object} context Objeto de contexto global (com token, email, vacCode, etc.).
 * @param {string} VFS_BASE_URL URL base da VFS (ex: 'https://visa.vfsglobal.com').
 * @param {object} currentContext Contexto de agendamento (não utilizado aqui).
 * @param {string} applicantArn ARN do solicitante.
 * @returns {object} { success: boolean, error: string }
 */
async function analisarCalendario( VFS_BASE_URL,currentContext) {
   
    
    try {
        apiLog(` 🔍 Buscando calendário de vagas...`, false);
        await delay(500); 
       // apiLog(` ✅ Atraso concluído. Prosseguindo com a chamada de API.`, false);
        
        const calendarResponse = await verificarCalendario( VFS_BASE_URL,currentContext);

        const responseString = calendarResponse ? JSON.stringify(calendarResponse).substring(0, 100) : "Resposta NULA/UNDEFINED";
        apiLog(` 🛠️ DEBUG - Resposta Bruta da API 9: ${responseString}...`, false, 'debug');
        
        const availableDates = calendarResponse ? calendarResponse.availableDates : null;

        if (!availableDates || availableDates.length === 0) {
            apiLog(` ❌ Calendário vazio ou falhou.`, true, 'error');
            return { success: false, targetDates: [], message: "Calendário sem vagas ou falha na API 9.", internalErrorCode: "CALENDAR_EMPTY", messageCode: "CALENDAR_EMPTY" };
        }
        
        const availableDateObjects = availableDates; 

        const uniqueDateStrings = Array.from(
            new Set(availableDateObjects.map(item => item.date))
        ).sort((a, b) => new Date(a) - new Date(b)); 
        
        const numDates = uniqueDateStrings.length;
        const selectedDateStrings = [];

        apiLog(` 🗓️ ${numDates} datas ÚNICAS disponíveis. `, false);

        if (numDates === 0) {
            return { success: false, targetDates: [], message: "Nenhuma data disponível no calendário.", internalErrorCode: "NO_DATES_FOUND", messageCode: "NO_DATES_FOUND" };
        } else if (numDates <= 3) {
            selectedDateStrings.push(...uniqueDateStrings);
        } else {
            const firstDate = uniqueDateStrings[0];
            const lastDate = uniqueDateStrings[numDates - 1];
            
            const middleIndex = Math.floor((numDates - 1) / 2); 
            const middleDate = uniqueDateStrings[middleIndex];

            selectedDateStrings.push(firstDate);
            
            if (middleDate !== firstDate) {
                selectedDateStrings.push(middleDate);
            }
            
            if (lastDate !== firstDate && lastDate !== middleDate) {
                selectedDateStrings.push(lastDate);
            }
        }
        
        const targetDates = selectedDateStrings.map(d => ({ date: d }));

       // apiLog(` 🎯 DATAS ESCOLHIDAS PARA BUSCA DE SLOTS: ${selectedDateStrings.join(', ')}`, true, 'debug');
      //  apiLog(` ✅ ${targetDates.length} datas-alvo selecionadas: ${selectedDateStrings.join(', ')}`, false, 'success');
        
        return { 
            success: true, 
            targetDates: targetDates, 
            message: `Selecionadas ${targetDates.length} datas-alvo para busca de slots.`
        };

    } catch (e) {
        // 4. CAPTURA DE FALHAS DE REDE/HTTP 
        const errorStatus = e.status || 'REDE/FATAL';
        
        // 🛑 CORREÇÃO NO LOG: Deve referir-se à API de Calendário, não ao OTP
        apiLog(`❌ Falha na API de Calendário (Status: ${errorStatus}). Mensagem: ${e.message}`, true);
        return { success: false, error: e.message }; // Retorna o erro no formato esperado
    }
}



/**
 * Implementação da função 'analisarTimeslot' para buscar TODOS os horários disponíveis
 * em MÚLTIPLAS datas e armazená-los no contexto.
 *
 * @param {Array<string>} apiDatesArray Array de datas no formato YYYYMMDD para análise.
 * @param {string} VFS_BASE_URL URL base da VFS (ex: 'https://visa.vfsglobal.com').
 * @param {object} currentContext Objeto de contexto (onde os slots serão armazenados).
 * @returns {object} { success: boolean, totalSlotsFound: number, message: string }
 */
async function analisarTimeslot(apiDatesArray, VFS_BASE_URL, currentContext) {
    if (!currentContext || !Array.isArray(apiDatesArray) || apiDatesArray.length === 0) {
        apiLog(` 🚨 ERRO: Funcao analisarTimeslot requer um array de datas e um contexto válido.`, true, 'error');
        return { success: false, totalSlotsFound: 0, message: "Parâmetros inválidos para análise de timeslots." };
    }

    // Inicializa uma estrutura para armazenar todos os slots encontrados no contexto
    // Isso garante que os slots de várias datas sejam armazenados.
    if (!currentContext.allAvailableSlots) {
        currentContext.allAvailableSlots = {}; // Armazenar por data para fácil acesso: { 'YYYYMMDD': [{slotData}, ...] }
    }

    let totalSlotsFound = 0;
    let allDatesProcessedSuccessfully = true;

    for (const apiDate of apiDatesArray) {
        const dateFormatted = apiDate; // Apenas para clareza
       // apiLog(` 🔄 Analisando slots para a data: ${dateFormatted}...`, false, 'info');

        try {
            const apiResponse = await obterTimeSlots(apiDate, VFS_BASE_URL, currentContext);
            
            if (apiResponse && apiResponse.error === "MISSING_APPLICANT_ARN_FOR_API_10") {
                apiLog(` 🚨 Erro de API para ${dateFormatted}: ${apiResponse.message}`, true, 'error');
                allDatesProcessedSuccessfully = false;
                // Continua para a próxima data, mas registra a falha.
                continue; 
            }

            let availableSlotsForDate = [];
            
            if (apiResponse && typeof apiResponse === 'object' && Array.isArray(apiResponse.slots)) {
                availableSlotsForDate = apiResponse.slots;
            } else {
                apiLog(` 🚨 Resposta da API 10 inválida ou sem a chave 'slots' para ${dateFormatted}.`, false, 'error');
                allDatesProcessedSuccessfully = false;
                continue; // Continua para a próxima data
            }
            
            if (availableSlotsForDate.length === 0) {
                apiLog(` ❌ Nenhum slot encontrado para ${dateFormatted}.`, false, 'info'); // 'info' pois não encontrar é normal
                // Continua para a próxima data
                continue;
            }

            // Mapeia TODOS os slots disponíveis para esta data e os adiciona ao contexto
            // A lógica de "selecionar os 3 últimos" NÃO deve estar aqui se o objetivo é guardar TODOS.
            const mappedSlots = availableSlotsForDate.map(slotData => ({
                allocationId: slotData.allocationId,
                slotTime: extractStartTime(slotData.slot), 
                fullSlotRange: slotData.slot, 
                categoryCode: slotData.categoryCode,
                date: dateFormatted 
            }));

            // Armazena no contexto sob a chave da data
            currentContext.allAvailableSlots[dateFormatted] = mappedSlots;
            totalSlotsFound += mappedSlots.length;

          //  apiLog(` ✅ ${mappedSlots.length} slots encontrados para ${dateFormatted}.`, false, 'success');

        } catch (e) {
            apiLog(` 🚨 ERRO INTERNO DURANTE A ANÁLISE DO SLOT para ${dateFormatted}: ${e.message}`, true, 'error');
            console.error(`Stack Trace detalhado em analisarTimeslot para ${dateFormatted}:`, e.stack);
            allDatesProcessedSuccessfully = false;
            // Continua para a próxima data
        }
    }

    if (totalSlotsFound > 0) {
       // apiLog(` 🏁 ANÁLISE CONCLUÍDA: Total de ${totalSlotsFound} slots encontrados em ${Object.keys(currentContext.allAvailableSlots).length} datas.`, false, 'success');
        return { 
            success: true, 
            totalSlotsFound: totalSlotsFound, 
            message: `Análise concluída. Total de ${totalSlotsFound} slots disponíveis.` 
        };
    } else {
        return { 
            success: allDatesProcessedSuccessfully, // Retorna true apenas se todas as chamadas foram bem-sucedidas mas não encontraram slots
            totalSlotsFound: 0, 
            message: "Nenhum horário disponível em todas as datas analisadas.", 
            internalErrorCode: "NO_SLOTS_ACROSS_ALL_DATES", 
            messageCode: "NO_SLOTS_ACROSS_ALL_DATES" 
        };
    }
}



/**
 * Inicia o fluxo de verificação de disponibilidade de vagas e escolha de horários.
 *
 * @param {string} VFS_BASE_URL URL base da VFS.
 * @param {object} currentContext Contexto de agendamento atual (pode conter dados de sessão e onde slots serão armazenados).
 * @returns {object} { success: boolean, finalBookingSlotsList: array, message: string }
 */
async function gerenciarSlot(VFS_BASE_URL, currentContext) {
    try {
        apiLog(`🏃 INICIANDO BUSCA E ANÁLISE DE SLOTS...`, true);
        
        // =======================================================
        // Passo 1: ANÁLISE DO CALENDÁRIO para obter as datas-alvo
        // =======================================================
        const analysisResult = await analisarCalendario(VFS_BASE_URL, currentContext); 

        if (!analysisResult.success) {
            apiLog(` ❌ Falha na Análise do Calendário: ${analysisResult.message}`, true, 'error');
            return { 
                success: false, 
                message: analysisResult.message, 
                internalErrorCode: analysisResult.internalErrorCode || "CALENDAR_FAILED",
                messageCode: analysisResult.messageCode || "CALENDAR_FAILED"
            };
        }

        const rawTargetDates = analysisResult.targetDates || []; 
        if (rawTargetDates.length === 0) {
            apiLog(` ❌ Nenhuma data alvo retornada pelo calendário.`, true, 'error');
            return { success: false, message: "Nenhuma data com vaga disponível encontrada no calendário.", internalErrorCode: "NO_DATES_FOUND" };
        }

        // Converter as datas para o formato esperado por analisarTimeslot (YYYYMMDD)
        const apiDatesArray = rawTargetDates.map(slotItem => {
            const dateString = slotItem.date;
            if (typeof dateString !== 'string' || dateString.split('/').length !== 3) {
                apiLog(` Erro: Formato de data inesperado ('${dateString}'). Ignorando.`, false, 'error');
                return null; // Retorna null para filtrar depois
            }
            const parts = dateString.split('/');
            const mes = parts[0]; 
            const dia = parts[1]; 
            const ano = parts[2]; 
           return `${dia}/${mes}/${ano}`; // Formato YYYYMMDD para a API
        }).filter(date => date !== null); // Remove datas inválidas

        if (apiDatesArray.length === 0) {
            apiLog(` ❌ Nenhuma data válida para análise de timeslots após conversão.`, true, 'error');
            return { success: false, message: "Nenhuma data válida para análise de timeslots.", internalErrorCode: "NO_VALID_DATES_FOR_SLOTS" };
        }
        
        // =======================================================
        // Passo 2: ANÁLISE DE TODOS OS TIME SLOTS DE TODAS AS DATAS
        // =======================================================
        // Chama a nova `analisarTimeslot` com o ARRAY COMPLETO de datas.
        // Essa função agora preenche `currentContext.allAvailableSlots`.
        const globalSlotAnalysisResult = await analisarTimeslot(apiDatesArray, VFS_BASE_URL, currentContext); 
        
        if (!globalSlotAnalysisResult.success) {
            apiLog(` ❌ Falha na Análise Global de Timeslots: ${globalSlotAnalysisResult.message}`, true, 'error');
            return { 
                success: false, 
                message: globalSlotAnalysisResult.message, 
                internalErrorCode: globalSlotAnalysisResult.internalErrorCode || "GLOBAL_SLOT_ANALYSIS_FAILED",
                messageCode: globalSlotAnalysisResult.messageCode || "GLOBAL_SLOT_ANALYSIS_FAILED"
            };
        }

        if (globalSlotAnalysisResult.totalSlotsFound === 0) {
            apiLog(` ❌ Nenhuma vaga encontrada em todas as datas analisadas.`, true, 'error');
            return { 
                success: false, 
                message: "Nenhuma vaga disponível em todas as datas analisadas.", 
                internalErrorCode: "NO_SLOTS_ACROSS_ALL_DATES", 
                messageCode: "NO_SLOTS_ACROSS_ALL_DATES" 
            };
        }

        // =======================================================
        // Passo 3: SELEÇÃO DA MELHOR ESTRATÉGIA DE SLOTS PARA RESERVA
        // =======================================================
        let slotsParaReserva = [];
        let selectedDate = null; // Para manter o controle da data do slot escolhido
        
        // Obter todos os slots coletados, achatando o objeto currentContext.allAvailableSlots
        let allCollectedSlots = [];
        for (const dateKey in currentContext.allAvailableSlots) {
            allCollectedSlots = allCollectedSlots.concat(currentContext.allAvailableSlots[dateKey]);
        }

        // *** AQUI VOCÊ DEFINE SUA ESTRATÉGIA DE SELEÇÃO! ***
        // Exemplo 1: Pegar os 3 slots mais tardios de QUALQUER data combinada.
        allCollectedSlots.sort((a, b) => {
            // Ordena primeiro por data (desc), depois por hora (desc)
            const dateTimeA = parseInt(a.date + a.slotTime.replace(':', '')); // YYYYMMDDHHMM
            const dateTimeB = parseInt(b.date + b.slotTime.replace(':', ''));
            return dateTimeB - dateTimeA; // Do mais recente para o mais antigo
        });
        
        // Seleciona os 3 primeiros da lista ordenada (que são os 3 mais recentes/tardios)
       slotsParaReserva = allCollectedSlots;
        
        if (slotsParaReserva.length > 0) {
            // Pega a data do primeiro slot selecionado para fins de log, ou refine conforme necessário
            selectedDate = slotsParaReserva[0].date; 
          //  apiLog(`🟢 Selecionados ${slotsParaReserva.length} slots (os mais recentes/tardios) para rotação. Data de referência: ${selectedDate}.`, true, 'success');
        } else {
            apiLog(` ❌ Nenhuma vaga ideal selecionada após a análise.`, true, 'error');
            return { 
                success: false, 
                message: "Nenhuma vaga ideal disponível para reserva.", 
                internalErrorCode: "NO_IDEAL_SLOTS_SELECTED", 
                messageCode: "NO_IDEAL_SLOTS_SELECTED" 
            };
        }

        // =======================================================
        // 📢 TRANSIÇÃO FINAL: Inclui a lista no objeto de retorno
        // =======================================================
        //apiLog(`📢 PRONTO! ${slotsParaReserva.length} slots pré-selecionados.`, true, 'warning');
            
        // 💥 CORREÇÃO AQUI: Retorna a lista de slots no objeto final para o main.js
        return { 
            success: true, 
            message: "Slots pré-selecionados e armazenados. Pronto para Rotação.",
            finalBookingSlotsList: slotsParaReserva // ⬅️ DADOS CRÍTICOS INCLUÍDOS
        };

    } catch (e) {
        apiLog(` 🚨 ERRO INTERNO NÃO ESPERADO EM gerenciarSlot: ${e.message}`, true, 'error');
        console.error("Stack Trace do Erro Interno em gerenciarSlot:", e.stack);
        return { success: false, message: `Erro fatal interno durante o gerenciamento de slots: ${e.message}`, internalErrorCode: "JS_FATAL_ERROR", messageCode: "JS_FATAL_ERROR" };
    }
}





// O JSON estático que serve de "cache" de segurança caso o servidor insista no 304.
// 🚨 VOCÊ DEVE PREENCHER ISTO com o JSON COMPLETO (token e preferences) obtido do navegador 🚨
const ONETRUST_STATIC_JSON = {
    "receiptApiEndpoint": "https://vfsglobal-privacy.my.onetrust.com/request/v1/consentreceipts",
    "token": "SEU_TOKEN_JWT_COMPLETO_AQUI", // Exemplo: "eyJhbGciOiJSUzUxMiJ9.eyJvdHpC..."
    "preferences": [
        // PREENCHER COM TODOS OS PURPOSE OBJECTS AQUI
        { "purpose": "32c664b6-d767-4963-afc2-29cd8bcd3e01", "identifierValues": "Consent to cross border transfer" },
        // ... (resto dos purposes) ...
        { "purpose": "5dac333a-b947-4f47-a1eb-4140bc27ff7c", "identifierValues": "VAS T&Cs" },
        { "purpose": "42bad10d-916e-401f-9197-68d757a4ee8a", "identifierValues": "To receive marketing information" }
    ],
    // ... restante da estrutura JSON ...
};

// =========================================================================
// API 8: Obter Payload de Consentimento (GET para OneTrust Settings)
// =========================================================================
async function getConsentPayload(VFS_BASE_URL, context) {
    const ONETRUST_URL = 'https://privacyportalde-cdn.onetrust.com/consentmanager-settings/0f1f48b5-2d1c-4db6-b608-3788fc2ccd6c/c49c0d50-1435-46a3-bb7b-3db2cfd7d7b5-active.json'; 
    
   // apiLog("Iniciando API 8 (OneTrust GET): Tentando forçar status 200 com no-cache...", false);

    // --- CABEÇALHOS ATUALIZADOS ---
    const headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
        "Cache-Control": "no-cache", 
        "Content-Type": "application/json",
        "Origin": "https://visa.vfsglobal.com",
        "Pragma": "no-cache", 
        "Referer": "https://visa.vfsglobal.com/", 
        "Priority": "u=1, i",
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        
        // ✅ ATUALIZADO CONFORME SOLICITADO
        "Sec-Fetch-Site": "cross-site", 
        
        "sec-ch-ua": '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": '"Windows"',
    };
    
    // Configuração do Timeout (mantida)
    const controller = new AbortController();
    const timeout = setTimeout(() => {
        controller.abort(); 
        apiLog("⚠️ API 8: Timeout de 10 segundos alcançado. Requisição abortada.", true);
    }, 10000); 

    let jsonResponse = null;

    try {
        const response = await fetch(ONETRUST_URL, { 
            method: 'GET', 
            headers: headers, 
            signal: controller.signal 
        });
        
        clearTimeout(timeout); 
        
        // --- TRATAMENTO DE RESPOSTA ---
        if (response.status === 304) {
            apiLog("⚠️ API 8 ainda retornou 304. Usando JSON estático como fallback.", true);
            jsonResponse = ONETRUST_STATIC_JSON; 
        } 
        else if (!response.ok) { 
            const errorText = await response.clone().text();
            apiLog(`❌ API 8 (OneTrust) falhou. Status: ${response.status}. Corpo: ${errorText.substring(0, 200)}...`, true);
            return null;
        } else {
            // SUCESSO 200 OK
            jsonResponse = await response.json(); 
           // apiLog(`✅ API 8 concluída com sucesso (Status: ${response.status}). JSON lido.`, false);//
        }

        // --- EXTRAÇÃO DOS DADOS VOLÁTEIS ---
        if (!jsonResponse || !jsonResponse.token || !jsonResponse.preferences) {
            apiLog("❌ API 8: Falha na leitura do Token/Purposes. JSON inválido ou cache vazio.", true);
            return null;
        }

        context.consentToken = jsonResponse.token; 
        const allPurposes = jsonResponse.preferences;
        context.lastTwoPurposes = allPurposes.slice(-2);
        
        //apiLog(`✅ Token e Purposes extraídos com sucesso. Pronto para API 9.`, false);//
        return jsonResponse; 

    } catch (e) {
        clearTimeout(timeout);
        const errorMessage = e.name === 'AbortError' ? "Requisição abortada por Timeout." : `Erro fatal de conexão: ${e.message}`;
        apiLog(`❌ ERRO na API 8. ${errorMessage}`, true);
        return null;
    }
}

// =========================================================================
// API 9: Registrar Consentimento (POST - CORREÇÃO FINAL)
// =========================================================================
async function registerConsent(VFS_BASE_URL, context, consentPayload) {
    const CONSENT_RECEIPT_URL = "https://vfsglobal-privacy.my.onetrust.com/request/v1/consentreceipts"; 
    
  //  apiLog("Iniciando API 9: Reconstruindo payload para corresponder à estrutura do Chrome...", false);

    // --- 1. PREPARAÇÃO DE DADOS ---
    const preferencesFromApi8 = consentPayload.preferences; 

    // 1.1. Obter os 2 últimos Propósitos e formatá-los para o POST: { Id: "..." }
    const lastTwoPurposesObjects = preferencesFromApi8.slice(-2); 

    // 🚨 AJUSTE CRUCIAL: Mapeamos apenas o ID e usamos "Id" em maiúscula
    const finalPurposesArray = lastTwoPurposesObjects.map(purposeObject => {
        return {
            "Id": purposeObject.purpose // Usamos 'Id' (maiúsculo) e o ID real do propósito
        };
    });
    
    // --- 2. CONSTRUÇÃO DO CORPO DE REQUISIÇÃO (Identical Match) ---
    // Criamos o objeto 'postBody' para espelhar o JSON do Chrome.
    const postBody = {
        // Campos de URL e Dados Pessoais (com strings vazias, conforme o seu exemplo)
        "dsDataElements": {
            "URL": "https://visa.vfsglobal.com/ago/pt/bra/review-pay",
            "FullName": "",
            "PatronymicName": ""
        },

        // 🚨 OBRIGATÓRIO: O email logado 🚨
        "identifier": context.loggedEmail, // O valor do e-mail é essencial aqui
       
        // Campos que vieram no seu exemplo de Chrome (mesmo que alguns pareçam redundantes)
        "identifier": context.email,

        "language": null, 
        
        // A lista filtrada de 2 propósitos (apenas com Id)
        "purposes": finalPurposesArray,
        
        // JWT Token (o 'token' da API 8)
        "requestInformation": consentPayload.token,
        
        
        
        // O campo 'receiptApiEndpoint' deve ser omitido pois faz parte da URL, não do corpo
        // O campo 'dataElements' é complexo e não está visível no topo da sua payload. Vamos omiti-lo
        // a menos que cause um novo erro. O OneTrust geralmente só precisa dos campos de 'dsDataElements'.
    };

    // ... (Headers e Requisição POST - Mantidos) ...
    // ...
    // Tente de novo! Se falhar com outro 400, envie a nova mensagem de erro.
    // ...

    // Remove o campo 'preferences' original, que continha dados extras e a lista completa
    delete postBody.preferences; 
    
  

    

  //  apiLog("Iniciando API 9 (OneTrust POST): Registrar Consentimento...", false);
    
    const headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br, zstd",
        "Accept-Language": "pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7",
        "Cache-Control": "no-cache", 
        "content-lenght": "3755",
        "Content-Type": "application/json",
        "Origin": "https://visa.vfsglobal.com",
        "Pragma": "no-cache", 
        "Priority": "u=1, i",
        "Referer": "https://visa.vfsglobal.com/", 
        "Sec-Fetch-Dest": "empty",
        "Sec-Fetch-Mode": "cors",
        "Sec-Fetch-Site": "cross-site",
        "sec-ch-ua": '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
        "sec-ch-ua-mobile": "?0",
        "sec-ch-ua-platform": '"Windows"',
    };

    // --- 3. REQUISIÇÃO POST ---
    try {
        const response = await fetch(CONSENT_RECEIPT_URL, { 
            method: 'POST', 
            headers: headers, 
            body: JSON.stringify(postBody), 
        });
        
        if (response.status === 200 || response.status === 201) {
           // apiLog(`✅ API 9 (Registrar Consentimento) concluída com SUCESSO. Status: ${response.status}`, false);//
            context.consentRegistered = true; 
            return true;
        } else {
            const errorText = await response.clone().text();
            apiLog(`❌ API 9 falhou. Status: ${response.status}. Erro: ${errorText.substring(0, 200)}`, true);
            return false;
        }

    } catch (e) {
        apiLog(`❌ ERRO FATAL de Rede na API 9 (POST). Mensagem: ${e.message}`, true);
        return false;
    }
}





// --- VARIÁVEIS DE CONTEXTO (Substitua pelos seus dados reais) ---
const context = {
    loggedEmail: "usuario@exemplo.com",
    securityToken: "SEU_AUTH_TOKEN_JWT", // Token obtido nas APIs anteriores
    clientSourceToken: "SEU_CLIENT_SOURCE", // Token obtido nas APIs anteriores
    centerCode: "BRL001",
    countryCode: "ago", // Código do País
    missionCode: "bra", // Código da Missão
    // dtCookie será adicionado aqui após a busca no SW
};


/**
 * 📞 Solicita o dtCookie ao Service Worker e o anexa ao contexto.
 */
async function attachDtCookieToContext(context) {
    try {
      //  apiLog("Solicitando dtCookie ao Service Worker via messaging...", false);
        
        // Esta linha envia a mensagem para o service-worker.js
        const response = await chrome.runtime.sendMessage({ action: "GET_DTCOOKIE_FOR_API" });
        const dtCookieValue = response?.dtCookie; 
        
        if (dtCookieValue) {
            context.dtCookie = dtCookieValue;
           // apiLog(`✅ dtCookie obtido via SW com sucesso! Valor: [${dtCookieValue.substring(0, 10)}...]`, false);//
            return true;
        } else {
            apiLog(`❌ RESPOSTA VAZIA do Service Worker (dtCookie = null).`, true);
        }
        
    } catch (e) {
        // Se o Service Worker não estiver ativo, este erro será disparado
        apiLog(`❌ ERRO GRAVE no Messaging. O Service Worker falhou ou o canal fechou: ${e.message}`, true);
    }
    return false;
}


/**
 * Verifica o status da conclusão do Facial ID.
 * @param {object} currentContext Contexto atual (contém centerCode, loginUser, applicantArn, requestRefNumber, etc.).
 * @param {string} token Token de autorização.
 * @param {string} VFS_BASE_URL URL base da VFS.
 * @returns {object} O objeto de resposta da API (incluindo 'status' e 'requestRefNumber').
 */
async function verificarStatusFacial(currentContext, VFS_BASE_URL) {
    const VFS_STATUS_FACIAL_URL = "https://lift-api.vfsglobal.com/appointment/idnfystatus";
   const { email } = apiDetectAuth(); 
    
    const jwtToken = currentContext.jwtToken 
    // 🔑 EXTRAÇÃO DO URN: O URN é a primeira parte se o ID for ARN/URN (Ex: XYZ123/1)
    const ARN = currentContext.individualARN
    const centerCode = currentContext.vacCode || 'LUA'; // Usar o VAC code do contexto, ou POLU como default.
    const loginUser = currentContext.email || email;; // Usar o loginUser do contexto ou o email detectado
    const refer = currentContext.requestRefNumber || "refer";
      const clientsourceToken = currentContext.clientsourceToken
    // O requestRefNumber é crucial e deve ter sido obtido da API anterior (getIdnfyToken)
    if (!currentContext.requestRefNumber) {
         apiLog(`❌ ERRO: requestRefNumber não encontrado no contexto para verificar o status facial.`, true, 'error');
         return { success: false, status: 'MISSING_REF', message: 'requestRefNumber ausente.' };
    }
    
    // --- Payload ---
    const payload = JSON.stringify({
        "aurn": ARN ,
        "centerCode": centerCode,
        "countryCode": currentContext.countryCode.toLowerCase(),
        "loginUser": email,
        "missionCode": 'bra',
        "requestRefNumber": refer,
    });

    // --- Headers ---
    const headers = {
        "authorize":jwtToken,
        "accept": "application/json, text/plain, */*", 
        'clientsource': '',
        "accept-language": "pt-BR,pt;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        "content-type":'application/json;charset=UTF-8',
        "origin": VFS_BASE_URL, 
        "referer": VFS_BASE_URL + "/appointment/select-vac",
        "route" : 'ago/en/bra', 
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", 
    };
        const stepName = "STATUS_FACIAL"
    
    try {
        const response = await fetchDataInjected(
        VFS_STATUS_FACIAL_URL, 
        stepName,                 // 2. stepName (String de identificação)
        headers,                  // 3. headers
        'POST',                   // 4. method (String 'POST')
        payload               // 5. body (String JSON preparada)
);
if (!response || !response.status) { // ⬅️ Blindagem para garantir que o campo 'status' existe
        // Se a resposta está incompleta ou falhou (mesmo que a chamada HTTP tenha sido feita)
        apiLog(`❌ Resposta incompleta ou inválida do servidor.`, true, 'error');
        throw new Error("Resposta da API de Facial inválida ou faltando campo 'status'.");
    }

    const APPROVED = 'APPROVED'; // O valor literal que a API retorna
    const DENIED = 'DENIED';     // O valor literal que a API retorna
    // 2. Acesso Seguro ao Campo:
    const statusFacial = response.status; // Renomeado para evitar confusão com HTTP status
    
    // 3. VERIFICAÇÃO DE NEGÓCIO
    if (statusFacial === APPROVED) {
        apiLog("✅ FACIAL APROVADO.", false, 'success');
        
        return { 
            success: true, 
            statusFacial: APPROVED,
            payload: response 
        };
    } else {
        // Se a resposta retornar false (falha de lógica de negócio)
        apiLog(`⚠️ FACIAL NÃO APROVADO REPETINDO. Status: ${statusFacial}`, true, 'warning');
        
        // Determinar o limite restante (se a chave existir no JSON)
        const remainingLimit = response.remainingLimit || 'N/A';
        
        return { 
            success: false, 
            statusFacial: DENIED, // Retorna o status real (e.g., DENIED, PENDING)
            message: `Facial não aprovado. Limite restante (se aplicável): ${remainingLimit}.`,
            messageCode: 'FACIAL_NOT_APPROVED'
        };
    }

} catch (e) {
    // 4. CAPTURA DE FALHAS DE REDE/HTTP/LÓGICA (inclui falha de JSON parse ou acesso inseguro)
    const errorStatus = e.status || 'REDE/FATAL';
    apiLog(`❌ FALHA NA VERIFICAÇÃO DO STATUS DO FACIAL: ${errorStatus}). Mensagem: ${e.message}`, true);
    
    // 🛑 Retorna sempre um objeto de falha estruturado
    return { 
        success: false, 
        statusFacial: 'FATAL_ERROR',
        error: { 
            code: errorStatus, 
            description: e.message || "Erro desconhecido na API 8." 
         }
    };
}
}
window.verificarStatusFacial = verificarStatusFacial; // Torna global


/**
 * Tenta confirmar o agendamento usando o slot escolhido (API Final de Booking).
 * * @param {object} slotInfo Os dados do slot escolhido (allocationId, date, categoryCode).
 * @param {object} feeResult Os dados da taxa (totalAmount, currency) obtidos da getFee.
 * @param {object} context Objeto de contexto global (com token, email, vacCode, etc.).
 * @param {string} VFS_BASE_URL URL base da VFS (ex: 'https://visa.vfsglobal.com').
 * @returns {object} Resultado do agendamento (success: boolean, message: string).
 */
async function agendarReservaFinal(VFS_BASE_URL,slotInfo,currentContext) {
    // Endpoints e variáveis principais
    const VFS_BOOK_URL = "https://lift-api.vfsglobal.com/appointment/schedule"; 
    // --- Extração do Contexto (Priorizando feeDetails para taxa) ---
    const { token,email } = apiDetectAuth();
    
    // 🎯 CORREÇÃO 1: Usar feeDetails para os valores de pagamento (mais fresco e confiável)
    const taxa = currentContext.totalAmount;
    const moeda = currentContext.currency;
    const payCode = currentContext.paymentTypeId;
    const allocationId =  slotInfo.allocationId
    // Campos necessários
    const urn = currentContext.groupURN // Assumindo que context.applicantArn é o URN completo
    const centerCode = currentContext.vacCode || 'LUA'; 
    const missionCode = currentContext.missionCode || "bra"; // Mantendo prt como default robusto
   
// --- Payload Final (API 11) - Cópia Fiel do Log do Site ---
const payload = JSON.stringify({
    // Parte 1: Cabeçalho
    "missionCode": "bra",
    "countryCode": "AGO",
    "centerCode": "LUA",
    "loginUser": email, 
    
    // Parte 2: Consensos e IDs
    "CanVFSReachoutToApplicant": true,
    "TnCConsentAndAcceptance": true,
    "allocationId": String(allocationId), 
    "aurn": null,
    "centerCode": "LUA",   
    "countryCode": "AGO", 
    "loginUser": email, 
    "missionCode": "bra",
    "notificationType": "none",
    
    // Parte 3: Objeto de Pagamento (Dentro)
    "paymentdetails": {
        "paymentmode": payCode, // "EMIS"
        "RequestRefNo": "",
        "clientId": "",
        "merchantId": "",
        "amount": Number(taxa),   
        "currency": moeda         
    },
    
    // Parte 4: Campos de Pagamento "Soltos" (Raiz) - OBRIGATÓRIO
    "RequestRefNo": "",
    "amount": Number(taxa),     
    "clientId": "",
    "currency": moeda,           
    "merchantId": "",
    "paymentmode": payCode,      
    "urn": urn                   
});
    
    // --- Headers Padronizados ---
  const headers = {
        "authorize": token,
        "accept": "application/json, text/plain, */*", 
        'clientsource': '',
        "accept-encoding": "gzip, deflate, br, zstd",
        "accept-language": "en-US,en;q=0.9",
        "content-type":'application/json;charset=UTF-8',
        "origin": VFS_BASE_URL, 
        "referer": VFS_BASE_URL + "/appointment/select-vac",
        "route" : 'Ago/en/bra', 
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", 
    };
        
const stepName = "BOOK"
    
    try {
        const response = await fetchDataInjected(
        VFS_BOOK_URL,
        stepName,                 // 2. stepName (String de identificação)
        headers,                  // 3. headers
        'POST',                   // 4. method (String 'POST')
        payload                // 5. body (String JSON preparada)
);
// 1. TRATAMENTO DE ERRO DE NEGÓCIO PELA PROPRIEDADE 'error'
        if (response.error) {
            apiLog(`❌  ERRO AO CONFIRMAR PAGAMENTO (${response.error.code}): ${response.error.description}`, true);
            return null; 
        }

        // 🛑 REMOVER ESTE BLOCO. Se response.error é null, IsAppointmentBooked: true 
        // é o estado de SUCESSO.
        /*
        const IsAppointmentBooked = response.IsAppointmentBooked;
        if (IsAppointmentBooked === true) {
             apiLog(`❌ API 8 ERRO DE NEGÓCIO (CÓDIGO ${response.error.code}): ${response.error.description}`, true);
             return null; 
        }
        */

        // 2. EXTRAÇÃO E LOG COMPLETO
        const RequestRefNo    = response.RequestRefNo;
        const appointmentDate = response.appointmentDate;
        const appointmentTime = response.appointmentTime;
        const payLoad         = response.payLoad;
        const URL             = response.URL;
        const IsAppointmentBooked = response.IsAppointmentBooked; // Adicionar de volta para a verificação de ausência

        // Se IsAppointmentBooked for false, mesmo sem erro, pode ser um estado de falha
        if (!IsAppointmentBooked) {
             apiLog(`❌ FALHA: A API não confirmou o agendamento (IsAppointmentBooked: false).`, true);
             return null;
        }
        
        // Trata a ausência de dados críticos
        if (!RequestRefNo || !appointmentDate || !appointmentTime || !payLoad || !URL) {
              console.log(`❌ datas ausentes `, true);
              return null;
        }

        // 🔑 LOG COMPLETO PARA O PAINEL: Link e Referência
        apiLog(`✅ AGENDAMENTO FEITO PARA DATA: ${appointmentDate} E HORA ${appointmentTime}.`, false)
        
        // 3. RETORNA APENAS OS DADOS CRÍTICOS PARA O CONSUMO
        return { 
            success: true, 
            IsAppointmentBooked: true,
            RequestRefNo: RequestRefNo,
            appointmentDate: appointmentDate,
            appointmentTime: appointmentTime,
            payLoad: payLoad,
            URL: URL,
            payload: response // Opcional: mantém o payload completo
        };

    } catch (e) {
        // 4. CAPTURA DE FALHAS DE REDE/HTTP (Onde estava o ReferenceError de URL)
        const errorStatus = e.status || 'REDE/FATAL';
        apiLog(`❌ API 8 FALHA FATAL (Status: ${errorStatus}). Mensagem: ${e.message}`, true);
        return null; 
    }
}

/**
 * FLUXO UNIFICADO: API 2, 3 e 4
 * Esta função gerencia a transição sem quebrar o contexto.
 */
async function processarFluxoFinalPagamento(VFS_BASE_URL, currentContext, urlCompletaPgto) {
    try {
        apiLog(`🚀 Iniciando fase final para: ${currentContext.nome || 'Solicitante'}`, false);

        // 1. SALVAR CONTEXTO: Garante que a extensão não "esqueça" o usuário após mudar de página
        const dadosParaSalvar = {
            ...currentContext,
            urlOriginal: window.location.href,
            passo: 'AGUARDANDO_PAYMENT_ID'
        };
        localStorage.setItem('vfs_automation_context', JSON.stringify(dadosParaSalvar));

        // 2. EXECUTAR API 2 (PayRequest): 
        // Usamos window.location.href para evitar o erro de CORS e carregar os cookies oficiais.
        apiLog(`1/3. Registrando intenção de pagamento (Navegação Direta)...`, false);
        
        // Em vez de fetch, nós NAVEGAMOS. A extensão deve estar configurada para
        // rodar automaticamente em "online.vfsglobal.com/PG-Component/Payment/PayRequest"
        window.location.href = urlCompletaPgto;

    } catch (e) {
        apiLog(`❌ Erro no disparo do fluxo: ${e.message}`, true);
    }
}

/**
 * FUNÇÃO DE CAPTURA (Deve rodar automaticamente ao carregar a página de erro/sucesso da VFS)
 * Esta parte lê o HTML que você postou acima.
 */
function extrairDadosEConfirmar() {
    const htmlDaPagina = document.documentElement.innerHTML;
    
    // Procura pelo redirectUrl que você encontrou no seu HTML
    const regexLink = /var redirectUrl = '(.*?)'/;
    const match = htmlDaPagina.match(regexLink);

    if (match && match[1]) {
        const urlRetorno = match[1];
        apiLog(`✅ Dados detectados no HTML!`, false);

        // Extrai o ID da URL de retorno: ...RequestRefNo=1038347367...
        const urlParams = new URLSearchParams(urlRetorno.split('?')[1]);
        const refNo = urlParams.get('RequestRefNo');
        
        // API 3 & 4: Monta a URL Final de Confirmação (Sucesso)
        // Se o pagamento fosse real, aqui teríamos o TransactionId. 
        // Para o "Fast Track", tentamos forçar o PaymentStatus=True
        const finalURL = urlRetorno.replace('PaymentStatus=', 'PaymentStatus=True')
                                   .replace('TransactionId=', 'TransactionId=TXN-' + Date.now());

        apiLog(`2/3 e 3/3. Finalizando Agendamento via Fast Track...`, false);
        
        // Redireciona para a confirmação final
        window.location.href = finalURL;
    }
}

/**
 * Orquestra o fluxo COMPLETO de pagamento e confirmação do agendamento VFS,
 * chamando as funções de API separadas em sequência.
 */
async function executarFluxoDePagamentoVFS(VFS_BASE_URL, currentContext, ) {

    // --- SETUP ---
    
    const URL_BASE = currentContext.URL; 
const rawPayLoad = currentContext.payLoad; 

// 🛑 Log de Depuração Crítico:
console.log(`DEBUG FASE 3 | URL: ${URL_BASE} | Payload: ${rawPayLoad ? rawPayLoad.substring(0, 30) + '...' : 'N/A'}`, false);

// Então, a construção da URL (garantindo a codificação):
const encodedPayLoad = encodeURIComponent(rawPayLoad);
const URL_PAY_REQUEST_COMPLETA = `${URL_BASE}?payLoad=${encodedPayLoad}`;

   // ------------------------------------------------------------------------
// 1. CHAMA API 2: Iniciar Pagamento e Obter TransactionId
// ------------------------------------------------------------------------
const payRequest = await iniciarPayRequest(VFS_BASE_URL, currentContext,URL_PAY_REQUEST_COMPLETA );

// ------------------------------------------------------------------------
// 1A. TRATAMENTO DE ERRO FATAL/REDE (Falha na execução da função iniciarPayRequest)
// ------------------------------------------------------------------------
if (!payRequest.success) {
    apiLog(`❌ FALHA FATAL NA CHAMADA PAYREQUEST (API 2): ${payRequest.error.description || 'Erro de execução desconhecido.'}`, true);
    
    return { success: false, message: `Falha na etapa PayRequest (API 2).`, error: payRequest.error };
}
currentContext.initiateResult = payRequest.initiateResult; 
// ------------------------------------------------------------------------
// 1B. TRATAMENTO DE ERRO DE NEGÓCIO DA VFS (Se a API retornou Status 200, mas com um bloco 'error')
// ------------------------------------------------------------------------

// Nota: Assumimos que a resposta bruta da API (payload) está dentro de payRequest.payload
const responseApiPayload = payRequest.payload; 

if (responseApiPayload && responseApiPayload.error && responseApiPayload.error.messageCode) {
    
    const errorCode = responseApiPayload.error.messageCode;
    const errorDescription = responseApiPayload.error.description || "Erro de negócio desconhecido.";

    apiLog(`❌ ERRO DE NEGÓCIO DA API 2 (${errorCode}): ${errorDescription}`, true);
    
    // Retorna a falha, informando o código de erro específico da VFS
    return {
        success: false,
        message: `API 2 PayRequest rejeitada com erro de negócio.`,
        error: {
            code: errorCode,
            description: errorDescription,
            source: "API 2 VFS Response"
        }
    };
}

// ------------------------------------------------------------------------
// 1C. SUCESSO E CONTINUIDADE (A API funcionou e não retornou erro de negócio)
// ------------------------------------------------------------------------


apiLog(`✅ API 2 (PayRequest) concluída com sucesso. Initiate Result obtido.`, false);

    // ------------------------------------------------------------------------
    // 2. CHAMA API 4: Simular Callback e Atualizar Estado para PAGO
    // ------------------------------------------------------------------------
    const callbackResult = await iniciarRedirecionamentoAngolaP2(VFS_BASE_URL,currentContext );
    
    if (!callbackResult.success) {
        // Isso é improvável, pois a função é um window.open, mas é bom manter
        return { success: false, message: `Falha no Callback de Pagamento (API 4).`, error: callbackResult.error };
    }
        currentContext.finalTransactionId = callbackResult.TransactionId;
    const responsePayload = callbackResult.payload; 
      if (responsePayload && responsePayload.error && responsePayload.error.messageCode) {
    
    const errorCode = responsePayload.error.messageCode;
    const errorDescription = responsePayload.error.description || "Erro de negócio desconhecido.";

    apiLog(`❌ ERRO DE NEGÓCIO DA API 2 (${errorCode}): ${errorDescription}`, true);
    
    // Retorna a falha, informando o código de erro específico da VFS
    return {
        success: false,
        message: `API 2 PayRequest rejeitada com erro de negócio.`,
        error: {
            code: errorCode,
            description: errorDescription,
            source: "API 2 VFS Response"
        }
    };
}
         currentContext.finalTransactionId = callbackResult.TransactionId;
    // ------------------------------------------------------------------------
    // 3. CHAMA API 5: Confirmar Agendamento
    // ------------------------------------------------------------------------
    const finalConfirmation = await simularCallbackDePagamento(VFS_BASE_URL, currentContext,);

    if (!finalConfirmation.success) {
        return { success: false, message: `Falha na Confirmação Final (API 5).`, error: finalConfirmation.error };
    }

    apiLog("✅ ORQUESTRAÇÃO COMPLETA: Agendamento Confirmado.", false);

    return { 
        success: true, 
        message: "Agendamento e Pagamento confirmados com sucesso.", 
        details: finalConfirmation.details 
    };
}

/**
 * Confirma a transação final de pagamento usando a Referência de Requisição (RequestRefNo).
 * @param {object} feeDetails Os dados da taxa (amount, currency) do getFee.
 * @param {string} requestReferenceNo A referência gerada pelo sistema VFS/pagamento.
 * @param {string} VFS_BASE_URL URL base da VFS.
 * @param {object} context Contexto global.
 * @returns {object} { success: boolean, RequestRefNo: string, details: object }
 */
async function firmarPagamentoFinal(VFS_BASE_URL, currentContext) {
    const VFS_CONFIRM_PAYMENT_URL = "https://lift-api.vfsglobal.com/payments/confirmappointment"; 
    // --- Extração do Contexto (Priorizando feeDetails para taxa) ---
    const { token,email } = apiDetectAuth();
    
    // 🎯 CORREÇÃO 1: Usar feeDetails para os valores de pagamento (mais fresco e confiável)
    const taxa = currentContext.totalAmount;
    const moeda = currentContext.currency;
    const dnt = "1";
   
    // Campos necessários
    const urn = currentContext.groupURN // Assumindo que context.applicantArn é o URN completo
    const centerCode = currentContext.vacCode || 'LUA'; 
    const loginUser = currentContext.loginUser || email;
    const missionCode = currentContext.missionCode || "bra"; // Mantendo prt como default robusto
    const refer = currentContext.RequestRefNo || "refer"; // Referência de pagamento (pode ser genérica)
    const cultureCode = currentContext.cultureCode;

    // --- Payload ---
    const payload =JSON.stringify({ 
        "amount": taxa, // DINÂMICO (feeDetails)
        "centerCode": centerCode,
        "countryCode": 'AGO',
        "cultureCode": 'en-US',
        "currency": moeda, // DINÂMICO (feeDetailsl
        "isCBankPayment": false,
        "isEMISPayment": true, // Mantido como no original
        "loginUser": email, 
        "missionCode": missionCode,
        "requestReferenceNo": refer, // CRÍTICO
        "urn": urn,
    });
    
    // --- Headers Padronizados ---
  const headers = {
        "authorize": token,
        "accept": "application/json, text/plain, */*", 
        'clientsource': '',
        "accept-language": "pt-BR,pt;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
        "content-type":'application/json;charset=UTF-8',
        "x-dnt-status": "1",
        "origin": VFS_BASE_URL, 
        "referer": VFS_BASE_URL + "/appointment/select-vac",
        "route" : 'ago/en/prt', 
        "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36", 
    };

    const stepName ="CONFIRM_PAYMENT"
    try {
        const response = await fetchDataInjected(
        VFS_CONFIRM_PAYMENT_URL, 
        stepName,                 // 2. stepName (String de identificação)
        headers,                  // 3. headers
        'POST',                   // 4. method (String 'POST')
        payload                // 5. body (String JSON preparada)
);

// 1. TRATAMENTO DE ERRO DE NEGÓCIO PELA PROPRIEDADE 'error'
        if (response.error) {
            apiLog(`❌  ERRO AO CONFIRMAR PAGAMENTO (${response.error.code}): ${response.error.description}`, true);
            return null; 
        }

        // 🛑 REMOVER ESTE BLOCO. Se response.error é null, IsAppointmentBooked: true 
        // é o estado de SUCESSO.
        /*
        const IsAppointmentBooked = response.IsAppointmentBooked;
        if (IsAppointmentBooked === true) {
             apiLog(`❌ API 8 ERRO DE NEGÓCIO (CÓDIGO ${response.error.code}): ${response.error.description}`, true);
             return null; 
        }
        */

        // 2. EXTRAÇÃO E LOG COMPLETO
        const TransactionId    = response.TransactionId;
        const TransactionDate = response.TransactionDate;
        const IsAppointmentBooked = response.IsAppointmentBooked; // Adicionar de volta para a verificação de ausência

        // Se IsAppointmentBooked for false, mesmo sem erro, pode ser um estado de falha
        if (!IsAppointmentBooked) {
             apiLog(`❌ FALHA: A API não confirmou o agendamento (IsAppointmentBooked: false).`, true);
             return null;
        }
        
        // Trata a ausência de dados críticos
        if (!TransactionId || !appointmentDate || !appointmentTime || !payLoad || !URL) {
              apiLog(`❌ datas ausentes `, true);
              return null;
        }

        // 🔑 LOG COMPLETO PARA O PAINEL: Link e Referência
        apiLog(`✅ REFERÊNCIA DO PAGAMENTO: ${TransactionId} DATA DA TRANSAÇÃO: ${TransactionDate}.`, false)
        
        // 3. RETORNA APENAS OS DADOS CRÍTICOS PARA O CONSUMO
        return { 
            success: true, 
            IsAppointmentBooked: true,
          TransactionId: TransactionId,
            TransactionDate: TransactionDate,
            payLoad: response.payLoad, // Preservando o payload completo
        };

    } catch (e) {
        // 4. CAPTURA DE FALHAS DE REDE/HTTP (Onde estava o ReferenceError de URL)
        const errorStatus = e.status || 'REDE/FATAL';
        apiLog(`❌ API 8 FALHA FATAL (Status: ${errorStatus}). Mensagem: ${e.message}`, true);
        return null; 
    }
}
const DELAY_ENTRE_TENTATIVAS_MS = 2000;
/**
 * Orquestra o agendamento: Reserva o slot e processa o pagamento/confirmação.
 */
async function confirmarFacialEAgendar(VFS_BASE_URL, currentContext) {
    
    if (!currentContext || !currentContext.finalBookingSlotsList || currentContext.finalBookingSlotsList.length === 0) {
        apiLog(`❌ ERRO: Contexto ou Slots ausentes.`, true);
        return "❌ FALHA CRÍTICA";
    }

    const applicantName = currentContext.firstName || 'Valdemiro';
    const slots = currentContext.finalBookingSlotsList;
    const maxRetries = 3; 
    
    // Variável para controlar se é a primeiríssima tentativa
    let primeiraTentativaGeral = true;

    apiLog(`[${applicantName}] 🚨 Iniciando ROTAÇÃO de Reserva (Máx: ${maxRetries} voltas)...`, true);

    for (let volta = 1; volta <= maxRetries; volta++) {
        apiLog(`[${applicantName}] 🟦 VOLTA ${volta}/${maxRetries} na lista de slots...`, true);

        for (const slotInfo of slots) {
            
            // APLICA DELAY: Se não for a primeira tentativa de todas, aguarda o delay
            if (!primeiraTentativaGeral) {
                apiLog(`[${applicantName}] ⏳ Aguardando ${DELAY_ENTRE_TENTATIVAS_MS}ms para o próximo slot...`, false);
                await new Promise(resolve => setTimeout(resolve, DELAY_ENTRE_TENTATIVAS_MS));
            }
            
            primeiraTentativaGeral = false; // Após passar pelo primeiro, todos os outros terão delay

            apiLog(`[${applicantName}] 🔄 Tentando slot: ${slotInfo.slotTime}...`, false);

            try {
                // --- ETAPA 1: RESERVA FINAL (API 11) ---
                const reserva = await agendarReservaFinal(VFS_BASE_URL, slotInfo, currentContext);

                if (!reserva || !reserva.success) {
                    apiLog(`[${applicantName}] ❌ Slot ocupado (${slotInfo.slotTime}). Próximo...`, false);
                    continue; 
                }

                // ... (Atualiza o contexto com RequestRefNo, payLoad, etc.) ...
                currentContext.RequestRefNo = reserva.RequestRefNo;
                currentContext.appointmentDate = reserva.appointmentDate;
                currentContext.appointmentTime = reserva.appointmentTime;
                currentContext.payLoad = reserva.payLoad;
                currentContext.URL = reserva.URL;

                localStorage.setItem('vfs_automation_context', JSON.stringify(currentContext));

              const resultadoFinal = await processarRedirecionamentoPagamento(reserva, currentContext);

if (resultadoFinal && resultadoFinal.success) {
    apiLog(`[${applicantName}] 🏆 SUCESSO TOTAL! Agendado com TXN: ${resultadoFinal.txnId}`, true);
    localStorage.removeItem('vfs_automation_context');
      return { 
            success: true, 
            message: `✅ SUCESSO! Reservado: ${slotInfo.date}.`,
            AgendamentoData: slotInfo.date,
            TransactionId : resultadoFinal.txnId
        };
    
} else {
    apiLog(`[${applicantName}] ❌ Falha no processo: ${resultadoFinal.error}`, true);
    return `⚠️ Falha na finalização. Verifique seu painel VFS.`;
}

            } catch (err) {
                apiLog(`[${applicantName}] 💥 Erro no slot ${slotInfo.slotTime}: ${err.message}`, true);
            }
        }

        // Se terminou a lista e não é a última volta, aguarda um pouco antes de recomeçar
        if (volta < maxRetries) {
            const waitTime = 3000; // 3 segundos entre as voltas
            apiLog(`[${applicantName}] ⏳ Lista percorrida. Reiniciando em ${waitTime/1000}s...`, false);
            await new Promise(resolve => setTimeout(resolve, waitTime));
        }
    }

    apiLog(`[${applicantName}] ❌ ROTAÇÃO ESGOTADA após ${maxRetries} tentativas completas.`, true);
    return "❌ ROTAÇÃO ESGOTADA.";
}

window.confirmarFacialEAgendar = confirmarFacialEAgendar;





// --- CICLO PRINCIPAL (COM CARGA ROBUSTA DE CONTEXTO) ---
async function runSimpleAttack(applicant, globalVfsContext, slotCheckResult) {
    
    // Assumimos que 'applicant' é o objeto único do requerente.
    if (!applicant || Object.keys(applicant).length === 0) {
        apiLog("Nenhum requerente para atacar.", false);
        return { success: false, messageCode: "FALHA_SEM_REQUERENTES" };
    }

    // Usamos 'applicantData' como o objeto de estado mutável
    const applicantData = applicant;
    const applicantName = applicantData.firstName;
    const attackStartTime = Date.now();
    
    let finalSuccess = false;
    let messageCode = ''; 
    
    // 1. Processo de Obtenção de ARN/URN (API 6)
    const result = await processApplicantFlow(applicantData, globalVfsContext, slotCheckResult); 
    
    // 🚨 DEBUG RETORNO BRUTO API 6 (Mantido para diagnóstico)
    // apiLog(`[${applicantName}] 🚨 DEBUG RETORNO BRUTO API 6: ${JSON.stringify(result.data)}`, true); 

    // --- LÓGICA DE TRATAMENTO DE RESULTADO DA API 6 ---
    if (result.messageCode === 'URN_SUCCESS' || result.messageCode === 'SUCESSO_ID_OBTIDO_COM_ALERTA') {
    
    // 1. Log de avanço
    //apiLog(`[${applicantName}] 🎉 SUCESSO: Avançando para o Fluxo de Segurança/Pagamento.`, true);

    // 2. 🔑 REMOÇÃO DO SALVAMENTO CRÍTICO DOS IDs (Já está salvo em processApplicantFlow)
    // REMOVER: applicantData.groupURN = result.data?.groupURN || null; 
    // REMOVER: applicantData.individualARN = result.data?.individualARN || null; 
    // REMOVER: applicantData.principalId = result.data?.principalId || null;
    
    // 3. Log de Confirmação (Pode usar os dados diretamente, pois foram salvos)
   // apiLog(`[${applicantName}] 🎉 Applicant Criado: URN ${applicantData.principalId}, ARN ${applicantData.individualARN}.`, true);
   // apiLog(`[${applicantName}] ✅ URN/ARN obtido. Prosseguindo para o Fast Track.`, false);

    // 4. Chamada da Função Seguinte (Os IDs já estão em applicantData)
    const resultadoFastTrack = await executarFluxoComRetry(
        applicantData,
        globalVfsContext,
    );

        // 5. TRATAMENTO DO RESULTADO DO FAST TRACK
        if (resultadoFastTrack.success) { 
            
            // SUCESSO na parte automatizada (Chegou à Pausa)
            apiLog(`🎉 ATAQUE SIMPLES BEM-SUCEDIDO: Pausa iniciada para Confirmação Final (Facial/Slot) para ${applicantName}.`, true);
            
            // 🚨 SALVAMENTO DO CONTEXTO DE SESSÃO
            await saveVfsContext(globalVfsContext);
          //  apiLog("💾 Contexto VFS salvo com sucesso após obtenção do URN/ARN.", false);
            
            finalSuccess = true;
            messageCode = 'SUCESSO_PAUSA_FAST_TRACK'; 
            
        } else {
            // FALHA no Fast Track (OTP falhou, ou API 7/8 falhou)
            apiLog(`❌ Falha na Confirmação (Fast Track) para ${applicantName}. Código: ${resultadoFastTrack.messageCode}`, true);
            messageCode = resultadoFastTrack.messageCode || 'FALHA_FAST_TRACK_SEM_CODIGO';
        }

    } else if (result.messageCode === 'RETRY_SONDA' || result.messageCode === 'RETRY_NET_ERROR') {
        // FALHA RECUPERÁVEL na API 6
        messageCode = result.messageCode;
        apiLog(`🐌 API 6 Retornou Retryable Error (${messageCode}). Tentará novamente.`, true);
        
    } else {
        // FALHA IRRECUPERÁVEL na API 6
        messageCode = result.messageCode || 'FALHA_API_6_FATAL';
        apiLog(`❌ Falha Irrecuperável na API 6: ${messageCode}.`, true);
    }
    
    // --- CONSOLIDAÇÃO FINAL ---
    const duration = (Date.now() - attackStartTime) / 1000;
    apiLog(`🏁 ATAQUE SIMPLES CONCLUÍDO. Sucesso: ${finalSuccess}. Duração: ${duration.toFixed(2)}s.`, true);

    return { success: finalSuccess, messageCode: messageCode };
}

// --- Fluxo Simples de Ataque (processApplicantFlow) ---
async function processApplicantFlow(applicantData, globalVfsContext, slotCheckResult) {
    const applicantName = applicantData.firstName;
    let preExistingId = applicantData.arn || null; 
    const REGEX_URN_SUCCESS = /^XYZ\d+$/; // Regex para URN (Grupo)

    // --- 1. Pré-Verificações ---
    if (!slotCheckResult || !slotCheckResult.slotId) {
        apiLog(`[${applicantName}] ❌ Erro: Slot ID de vaga ausente. Impossível chamar API 6.`, false);
        return defaultReturn(false, null, 'FALHA_DADOS_SLOT');
    }

    if (preExistingId) {
     //   apiLog(`[${applicantName}] ✅ ARN/URN já existe: ${preExistingId}.`, false);
        return defaultReturn(true, { principalId: preExistingId }, 'ARN_EXISTENTE');
    }
    
    //apiLog(`[${applicantName}] Tentativa API 6. Enviando requisição...`, false);
    
    let apiResponse = null;

    try {
        apiResponse = await createApplicantSimple(applicantData, globalVfsContext, slotCheckResult); 
    } catch (e) {
        const safeErrorMessage = (e instanceof Error) 
            ? e.message 
            : (String(e) || 'Erro de conexão/infraestrutura desconhecido');
        apiLog(`[${applicantName}] 🛑 ERRO DE INFRAESTRUTURA/CONEXÃO: ${safeErrorMessage}`, true, 'error');
        return defaultReturn(false, null, 'RETRY_NET_ERROR');
    }

    if (!apiResponse) return defaultReturn(false, null, 'FALHA_GENERICA');
    
    // 🚨 LOG DE DIAGNÓSTICO CRÍTICO
  //  apiLog(`[${applicantName}] 🚨 DEBUG RAW RESPONSE KEYS: ${Object.keys(apiResponse).join(', ')}`, true);
    //apiLog(`[${applicantName}] 🚨 DEBUG JSON DATA (Se existir): ${JSON.stringify(apiResponse.data || apiResponse.applicantList || {})}`, true);

    // 🔑 DEFINIÇÃO DO CÓDIGO DE ERRO (Para ser usado na lógica de erro)
    const errorCode = apiResponse?.error?.code || null;
    
    // --- 2. Extração de IDs (Super-Robusta) ---
    const groupURN = apiResponse.urn 
        || apiResponse.groupURN 
        || apiResponse.applicantId 
        || apiResponse.data?.groupURN 
        || apiResponse.data?.applicantId 
        || null; 

    const individualARN = apiResponse.applicantList?.[0]?.arn 
        || apiResponse.applicantARN 
        || apiResponse.data?.applicantList?.[0]?.arn 
        || apiResponse.data?.applicantARN 
        || null; 
        
    const isUrnValid = groupURN && REGEX_URN_SUCCESS.test(groupURN) && individualARN;

    if (isUrnValid) {
    // 🚨 NOVO: SALVAMENTO DIRETO NO OBJETO MÚTAVEL (applicantData)
    applicantData.groupURN = groupURN;      
    applicantData.individualARN = individualARN; 
    applicantData.principalId = groupURN; 

    // O returnData agora é apenas informativo (ou pode ser removido se não for usado para mais nada)
    const returnData = {
        groupURN: groupURN, 
        individualARN: individualARN,
        principalId: groupURN     
    };
    
    // ... restante do código de log e return ...
   // apiLog(`[${applicantName}] 🎉 Applicant Criado: URN ${groupURN}, ARN ${individualARN}.`, true);
    
    const message = apiResponse.success ? 'URN_SUCCESS' : 'SUCESSO_ID_OBTIDO_COM_ALERTA';
    
    // Retorna true, mas o salvamento já foi feito
    return defaultReturn(true, returnData, message); 
}

    // --- 4. Lógica de Erro (Só se não houver URN/ARN) ---

    // 2. Modo Retry (Erros temporários/Servidor)
    if (errorCode && (String(errorCode) === '1040' ||String(errorCode) === '422'||String(errorCode) === '1035' || String(errorCode).includes('not slot disponible') || String(errorCode).startsWith('HTTP_'))) {
        apiLog(`[${applicantName}] 🔄 Servidor Ocupado ou Protegido. Code: ${errorCode}.`, true);
        return defaultReturn(false, null, 'RETRY_SONDA');
    }

    // 3. Falha Genérica
    apiLog(`[${applicantName}] ❌ Falha na API 6 (Criação de Requerente): Code ${errorCode || 'DESCONHECIDO'}.`, false);
    return defaultReturn(false, null, 'FALHA_GENERICA');
}
// Função para iniciar as outras APIs para um candidato já com ARN/URN
// (Mantida sem alterações)
async function runFastTrackForApplicant(applicantData, globalContext) {
    const { applicantArn } = applicantData; 
    const VFS_BASE_URL = "https://visa.vfsglobal.com";

    // Checagem crítica: Se o candidato não tem ARN/URN, aborta o fluxo
    if (!applicantArn) {
        apiLog(`[${applicantData.firstName}] ❌ Fluxo de API 6 não obteve ARN/URN. Abortando.`, true);
        return defaultReturn(false);
    }

    // Criação do contexto estendido para as APIs
    const currentContext = {
        ...globalContext,
        ...applicantData,
        applicantArn: applicantArn,
        urn: applicantArn.split('/')[0], 
    };
    
    // Assumimos que a função attachDtCookieToContext existe e funciona
    try {
        // Passo 1: Checar se o "dtCookie" está presente
        const hasCookie = await attachDtCookieToContext(currentContext);
        if (!hasCookie) {
            apiLog(`[${applicantData.firstName}] ❌ Falha crítica: dtCookie ausente. Abortando.`, true);
            return defaultReturn(false);
        }

        // =======================================================
        // Passo 2: API 7 - TAXAS (getFee)
        // =======================================================
        const feeResponse = await getFee(VFS_BASE_URL, currentContext, globalContext.token, applicantArn);
        
        // Trata a falha, mas permite a continuação se for o erro 1003 (ARN/URN de teste)
        if (!feeResponse || (feeResponse.error && feeResponse.error.code !== 1003)) {
            apiLog(`[${applicantData.firstName}] ❌ API 7 (getFee) falhou. Abortando.`, true);
            return defaultReturn(false);
        }
        
        currentContext.feeDetails = feeResponse; // Armazenando as taxas/resposta no contexto

        // =======================================================
        // Passo 3: API 8 - IDNFY TOKEN (getIdnfyToken)
        // =======================================================
        const idnfyTokenResponse = await getIdnfyToken(
            VFS_BASE_URL, 
            currentContext, 
            globalContext.token, 
            applicantArn
        );
    
        if (!idnfyTokenResponse || !idnfyTokenResponse.redirectUrl) {
            apiLog(`[${applicantData.firstName}] ❌ API 8 (getIdnfyToken) falhou ou 'redirectUrl' ausente. Abortando.`, true);
            return defaultReturn(false);
        }
        
        // --- Extração do IDNFY Token (Mantido) ---
        let idnfyToken = null;
        const redirectUrlString = idnfyTokenResponse.redirectUrl;
        
        try {
            const redirectUrlObj = new URL(redirectUrlString);
            idnfyToken = redirectUrlObj.searchParams.get('token'); 
        } catch (e) {
            apiLog(`[${applicantData.firstName}] ❌ Falha ao parsear 'redirectUrl' para extrair o token. Erro: ${e.message}`, true);
            return defaultReturn(false);
        }

        currentContext.idnfyToken = idnfyToken; 
        
        // --- Abertura da Nova Aba (Facial) ---
        const finalRedirectUrl = idnfyTokenResponse.redirectUrl;
        apiLog(`[${applicantData.firstName}] 🟢 ABRINDO NOVA ABA: ${finalRedirectUrl}...`, true);
        window.open(finalRedirectUrl, '_blank');
        
        // =======================================================
        // 🎯 TRANSIÇÃO CRÍTICA: Chamada ao Orquestrador de OTP
        // O controle do restante do fluxo é passado para esta função.
        // =======================================================
        apiLog(`[${applicantData.firstName}] 🚀 Transição: Iniciando Fluxo OTP/Agendamento...`, true);
        
        // Chamamos a função que agora orquestra o OTP, Calendário, Slot, e o Botão.
        const finalResult = await iniciarFluxoOTP(currentContext);

        // O resultado final será o que for retornado por iniciarFluxoOTP (sucesso ou falha)
        return finalResult; 

    } catch (e) {
        apiLog(`[${applicantData.firstName}] ❌ ERRO FATAL no fluxo API 7-8. Erro: ${e.message}`, true);
        return defaultReturn(false);
    }
}

function abrirTunelFacialIndependente(facialUrl) {
   // apiLog(`📸 [SISTEMA] Abrindo Túnel Facial (Alvo: idnvui.vfsglobal.com)...`, true);

    const largura = 1000; 
    const altura = 750;
    const esquerda = (window.screen.width / 2) - (largura / 2);
    const windowFeatures = `width=${largura},height=${altura},left=${esquerda},top=0,scrollbars=yes,resizable=yes`;

    const popupFacial = window.open(facialUrl, 'TunelFacialVFS', windowFeatures);

    if (popupFacial) {
        popupFacial.focus();
        const momentoAbertura = Date.now();

        const monitorSeguranca = setInterval(() => {
            try {
                // 1. Verificação de existência
                if (!popupFacial || popupFacial.closed) {
                    clearInterval(monitorSeguranca);
                   // apiLog("✅ Janela Facial encerrada.", false);
                    return;
                }

                // 2. Tenta capturar a URL
                let currentUrl = "";
                try {
                    currentUrl = popupFacial.location.href;
                } catch (corsError) {
                    // 🛡️ LÓGICA DE MANUTENÇÃO:
                    // Se der erro de CORS, o popup está em um subdomínio diferente (idnvui).
                    // Como idnvui é o nosso ALVO, se der CORS, nós MANTEMOS aberto.
                    return; 
                }

                // 3. Se NÃO der erro de CORS, conseguimos ler a URL.
                // Isso significa que o popup está no MESMO domínio da página principal (visa.vfsglobal.com).
                if (currentUrl && currentUrl !== "about:blank") {
                    
                    // Se já passou o tempo de carregamento e voltamos ao domínio principal ou outro...
                    if (Date.now() - momentoAbertura > 10000) {
                        // Se a URL lida NÃO contém o nosso alvo específico
                        if (!currentUrl.includes("idnvui")) {
                        //    apiLog("🛡️ Alvo 'idnvui' perdido. Fechando popup por segurança.", true);
                            popupFacial.close();
                            clearInterval(monitorSeguranca);
                        }
                    }
                }

            } catch (err) {
                // Erro genérico de monitoramento
            }
        }, 3000); 
    }
}


/**
 * Orquestra o fluxo Fast Track, gerencia o estado (lastSuccessfulStep) e oferece Retry.
 * @param {object} applicantData do solicitante.
 * @param {object} globalContext Contexto global.
 * @param {string} startStep O ponto de onde recomeçar (para retry).
 */
async function executarFluxoComRetry( applicantData, globalVfsContext, startStep = FLOW_STEPS.START) {
 let lastSuccessfulStep = startStep;
    let currentStep = startStep; 
    // Variáveis auxiliares (Removida a redefinição de defaultReturn e apiLog)
    const applicantName = applicantData.firstName;
    const globalContext = globalVfsContext;
    const VFS_BASE_URL = "https://visa.vfsglobal.com";
    const groupURN = applicantData.groupURN;
    const individualARN = applicantData.individualARN;
    // 🛑 VERIFICAÇÃO DE ID PRINCIPAL (groupURN é o ID crucial)
    //if (!applicantData.groupURN) {
      //  apiLog(`[${applicantName}] ❌ ID Principal (groupURN) ausente. Abortando.`, true, 'error');
        // Usamos defaultReturn GLOBAL com messageCode explícito
        //return defaultReturn(false, null, 'FALHA_ID_FAST_TRACK');
   // }

    // Variável para armazenar o contexto atualizado (não há mais currentContext = {...} no início)
    let currentContext = { 
        ...globalVfsContext,
        ... applicantData,
        finalBookingSlotsList: [] 
        
    };

    
    // 🛑 LÓGICA DE RETOMADA DE FLUXO (Se startStep é o padrão)
    if (startStep === FLOW_STEPS.START && groupURN) {
        const savedState = loadStatus(groupURN);
        if (savedState) {
            currentContext = savedState.context;
            currentStep = savedState.currentStep; 
            lastSuccessfulStep = savedState.lastSuccessfulStep; 
            
            apiLog(`[${applicantName}] 🔄 RETOMANDO FLUXO do passo: ${currentStep}. Último sucesso: ${lastSuccessfulStep}`, true, 'info');

            // 💡 RE-INJEÇÃO DE BOTÕES TEMPORÁRIOS
            // Chamamos as funções de injeção sempre que entramos no estado de pausa crítica.
            // O próprio código da injeção deve checar se o botão já existe.



            if (currentStep === FLOW_STEPS.AWAITING_CONFIRMATION) {
                 await window.injetarBotaoConfirmacaoFinal(applicantName, currentContext);
                 // Adicionar lógica de re-injeção para o botão OTP se for o caso
            }
        }
    }



   try {
        // --- LOOP PRINCIPAL ---
        let loopLimit = 30; 
        while (currentStep !== FLOW_STEPS.COMPLETE && loopLimit > 0) {
            loopLimit--;

            switch (currentStep) {
                
                case FLOW_STEPS.START:
                  //   apiLog(`[${applicantName}] 🔄 Iniciando passo START: APIs 7 (getFee) e 8 (getIdnfyToken)...`, true, 'info');

    // =======================================================
    // 1. CHAMADA API 7 (getFee)
    // =======================================================
    const feeResult = await window.getFee(VFS_BASE_URL,currentContext);
    
    // 💥 CORREÇÃO CRÍTICA NA VERIFICAÇÃO DE FALHA:
    // Verifica se getFee retornou null (falha HTTP/Negócio) OU se o objeto retornado não possui o totalamount (dados inválidos/sucesso incompleto).
    if (!feeResult || typeof feeResult.totalamount === 'undefined') {
        throw new Error("API 7 (getFee) falhou (getFee retornou null ou TotalAmount ausente).");
    }
    
    // 2. ARMAZENAMENTO NO CONTEXTO (CORRIGIDO)
    lastSuccessfulStep = FLOW_STEPS.API_7;
    
    // O feeResult JÁ É o objeto de dados de taxa, não está dentro de uma propriedade 'feeData'.
    currentContext.dadosTaxa = feeResult; 
    currentContext.totalAmount = feeResult.totalamount; 
    
    // Verificação para acessar feeDetails de forma segura
    const feeDetails = feeResult.feeDetails; // Acessando diretamente
    if (Array.isArray(feeDetails) && feeDetails.length > 0) {
        currentContext.currency = feeDetails[0].currency;
    } else {
       window.apiLog(`[${applicantName}] ⚠️ Aviso: Não foi possível extrair a moeda de feeDetails. Usando N/A.`, false, 'warning');
        currentContext.currency = 'N/A';
    }

  

                     // O fluxo avança para OTP, conforme o mapa.
                     currentStep = FLOW_STEPS.OTP_INIT;
                     saveStatus(currentContext, FLOW_STEPS.OTP_INIT, lastSuccessfulStep);
                     break;
                    
                case FLOW_STEPS.OTP_INIT:
                    window.apiLog(`[${applicantName}] 🔄 Iniciando validação OTP...`, true, 'info');
                    
                    const otpResult = await window.iniciarFluxoOTP( VFS_BASE_URL,currentContext); 
                    if (!otpResult.success) throw new Error("Validação OTP falhou.");
                    
                    if (otpResult && otpResult.contextUpdates) {
                        Object.assign(currentContext, otpResult.contextUpdates);
                    }

                    currentStep = FLOW_STEPS.SLOT_ANALYSIS;
                    saveStatus(currentContext, FLOW_STEPS.SLOT_ANALYSIS, lastSuccessfulStep);
                    lastSuccessfulStep = FLOW_STEPS.OTP_INIT; 
                    break;

                case FLOW_STEPS.SLOT_ANALYSIS:
                    // --- 3. ANÁLISE E BUSCA DE SLOTS (Pode entrar em loop aqui) ---
                    window.apiLog(`[${applicantName}] 📅 Iniciando busca de Slot (Ciclo #${30 - loopLimit})...`, true, 'info');
                    
                    const slotResult = await window.gerenciarSlot( VFS_BASE_URL,currentContext);
                    
                    if (!slotResult.success) {
                        throw new Error("Gerenciamento de Slot falhou na API.");
                    }
                    
                    // --- CORREÇÃO DE LEITURA DO SLOT ---
                    const slotsList = slotResult.finalBookingSlotsList || [];
                    currentContext.finalBookingSlotsList = slotsList; // Atualiza contexto para uso futuro
                    
                    if (slotsList.length > 0) {
                        
                        window.apiLog(`[${applicantName}] 🎉 Slot(s) encontrado(s): ${slotsList.length}. Prosseguindo para Confirmação.`, true, 'success');
                        currentStep = FLOW_STEPS.AWAITING_CONFIRMATION; 
                        
                    } else {
                        
                        window.apiLog(`[${applicantName}] ⚠️ Gerenciamento de Slot bem-sucedido, mas nenhuma vaga final foi selecionada. Aguardando 5s para retentar...`, true, 'warning');
                     saveStatus(currentContext, FLOW_STEPS.SLOT_ANALYSIS, lastSuccessfulStep);
                        await new Promise(r => setTimeout(r, 5000)); 
                        currentStep =  FLOW_STEPS.SLOT_ANALYSIS; // Volta para este mesmo estado para retentar a busca
                    }
                      currentStep = FLOW_STEPS.SUBMIT_AGENDAMENTO_FINAL;
                    saveStatus(currentContext, FLOW_STEPS.SUBMIT_AGENDAMENTO_FINAL, lastSuccessfulStep);
                    lastSuccessfulStep = FLOW_STEPS.SLOT_ANALYSIS; 
                    break;




                case FLOW_STEPS.SUBMIT_AGENDAMENTO_FINAL:
                    // ... (Submissão Final) ...
                    window.apiLog(`[${applicantName}] 🏃‍♀️ Confirmação recebida. Iniciando pagamento final.`, true, 'info');
                    const finalResult = await window.confirmarFacialEAgendar( VFS_BASE_URL,currentContext);
                    
                    if (finalResult.success) {
                        currentStep = FLOW_STEPS.COMPLETE;
                        saveStatus(currentContext, FLOW_STEPS.COMPLETE, lastSuccessfulStep);
                    } else {
                        // Não faz um throw para usar handleFailure com o resultado explícito
                        return handleFailure('Agendamento Final Rejeitado', finalResult); 
                    }
                    lastSuccessfulStep = FLOW_STEPS.SUBMIT_AGENDAMENTO_FINAL;
                    break;
                    
              case FLOW_STEPS.COMPLETE:
                    clearStatus(groupURN);
                   window.apiLog(`[${applicantName}] 🏁 Processo Finalizado com Sucesso.`, true, 'success');
                    return defaultReturn(true, { lastSuccessfulStep: currentStep }, 'SUCESSO_COMPLETO');
                    
                default:
                    throw new Error(`Estado desconhecido: ${currentStep}`);
            }
        } 
        
        if (loopLimit <= 0) {
            throw new Error("Loop de automação esgotou o limite de tentativas.");
        }
        
        return defaultReturn(true, { lastSuccessfulStep: currentStep }, 'SUCESSO_FAST_TRACK'); 

    } catch (e) {
        const errorMessage = e.message || 'Erro de Referência Desconhecido';
        
        // 🚨 LOGS DE DEPURAÇÃO PARA VER SE CHEGA AQUI
        console.log(`[${applicantName}] ❌ Falha geral capturada no Fast Track: ${errorMessage}`, true, 'error');
        window.apiLog(`[${applicantName}] 🚨 ERRO CAPTURADO NO TRY/CATCH: ${errorMessage}`, true, 'error');
        console.log(`[${applicantName}] 🚨 OBJETO DE ERRO: ${JSON.stringify(e)}`, true, 'error');
         const errorStep = currentStep; // O passo que falhou
        saveStatus(currentContext, errorStep,errorMessage,globalVfsContext,e,applicantName, lastSuccessfulStep);
        // 🔑 CHAMA A FUNÇÃO EXTERNA handleFailure, PASSANDO TODOS OS ARGUMENTOS
        return handleFailure(
            applicantName, 
            lastSuccessfulStep, 
            errorMessage, 
            e, // O objeto de erro capturado
            applicantData, 
            globalVfsContext, 
            executarFluxoComRetry // Passa a si mesma para o callback de retry
        ); 
    }
}


// 💥 CORREÇÃO CRÍTICA: EXPOR A FUNÇÃO GLOBALMENTE PARA QUE O BOTÃO DE CONTROLE POSSA CHAMÁ-LA
window.executarFluxoComRetry = executarFluxoComRetry;
window.getFee = getFee;
window.getIdnfyToken = getIdnfyToken;
 window.iniciarFluxoOTP = iniciarFluxoOTP;
 window.gerenciarSlot = gerenciarSlot;
  window.injetarBotaoConfirmacaoFinal = injetarBotaoConfirmacaoFinal


// Constante para prefixar as chaves no sessionStorage
const STORAGE_KEY_PREFIX = 'vfs_flow_status_';

/**
 * 🔑 Retorna a chave de armazenamento única para o requerente.
 * @param {string} groupURN O ID principal do requerente.
 * @returns {string} Chave de armazenamento.
 */
function getFlowKey(groupURN) {
    return STORAGE_KEY_PREFIX + groupURN;
}

/**
 * 💾 Salva o estado atual do fluxo de automação (contexto, logs e passo) no sessionStorage.
 * Este é o ponto central de persistência.
 *
 * @param {object} context O currentContext completo e atualizado (incluindo tokens, dadosTaxa).
 * @param {string} currentStep O próximo passo a ser executado no loop.
 * @param {string} lastSuccessfulStep O último passo concluído com sucesso (ponto de repetição).
 */
function saveStatus(context, currentStep, lastSuccessfulStep) {
    if (!context.groupURN) {
        console.error("Não é possível salvar o estado: groupURN ausente.");
        return;
    }

    // 🛑 O estado dos botões temporários (OTP, Confirmação Facial) é implicitamente salvo aqui.
    // A presença de tokens no 'context' (ex: idnfyToken, otpTokenStatus) e a variável 'currentStep'
    // indicam qual botão deve ser reinjetado na função de carregamento.
    
    // Assumimos que a função global 'getFullLogHtml' existe para pegar o HTML do log
    const logs = window.getFullLogHtml ? window.getFullLogHtml() : ''; 

    const stateToSave = {
        context: context,
        currentStep: currentStep,
        lastSuccessfulStep: lastSuccessfulStep,
        logs: logs, // Salvamos o HTML dos logs
        timestamp: new Date().toISOString(),
    };

    try {
        const key = getFlowKey(context.groupURN);
        sessionStorage.setItem(key, JSON.stringify(stateToSave));
        // console.log(`[${context.firstName}] ✅ Estado salvo: ${lastSuccessfulStep}`);
    } catch (e) {
        console.error("Erro ao salvar o estado no sessionStorage:", e);
    }
}

/**
 * ♻️ Carrega o estado salvo no sessionStorage para retomar o fluxo.
 *
 * @param {string} groupURN O ID principal do requerente.
 * @returns {object|null} O estado salvo (com context, currentStep, logs) ou null.
 */
function loadStatus(groupURN) {
    try {
        const key = getFlowKey(groupURN);
        const savedState = sessionStorage.getItem(key);
        if (savedState) {
            const state = JSON.parse(savedState);
            console.log(`✅ Estado recuperado. Último passo: ${state.lastSuccessfulStep}`);
            
            // Re-injetar logs na UI
            if (state.logs && window.restoreLogHtml) {
                window.restoreLogHtml(state.logs);
            }
            
            return state;
        }
    } catch (e) {
        console.error("Erro ao carregar o estado do sessionStorage:", e);
    }
    return null;
}
window.loadStatus = loadStatus;
/**
 * 🗑️ Limpa o estado salvo (Usar após sucesso completo ou falha fatal).
 * @param {string} groupURN O ID principal do requerente.
 */
function clearStatus(groupURN) {
    if (groupURN) {
        sessionStorage.removeItem(getFlowKey(groupURN));
        console.log(`Estado para ${groupURN} limpo.`);
    }
}


// Constantes para os IDs dos botões (para não usar strings mágicas)
const RETRY_BUTTON_ID = 'btnRetryFlow';
const START_BUTTON_ID = 'btnStartFlow';

/**
 * 1. Função auxiliar para renderizar o botão de Retomada/Retry.
 * (Substitui o botão de Iniciar padrão)
 * @param {object} applicantData Dados do requerente.
 * @param {object} savedState O estado recuperado (contém currentStep e logs).
 */
function renderizarBotaoRetomada(applicantData, savedState) {
    const container = document.getElementById('bookingHeaderButtons');
    const stepToResume = savedState.currentStep;
    const applicantName = applicantData.firstName;
    
    // Garantir que o container existe
    if (!container) {
        window.apiLog(`[RENDER] ❌ Contêiner de botões de cabeçalho 'bookingHeaderButtons' não encontrado.`, true, 'error');
        return;
    }

    // 1. Limpa o contêiner (remove o botão "Iniciar" padrão, se existir)
    container.innerHTML = '';
    
    // 2. Injeta o HTML do botão de Retomada
    const buttonHtml = `
        <button id="${RETRY_BUTTON_ID}" 
            data-start-step="${stepToResume}"
            data-group-urn="${applicantData.groupURN}"
            style="${BASE_PRIMARY_BUTTON_STYLE.replace('#7e5c13,#d4af37', '#137e5c,#37d4af')} 
                   width: auto; padding: 10px 20px; font-size: 16px;">
            ▶ Retomar Automação: ${applicantName} (Passo: ${stepToResume.replace(/_/g, ' ')})
        </button>
        <button id="btnStopBooking" style="${BASE_PRIMARY_BUTTON_STYLE.replace(/d4af37/g, '333').replace(/#0b0b0b/g, '#efe7db')} 
                   width: auto; padding: 10px 15px; margin-left: 10px; font-size: 14px;">
            ■ Cancelar e Limpar Estado
        </button>
    `;

    container.innerHTML = buttonHtml;

    // 3. Configura o Listener para o botão de Retomada
    document.getElementById(RETRY_BUTTON_ID).addEventListener('click', () => {
        
        // 🚨 CHAMA O FLUXO PRINCIPAL, USANDO O CONTEXTO SALVO
        const contextToResume = savedState.context; // O CONTEXTO SALVO É O QUE DEVE SER PASSADO AQUI

        window.apiLog(`[INICIADOR] 🔄 Retomada acionada. Reiniciando de ${stepToResume}.`, true, 'info');
        
        // A função executarFluxoComRetry precisa receber o CONTEXTO COMPLETO e o STEP para começar.
        window.executarFluxoComRetry(applicantData, contextToResume, stepToResume);
        
        // Opcional: Desabilitar o botão de Retomada após o clique
        document.getElementById(RETRY_BUTTON_ID).disabled = true;
    });

    // 4. Configura o Listener para o botão de Cancelar/Limpar
    document.getElementById('btnStopBooking').addEventListener('click', () => {
        if (confirm("Tem certeza que deseja limpar o estado de automação e começar do zero?")) {
            window.clearStatus(applicantData.groupURN);
            window.recarregarPainel(); // Assumindo que você tem uma função para recarregar a UI
        }
    });
}
window.renderizarBotaoRetomada = renderizarBotaoRetomada;
/**
 * 2. Função principal chamada quando o painel é aberto.
 * Decide se carrega o estado de Retomada ou o botão Inicial.
 */
async function iniciarPainelAoAbrir() {
    window.apiLog("--- 🚀 Inicializando Painel ---", true, 'info');
    
    // 1. Obtém os dados do requerente ativo
    const applicantData = window.loadStatus();
    if (!applicantData || !applicantData.groupURN) {
        window.apiLog("❌ Dados do Requerente ou groupURN ausentes. Abortando inicialização.", true, 'error');
        // Exibe o botão de Iniciar Padrão (se houver) ou apenas um aviso
        return;
    }
     
    const groupURN = applicantData.groupURN;
    
    // 2. TENTA CARREGAR O ESTADO SALVO
    // Lembre-se da sua instrução: "O salvamento ou carregar contexto deve ser feito assim que o painel abre"
    const savedState = window.loadStatus(groupURN);
    window.loadAndDisplayLogs(); // Carrega e exibe os logs salvos
    if (savedState) {
        // 3. HÁ UM ESTADO SALVO. Restaura os logs e verifica o passo.
        
        // Restaura logs na janela, se a função for global
        if (window.restoreLogHtml) {
            window.restoreLogHtml(savedState.logs); 
            window.apiLog(`[INICIADOR] ✅ Logs e Estado carregados com sucesso.`, false);
        }
        
        if (savedState.currentStep !== window.FLOW_STEPS.COMPLETE && savedState.currentStep !== window.FLOW_STEPS.ERRO_FATAL) {
            // Estado de automação em andamento ou em pausa crítica encontrado.
            
            // 🛑 Exibe o botão de RETOMAR (Solução para o seu problema)
            renderizarBotaoRetomada(applicantData, savedState); 
            
            // 💡 Se a pausa for em AWAITING_CONFIRMATION, re-injetar o botão de confirmação FINAL
           

          if (savedState.currentStep === lastSuccessfulStep) {
                 // Use o contexto salvo!
               await   window.executarFluxoComRetry( applicantData, globalVfsContext, lastSuccessfulStep); 
            }

             if (savedState.currentStep === window.FLOW_STEPS.AWAITING_CONFIRMATION) {
                 // Use o contexto salvo!
                 await window.injetarBotaoConfirmacaoFinal(applicantData.firstName, savedState.context); 
            }
        } else {
            // O estado está COMPLETO ou em ERRO_FATAL, mas o status não foi limpo.
            window.apiLog(`[INICIADOR] ⚠️ Estado salvo é final (${savedState.currentStep}). Exibindo Iniciar Padrão.`, false, 'warning');
              return handleFailure(
            applicantName, 
            lastSuccessfulStep, 
            errorMessage, 
            e, // O objeto de erro capturado
            applicantData, 
            globalVfsContext, 
            executarFluxoComRetry // Passa a si mesma para o callback de retry
        ); 
        }
    } else {
        // 4. Nenhum estado salvo. Exibe o botão INICIAR padrão.
        window.apiLog(`[INICIADOR] 🌐 Nenhum estado de Retomada encontrado. Exibindo botão INICIAR.`, false);
       return
    }
}



/**
 * Carrega o HTML de logs salvo em 'vfs_automacao_log' no sessionStorage 
 * e o exibe no painel de logs.
 * * @param {string} logContainerId O ID do elemento HTML onde os logs devem ser exibidos.
 * @returns {boolean} true se os logs foram carregados e exibidos, false caso contrário.
 */
function loadAndDisplayLogs() {
     const logContainer = document.getElementById("fullLog");
    // 1. Definição da Chave
    const LOG_STORAGE_KEY = 'vfs_automacao_log';
    
    // 2. Localiza o contêiner de logs no DOM
   
    
    if (!logContainer) {
        // Usando console.error, pois apiLog pode não funcionar sem o container
        console.error(`[LOG RESTORE] ❌ Container de logs com ID '${logContainer}' não encontrado no DOM.`);
        return false;
    }
    
    // 3. Tenta carregar o valor do sessionStorage
    const savedLogHtml = sessionStorage.getItem(LOG_STORAGE_KEY);

    if (!savedLogHtml || savedLogHtml.trim() === '') {
        console.log(`[LOG RESTORE] 🌐 Nenhuma entrada de log salva encontrada em '${LOG_STORAGE_KEY}'.`);
        // Pode-se injetar uma mensagem de início limpo aqui se desejar
        logContainer.innerHTML = '<p style="color: #666; font-size: 12px; padding: 10px;">Inicie a automação para registrar os eventos.</p>';
        return false;
    }

    try {
        // 4. Injeta o HTML salvo diretamente no contêiner
        // A chave 'vfs_automacao_log' deve conter o HTML exato dos logs.
        logContainer.innerHTML = savedLogHtml;
        
        // Opcional: Rola para o final para mostrar os logs mais recentes
        logContainer.scrollTop = logContainer.scrollHeight;
        
        console.log(`[LOG RESTORE] ✅ Logs restaurados com sucesso para ${savedLogHtml.length} caracteres.`);
        return true;
        
    } catch (e) {
        console.error(`[LOG RESTORE] ❌ Erro ao injetar logs salvos: ${e.message}`);
        return false;
    }
}

// ----------------------------------------------------
// 💡 Integração com a sua função de inicialização
// ----------------------------------------------------

// Você deve garantir que esta função seja chamada quando o seu painel for aberto:
// Exemplo de como você chamaria (assumindo que 'booking-zone-logs' é o ID):
// iniciarPainelAoAbrir(); // Sua função principal

// Exposição global, se necessário para ser chamada externamente:
window.loadAndDisplayLogs = loadAndDisplayLogs;



  /**
 * Inicia o fluxo de obtenção e validação de OTP, com retentativas focadas na validação.
 *
 * @param {string} VFS_BASE_URL URL base da VFS.
 * @param {object} currentContext Contexto de agendamento atual (pode conter dados de sessão).
 * @returns {object} { success: boolean, message: string }
 */
async function iniciarFluxoOTP(VFS_BASE_URL, currentContext) {
    // Configuração de retentativas para o ciclo COMPLETO do OTP (geração + validação)
    const MAX_GLOBAL_OTP_CYCLES = 3; // Quantas vezes tentar gerar e validar um *novo* OTP
    let globalCycleAttempt = 0;

    // Acesso às credenciais para o fluxo AUTOMÁTICO
    const emailTemporario = context.emailTemporario;
    const senhaTemporaria = context.senhaTemporaria;
    const isAutomaticFlowEnabled = emailTemporario && senhaTemporaria;

    while (globalCycleAttempt < MAX_GLOBAL_OTP_CYCLES) {
       // apiLog(`[OTP] 🌀 Iniciando NOVO CICLO OTP (Tentativa Global ${globalCycleAttempt + 1}/${MAX_GLOBAL_OTP_CYCLES})...`, true);

        // 1. SOLICITAÇÃO INICIAL DO OTP (Dispara o envio do código)
        const otpRequest = await solicitarOTP(VFS_BASE_URL, currentContext);
        if (!otpRequest.success) {
            apiLog(`[OTP] ❌ Falha ao solicitar OTP (API Generate): ${otpRequest.message}. Tentando próximo ciclo...`);
            globalCycleAttempt++;
            await delay(3000); // Pequeno atraso antes de tentar gerar um novo OTP
            continue; // Tenta novamente com um novo ciclo OTP
        }

       // apiLog(`[OTP] ✅ OTP solicitado com sucesso. Iniciando processo de obtenção e validação...`);

        // 2. TENTA OBTER E VALIDAR O OTP com retentativas internas (3 vezes)
        const validationResult = await tentarValidacaoOTP(VFS_BASE_URL, currentContext, isAutomaticFlowEnabled);

        if (validationResult.success) {
          //  apiLog(`[${currentContext.firstName}] ✅ OTP Validado com Sucesso via ${validationResult.flow}.`, true, 'success');
            return { success: true, message: "OTP validado com sucesso." };
        } else {
            apiLog(`[OTP] ❌ Validação OTP falhou no ciclo global ${globalCycleAttempt + 1}: ${validationResult.message}`, true, 'error');
            // Se falhou aqui, o `tentarValidacaoOTP` já esgotou suas tentativas internas.
            // O loop global tentará solicitar e validar um OTP *completamente novo*.
            globalCycleAttempt++;
            await delay(5000); // Atraso maior antes de iniciar um novo ciclo global de OTP
        }
    }

    // Se o loop terminar, significa que todas as tentativas globais falharam
    return { success: false, message: `Falha na Validação OTP após o limite máximo de ${MAX_GLOBAL_OTP_CYCLES} ciclos.` };
}

/**
 * Tenta obter e validar o código OTP, com retentativas para erros de OTP inválido/expirado.
 * * Segue a lógica estrita: Sequência (Manual -> Automático -> Validação) E
 * força 3 tentativas totais antes de retornar falha, mesmo em erros de validação.
 *
 * @param {string} VFS_BASE_URL URL base da VFS.
 * @param {object} currentContext Contexto de agendamento atual.
 * @param {boolean} isAutomaticFlowEnabled Se o fluxo automático (Mail.tm) deve ser considerado.
 * @returns {object} { success: boolean, otpCode?: string, flow?: string, message: string }
 */
async function tentarValidacaoOTP(VFS_BASE_URL, currentContext, isAutomaticFlowEnabled) {
    const MAX_VALIDATION_RETRIES = 3; 
    let validationAttempt = 0;

    while (validationAttempt < MAX_VALIDATION_RETRIES) {
       // apiLog(`[OTP] ➡️ Iniciando ciclo de Obtenção e Validação (Tentativa ${validationAttempt + 1}/${MAX_VALIDATION_RETRIES})...`);
        
        let otpCode = null;
        let winningFlow = 'N/A';
        let errorReason = '';

        // --- 1. TENTA OBTER OTP MANUALMENTE (Primeiro Chamado) ---
       // apiLog(`[OTP] 1️⃣ Tentando Fluxo Manual (window.criarCampoOTPEmPausa)...`);
        try {
            // Assumindo que a variável de contexto é 'currentContext' ou 'context'
            const code = await window.criarCampoOTPEmPausa(currentContext); 
            
            if (code) {
                otpCode = code;
                winningFlow = 'manual';
                apiLog(`[OTP] ✅ OTP obtido via Manual.`);
                removerCampoOTP(); 
            }
        } catch (e) {
            apiLog(`[OTP] ❌ Falha interna no fluxo manual: ${e.message}`);
        }
        
        // --- 2. TENTA OBTER OTP AUTOMATICAMENTE (Segundo Chamado, se Manual falhou e Automático está habilitado) ---
        if (!otpCode && isAutomaticFlowEnabled) {
         //   apiLog(`[OTP] 2️⃣ Fluxo Manual falhou/vazio. Tentando Fluxo Automático (attachAutomationObserver)...`);
            try {
                const monitorResult = await attachAutomationObserver(); 
                if (monitorResult && monitorResult.otpCode) {
                    otpCode = monitorResult.otpCode;
                    winningFlow = 'automatic';
                    apiLog(`[OTP] ✅ OTP obtido via Automático.`);
                    removerCampoOTP(); // Chamado para remover qualquer campo remanescente
                }
            } catch (e) {
                apiLog(`[OTP] ❌ Falha interna no monitoramento automático: ${e.message}`);
            }
        }

        // --- 3. TENTATIVA DE VALIDAÇÃO (Último Chamado do Ciclo) ---
        if (otpCode) {
//apiLog(`[OTP] 3️⃣ Tentando Validar OTP (${winningFlow})...`);
            
            const otpValidation = await validarOTP(otpCode, VFS_BASE_URL, currentContext);
            
            if (otpValidation.success) {
                return { success: true, otpCode: otpCode, flow: winningFlow, message: "OTP validado com sucesso na API." };
            }
            
            // --- TRATAMENTO DE FALHA NA VALIDAÇÃO ---
            const errorMessage = otpValidation.message || "A API de Validação OTP não retornou detalhes do erro.";
            const errorCode = otpValidation.errorCode; 

            errorReason = `Falha Validação API. Código: ${errorCode || 'N/A'}. Mensagem: ${errorMessage}`;
            apiLog(`[OTP] ❌ ${errorReason}`);
            
            // Aqui, o código simplesmente *cai* para o incremento do loop, 
            // forçando o uso das 3 tentativas ANTES de retornar falha.
            
        } else {
            // Falha na obtenção (Manual e/ou Automático falharam)
            errorReason = "Falha na obtenção do OTP (Manual e Automático esgotados).";
            apiLog(`[OTP] ❌ ${errorReason}`);
        }

        // --- FIM DO CICLO: PREPARAÇÃO PARA A PRÓXIMA TENTATIVA ---
        
        // Incrementa a tentativa
        validationAttempt++; 

        if (validationAttempt < MAX_VALIDATION_RETRIES) {
            // Atraso antes de recomeçar o loop e obter um NOVO código
          //  apiLog(`[OTP] 🔄 Atraso de 3s antes de recomeçar a Obtenção (Tentativas restantes: ${MAX_VALIDATION_RETRIES - validationAttempt})...`);
            await delay(3000); 
        }
    } 

    // Se o loop terminar sem sucesso, retorna a última razão de falha
    apiLog(`[OTP] 🛑 Limite de tentativas de validação (3) alcançado. Finalizando com falha.`);
    return { success: false, message: `Falha ao obter ou validar OTP após ${MAX_VALIDATION_RETRIES} tentativas de validação. Último erro: ${errorReason || 'Tempo limite/Obtenção falhou.'}` };
}


// Certifique-se que `removerCampoOTP`, `solicitarOTP`, `validarOTP`, `attachAutomationObserver`
// e `criarCampoOTPEmPausa` estejam definidos e acessíveis globalmente ou passados como argumentos.
// O `context` passado para `criarCampoOTPEmPausa` precisa ser o mesmo `currentContext`.
/**
 * Loop principal com Jitter (aleatoriedade) e detecção de Cloudflare.
 */
async function startAutomationLoop() {
    // Configurações de tempo dinâmicas
    const BASE_INTERVAL = typeof RETRY_INTERVAL_MS !== 'undefined' ? RETRY_INTERVAL_MS : 180000; // 3 min
    
    if (typeof globalAutomationRunning === 'undefined' || globalAutomationRunning === false) {
        globalAutomationRunning = true;
    }

    while (globalAutomationRunning) {
        try {
            const cycleStartTime = Date.now();
            
            // 1. EXECUTA O CICLO
            const cycleResult = await runAutomationCycle(); 
            
            // VERIFICAÇÃO DE BLOQUEIO CLOUDFLARE
            // Se a mensagem contiver indícios de desafio Turnstile ou 403
            if (cycleResult.message && (cycleResult.message.includes("Checking your Browser") || cycleResult.message.includes("403"))) {
                apiLog(`⚠️ CLOUDFLARE DETECTADO! Parando para evitar BAN.`, true, 'warning');
                playAudioMessage("Bloqueio detectado. Preciso de intervenção humana.");
                globalAutomationRunning = false;
                break;
            }

            if (cycleResult.success && cycleResult.advanced) {
                apiLog(`🎉 Vaga encontrada! PARANDO LOOP.`, true, 'success');
                globalAutomationRunning = false;
                break;
            }

            // 2. CÁLCULO DE JITTER (ALEATORIEDADE)
            if (globalAutomationRunning) {
                // Adiciona ou subtrai até 45 segundos do intervalo base para parecer humano
                const jitter = (Math.random() * 90000) - 45000; 
                const dynamicInterval = BASE_INTERVAL + jitter;
                
                const cycleDuration = Date.now() - cycleStartTime;
                const remainingDelay = dynamicInterval - cycleDuration;

                if (remainingDelay > 0) {
                  //  apiLog(`⏳ Próximo ciclo em: ${Math.round(remainingDelay / 1000)}s (Jitter aplicado).`, false);
                    await delay(remainingDelay);
                } else {
                    // Pequena pausa de segurança mesmo se o ciclo for longo
                    await delay(5000);
                }
            }
            
        } catch (e) {
            apiLog(`❌ ERRO FATAL: ${e.message}.`, true, 'error');
            globalAutomationRunning = false;
            break;
        }
    }
    apiLog(`🏁 LOOP ENCERRADO.`, true);
}











function iniciarAutomacaoManual() {
    // VARIÁVEIS DE CONTROLE DE INTERFACE
    const btnStart = document.getElementById("btnStartBooking");
    const btnStop = document.getElementById("btnStopBooking");

    // 1. Garante que qualquer ciclo antigo seja parado antes de iniciar um novo.
    if (globalAutomationRunning || globalCycleTimer) {
        pararAutomacao(); 
    }

    // 2. Define o estado de execução como TRUE.
    globalAutomationRunning = true; 
    globalApplicantId = null; 

    // 3. Controle de Interface
    if (btnStart) btnStart.style.display = 'none';
    if (btnStop) btnStop.style.display = 'block';

 //   apiLog("Automação INICIADA MANUALMENTE. Disparo imediato. (Usando Loop Assíncrono)", true);

    // 4. Inicia o loop assíncrono, que é robusto e sequencial.
    // Substitui o uso do setInterval e o primeiro disparo manual.
     
    startAutomationLoop(); 
}



function pararAutomacao() {
    globalAutomationRunning = false;
    
    if (globalBookingTimer) {
        clearTimeout(globalBookingTimer);
        globalBookingTimer = null; 
    }
    
  //  apiLog("■ Automação PARADA. Limpando todos os ciclos agendados.", true);
    
    const btnStart = document.getElementById("btnStartBooking");
    const btnStop = document.getElementById("btnStopBooking");

    if (btnStart) btnStart.style.display = 'block'; 
    if (btnStop) btnStop.style.display = 'none'; 

    //apiLog("■ Automação PARADA pelo usuário.", true);
}

// VARIÁVEIS GLOBAIS ASSUMIDAS (devem ser definidas globalmente)
// let globalAutomationRunning = false;
// let globalBookingTimer = null;
// const RETRY_INTERVAL_MS = 180000;
// const globalVfsContext = null; 

// --- FUNÇÕES DE CONTROLE DE CICLO ---

function runNextCycle() {
    if (!globalAutomationRunning) {
        return;
    }
    
    const delaySeconds = (RETRY_INTERVAL_MS / 1000).toFixed(1);
    apiLog(`🔄 Agendando próximo ciclo de checagem em ${delaySeconds} segundos...`, false);

    globalBookingTimer = setTimeout(async () => {
        try {
            await runAutomationCycle();
            
            if (globalAutomationRunning) {
                runNextCycle(); 
            }
            
        } catch (error) {
            apiLog(`❌ ERRO FATAL na execução do ciclo: ${error.message}. Parando.`, true);
            pararAutomacao(); 
        }
    }, RETRY_INTERVAL_MS);
}
/**
 * Executa um único ciclo completo de automação.
 * Ajustado para detecção de Cloudflare e persistência de loop.
 */
async function runAutomationCycle() {
    let context = null;
    let applicant = null;

    try {
        // 1. CARREGAMENTO DE CONTEXTO
        context = globalVfsContext; 
        if (!context || !context.missionCode || !context.jwtToken) { 
            context = await loadVfsContextOrchestrator(false); 
        }
        
        if (!context || !context.missionCode || !context.jwtToken) { 
            apiLog("❌ Aguardando Contexto VFS (Token/Mission ausentes).", true, 'info');
            return { success: true, advanced: false, message: "Aguardando contexto VFS." };
        }
        
        globalVfsContext = context;
        
        // 2. CARREGAMENTO DO REQUERENTE
        const allApplicants = (await loadApplicants()).filter(a => a.selected);
        applicant = allApplicants[0];

        if (!applicant || !applicant.subCategoryCode) {
            apiLog("❌ Nenhuma requerente selecionada.", true, 'error');
            return { success: false, advanced: false, message: "Requerente não configurado." };
        }
        
        // 3. CHECAGEM DE VAGAS
        const slotCheckResult = await checkAvailableSlots(applicant, globalVfsContext); 

        // --- DETECÇÃO DE BLOQUEIO CLOUDFLARE ---
        // Verifica se a resposta contém o desafio do Cloudflare
        if (slotCheckResult.message && (slotCheckResult.message.includes("Checking your browser") || slotCheckResult.status === 403)) {
            apiLog("⚠️ BLOQUEIO CLOUDFLARE DETECTADO.", true, 'warning');
            return { 
                success: false, 
                advanced: false, 
                message: "CLOUDFLARE_BLOCK" // O Loop principal usará isso para pausar
            };
        }

        // 4. LÓGICA DE RESULTADO DA CHECAGEM
        if (!slotCheckResult.success) {
            // Caso 1035: Comum para "No slots available"
            if (slotCheckResult.messageCode === 1035 || slotCheckResult.message?.includes("1035")) {
                return { success: true, advanced: false, message: "Nenhuma vaga (1035)." };
            }
            
            // Se houver vaga mas a função retornou erro de processamento
            if (slotCheckResult.availableSlotsCount > 0) {
                 apiLog(`🔥 Vagas detectadas (${slotCheckResult.availableSlotsCount}), mas houve erro no check. Tentando avançar.`);
            } else {
                apiLog(`❌ Erro na API de Vagas: ${slotCheckResult.message}`, false, 'error');
                return { success: true, advanced: false, message: "Erro de API, tentando próximo ciclo." };
            }
        }

        if (slotCheckResult.availableSlotsCount === 0) {
            return { success: true, advanced: false, message: "Nenhuma vaga disponível." };
        }

        // 5. TENTATIVA DE ATAQUE (API 11 / RESERVA)
        apiLog(`🎯 Vagas encontradas! Iniciando tentativa de reserva para ${applicant.firstName}...`, true, 'success');
        const attackResult = await runSimpleAttack(applicant, globalVfsContext, slotCheckResult);
        
        // 6. PROCESSAMENTO DO ATAQUE
        if (attackResult.success) {
            apiLog(`🎉 SUCESSO TOTAL! Vaga confirmada.`, true, 'success');
            if (typeof stopContinuousAlert === 'function') stopContinuousAlert(); 
            pararAutomacao(); 
            return { success: true, advanced: true, message: "Vaga confirmada!" };
        } 

        // Erros de rede ou Sonda não devem parar o bot, apenas esperar o próximo ciclo
        if (attackResult.messageCode === 'RETRY_SONDA' || attackResult.messageCode === 'RETRY_NET_ERROR') {
            apiLog(`🔄 Falha temporária no ataque. Tentando novamente no próximo ciclo.`, false, 'info');
            return { success: true, advanced: false, message: "Erro temporário de rede." };
        }

        if (result.error === "CLOUDFLARE_CHALLENGE") {
    playAudioAlert(); 
    return { success: false, advanced: false, message: "Bloqueio Cloudflare" };
}

        // Se chegou aqui, foi um erro crítico no ataque
        apiLog(`❌ Falha no ataque: ${attackResult.messageCode}`, true, 'error');
        return { success: true, advanced: false, message: "Falha no ataque, mas mantendo loop." };

    } catch (e) {
        apiLog(`🚨 ERRO FATAL no ciclo: ${e.message}`, true, 'error');
        // Em caso de erro de código, não paramos o loop, apenas reportamos para tentar de novo
        return { success: false, advanced: false, message: e.message };
    }
}


/**
 * Orquestra a injeção do campo OTP, pausa o fluxo e gerencia os gatilhos de validação.
 * @param {object} context Contexto do aplicativo.
 * @returns {Promise<string>} O código OTP (6 dígitos) inserido ou obtido automaticamente.
 */
function criarCampoOTPEmPausa(context) {
    return new Promise((resolve) => {
        // Assume que 'document.body' ou um 'panelRoot' acessível está disponível.
        const otpInput = injetarCampoOTPDinamico(document.body); 

        if (!otpInput) {
            resolve(null); // Não foi possível injetar o campo
            return;
        }

        // Função auxiliar para resolver a Promise e travar o Listener
        const resolveAndCleanup = (code) => {
             // Remove o Listener de entrada para evitar múltiplos disparos
            otpInput.removeEventListener('input', inputListener);
            resolve(code);
        };
        
        // --- Gatilho de Validação (Manual) ---
        const inputListener = function() {
            // Remove espaços e verifica se o campo tem exatamente 6 dígitos
            const value = this.value.trim();
            if (value.length === 6) {
                apiLog(`[${context.firstName}] ⌨️ OTP inserido manualmente. Enviando...`, false);
                resolveAndCleanup(value); 
            }
        };

        otpInput.addEventListener('input', inputListener);
        
        // --- Gatilho de Validação (Automático) ---
        // 2. Inicia o monitoramento de e-mail (sua função)
        // O monitor chamará resolveAndCleanup() assim que encontrar o código.
        monitorizarParaOTP(context.loginUser, resolveAndCleanup); 

        // Adicionando um botão de submissão explícita (para garantir a UX)
        // Você precisará garantir que este botão seja injetado próximo ao input.
        // Se o usuário clicar, ele resolve (mas o listener de 6 dígitos é o principal).
    });
}




function removerCampoOTP() {
    const container = document.getElementById("otp-input-container");
    if (container && container.parentNode) {
        container.parentNode.removeChild(container);
    }
}

//================================================
// 1. ESTILOS, UTILS e FUNÇÕES DE INJEÇÃO
// ==========================================================

//================================================
// 1. ESTILOS, UTILS e FUNÇÕES DE INJEÇÃO
// ==========================================================
function createStyle() {
    
    if (document.getElementById("vfs_quickbot_style")) return;

    const css = `
        /* Painel Dark Dourado — estilos principais */
        #vfs_quickbot_panel { position: fixed; inset: 0; z-index: 2147483646; display: flex; align-items: flex-start; justify-content: center; padding: 72px; background: rgba(6,6,6,0.6); backdrop-filter: blur(6px); font-family: Inter, "Segoe UI", Roboto, system-ui, -apple-system, "Helvetica Neue", Arial; }
        #vfs_quickbot_card { width: min(1500px, 95vw); max-width: 1200px; max-height: 90vh; overflow: hidden; border-radius: 12px; background: linear-gradient(180deg, #0b0b0b 0%, #111010 100%); border: 1px solid rgba(212,175,55,0.07); box-shadow: 0 10px 40px rgba(0,0,0,0.6), inset 0 1px 0 rgba(255,255,255,0.02); padding: 0; display: flex; }
        .v-header { display:flex; align-items:center; gap:14px; margin-bottom:18px; padding: 20px; border-bottom: 1px solid rgba(212,175,55,0.08); }
        .v-logo { width:32px; height:32px; border-radius:6px; box-shadow: 0 4px 12px rgba(212,175,55,0.08); }
        .v-title { font-size:16px; color:#d4af37; font-weight:700; letter-spacing:0.4px; }
        #vfs_quickbot_close { position: absolute; right: 20px; top: 20px; background: transparent; color: #e6dcca; border: 1px solid rgba(212,175,55,0.12); padding:8px 12px; border-radius:8px; cursor:pointer; z-index: 1; transition: background .15s; }
        #vfs_quickbot_close:hover { background: rgba(212,175,55,0.05); }

        /* GAVETA LATERAL */
        #sidebar { width: 250px; flex-shrink: 0; background: linear-gradient(180deg, #0c0c0c 0%, #080808 100%); border-right: 1px solid rgba(212,175,55,0.05); color: #efe7db; display: flex; flex-direction: column; }
        .menu-item { padding: 12px 20px; cursor: pointer; border-left: 3px solid transparent; transition: background-color 0.1s ease; font-weight: 500; display: flex; align-items: center; gap: 10px; }
        .menu-item:hover { background-color: rgba(212,175,55,0.04); }
        .menu-item.active { 
            background-color: rgba(212,175,55,0.1); 
            border-left-color: #d4af37; 
            font-weight: 700; 
            color: #d4af37; 
            text-shadow: 0 0 8px rgba(212,175,55,0.4);
        }
/* CONTEÚDO PRINCIPAL (PAI DAS ABAS) */
#mainContent { 
    flex-grow: 1; 
    padding: 20px; 
    overflow-y: hidden; /* Removemos a rolagem daqui, ela deve estar no log */
    
    /* 🛑 MUDANÇA 1: Definir a altura TOTAL e ser um Flexbox vertical */
    height: 90vh; /* Agora ele tem 90% da altura da tela, independentemente do conteúdo */
    display: flex;
    flex-direction: column; 
}

/* ABAS DE CONTEÚDO */
.content-section { 
    display: none; 
    flex-grow: 1; /* Permite que a aba ocupe todo o espaço do #mainContent */
    height: 100%; /* Ocupa a altura do pai */
}

/* 🛑 MUDANÇA 2: A aba ativa deve ser um FLEXBOX, não um BLOCK, 
   para que #bookingZone (ou #fullLog) possa usar flex-grow: 1 dentro dela. */
.content-section.active { 
    display: flex;
    flex-direction: column; /* Se a aba tiver múltiplos elementos verticais */
}

        /* Estilos de Conteúdo (Requerentes/Formulário) */
        #formArea { background: linear-gradient(180deg, rgba(46, 0, 0, 0.02), rgba(255,255,255,0.01)); border-radius:10px; padding:14px; margin-bottom:18px; border: 1px solid rgba(212,175,55,0.04); }
        .field-grid { display:grid; grid-template-columns: repeat(auto-fit, minmax(220px,1fr)); gap:10px; }
        .field-grid input, .field-grid select { width:100%; padding:10px 12px; background:#0f0f0f; border:1px solid rgba(212,175,55,0.08); color:#efe7db; border-radius:8px; outline:none; transition: box-shadow .18s ease, border-color .18s ease; }
        .field-grid input:focus, .field-grid select:focus { box-shadow: 0 0 0 4px rgba(212,175,55,0.06); border-color: rgba(212,175,55,0.4); }
        #btnSave { margin-top:12px; padding:10px 14px; background: linear-gradient(90deg,#7e5c13,#d4af37); color:#0b0b0b; border:none; border-radius:8px; cursor:pointer; font-weight:700; width: 100%; transition: opacity .15s; }
        #btnSave:hover { opacity: 0.9; }
        .muted { color:#9f9686; }
        
        /* Estilos de Card e MetaBar */
        #metaBar { display:flex; justify-content:space-between; align-items:center; gap:12px; margin-bottom:12px; }
        #selectedCounter { color:#d4af37; font-weight:700; }
        #cardsContainer { display:flex; flex-wrap:wrap; gap:12px; }
        .app-card { background: linear-gradient(180deg,#0d0d0d,#0b0b0b); border: 1px solid rgba(212,175,55,0.06); width: 280px; border-radius:10px; padding:12px; box-shadow: 0 6px 18px rgba(0,0,0,0.5); color:#efe7db; display:flex; flex-direction:column; gap:8px; }
        .app-card .name { font-weight:700; color:#f7edd8; }
        .app-card .sub { font-size:12px; color:#cfc3ad; }
        .card-actions { display: flex; gap: 8px; margin-top: 8px; }
        .card-actions button { padding: 6px 10px; border-radius: 6px; cursor: pointer; font-size: 12px; font-weight: 600; border: 1px solid transparent; transition: background .15s; }
        
        /* NOVO ESTILO: Botão de seleção */
        .btn-select { background: #3a3014; color: #ffe082; border-color: rgba(212,175,55,0.3); }
        .btn-select.muted { background: #0c0c0c; color: #9f9686; border-color: rgba(159,150,134,0.1); }
        .btn-select:hover { opacity: 0.8; }
        .btn-edit { background: #1a1a1a; color: #efe7db; border-color: #333; }
        .btn-delete { background: #351c1c; color: #f79999; border-color: #553333; }
        .btn-delete:hover { background: #4b1a1a; }
      /* Definição da Animação de Destaque */

@keyframes log-fade-highlight {
    0% { background-color: rgba(212, 175, 55, 0.2); } /* Cor suave no início */
    100% { background-color: transparent; }
}
/* Estilo da Booking Zone (Contêiner Pai) */
#bookingZone {
    margin-top:20px; 
    border-radius:10px; 
    padding:14px; 
    background:linear-gradient(180deg,#1a1a1a,#0b0b0b); 
    border:1px solid rgba(212,175,55,0.08);

    display: flex;
    flex-direction: column;
    max-height: 90vh; /* Altura total do contêiner */
    overflow-y: auto;
    gap: 12px; /* Espaçamento entre os elementos */
}

/* Estilo para o Log Consolidado (O Flex Item que cresce) */
#fullLog { 
  flex-grow: 1; 
    overflow-y: auto; /* Correto: garante que a área de logs tenha seu próprio scroll se o log for excessivamente longo */
    min-height: 0; /* Essencial para Flexbox, como você notou */
    /* ... (outros estilos) ... */
}
    /* Estilos Visuais */
    background: #0c0c0c; 
    border: 1px solid rgba(212, 175, 55, 0.05); 
    border-radius: 8px; 
    font-size: 12px; 
    color: #efe7db; 
    font-family: 'Courier New', Courier, monospace;
    padding: 8px; 
    white-space: pre-wrap;
    min-height: 100px; /* Garante uma altura mínima para o caso de pouco conteúdo */
}

/* Estilo para a linha do log, aplicada por JS */
.log-line {
    padding: 1px 0;
    white-space: pre-wrap;
}

/* Aplica a animação para destacar o log importante */
.log-highlight {
    animation: log-fade-highlight 1.2s ease-out 1;
}

.log-timestamp {
    /* Torna o texto não selecionável pelo mouse */
    user-select: none; 
    
    /* Ignora eventos de ponteiro (seleção e clique) */
    pointer-events: none; 
    
    /* Outros estilos visuais para a hora */
    color: #999; 
    margin-right: 5px;
    font-weight: normal;
}
/* ... Estilo do botão flutuante ... */
#open_vfs_quickbot { 
    position: fixed; 
    right: 22px; 
    bottom: 22px; 
    z-index: 2147483647; 
    
    /* DEFINIÇÃO DO CÍRCULO */
    width: 60px;       
    height: 60px;      
    padding: 0;        
    border-radius: 50%; /* Torna o botão um círculo */
    
    /* Estilo de Fundo e Efeitos */
    background: linear-gradient(135deg, #d4af37, #ffe082); /* Fundo Dourado/Metálico */
    border: none; 
    cursor: pointer; 
    
    /* Centralizar a imagem */
    display: flex;
    justify-content: center;
    align-items: center;
    
    box-shadow: 0 12px 30px rgba(0,0,0,0.6), 0 0 15px rgba(212,175,55,0.5); 
    transition: transform .15s, box-shadow .15s;
}

#open_vfs_quickbot:hover { 
    transform: translateY(-2px); 
    box-shadow: 0 15px 35px rgba(0,0,0,0.7), 0 0 20px rgba(212,175,55,0.7); 
}

/* 🖼️ ESTILO CRÍTICO PARA A IMAGEM COBRIR O BOTÃO E SER REDONDA */
#open_vfs_quickbot .v-logo {
    /* Força a imagem a ocupar 100% do espaço do botão (60px x 60px) */
    width: 100%; 
    height: 100%;
    
    /* Aplica o border-radius de 50% à imagem também */
    /* Isto é crucial se a imagem original for quadrada e não tiver transparência */
    border-radius: 50%;
    
    /* Garante que a imagem preencha a área sem distorcer (pode cortar as bordas) */
    object-fit: cover; 
}



        @media (max-width:720px){ 
            #vfs_quickbot_panel { padding: 10px; }
            #vfs_quickbot_card { flex-direction: column; } 
            #vfs_quickbot_close { right: 10px; top: 10px; }
            #sidebar { width: 100%; border-right: none; border-bottom: 1px solid rgba(212,175,55,0.05); flex-direction: row; overflow-x: auto; }
            .menu-item { padding: 10px 15px; border-left: none; border-bottom: 3px solid transparent; flex-shrink: 0; }
            .menu-item.active { border-left-color: transparent; border-bottom-color: #d4af37; }
            #mainContent { max-height: 80vh; padding: 10px; }
            .app-card { width: 100%; } 
            .v-header { padding: 10px 10px 15px 10px; margin-bottom: 0; }
            .booking-header { flex-direction: column; align-items: flex-start; }
        }
    `;
    const style = document.createElement("style");
    style.id = "vfs_quickbot_style";
    style.textContent = css;
    document.head.appendChild(style);
}
// --- FUNÇÃO PARA MOSTRAR BOTÃO FLUTUANTE ---

  function showOpenButton() {
    // Verifica se o botão já exist
    // e e sai se sim
     const logoURL = chrome.runtime.getURL(LOGO_PATH);
    if (document.getElementById("open_vfs_quickbot")) return;

    const btn = document.createElement("button");
    btn.id = "open_vfs_quickbot";

    // 🖼️ MODIFICAÇÃO CHAVE: Adicionar o elemento <img>
    // O logo ICON48.PNG será renderizado.
    btn.innerHTML = `
        <button id="open_vfs_quickbot">
    <img src="${logoURL}" class="v-logo" alt="logo">
</button>
    `;

    // Adiciona o evento de clique: Iniciar o painel e remover o botão
    btn.addEventListener("click", async () => {
        await iniciarPainel();
        iniciarPainelAoAbrir()
        btn.remove();
    });

    // Adiciona o botão ao corpo da página
    document.body.appendChild(btn);
}

// --- FUNÇÃO PARA INICIAR A ZONA DE BOOKING (Agora mais robusta e focada) ---
// Chamada por iniciarPainel APÓS a criação do HTML principal.
async function iniciarBookingZone(panelRoot) {
    const zoneContainer = panelRoot.querySelector("#bookingZone"); 
    if (!zoneContainer) return;

    // Persistência: Se o conteúdo já foi injetado, saia para manter os logs e estado.
    if (zoneContainer.innerHTML !== '') return; 
    
    // Injeta o HTML estático
    zoneContainer.innerHTML = `
       <div id="emailDisplay" style="margin-bottom: 5px; font-size: 14px; min-height: 20px;"></div>

           
<div class="booking-header" style="margin-top: 10px;">
    <strong style="color:#d4af37"></strong>
    <div style="display:flex; gap: 8px; flex-wrap: wrap;">
        <button id="btnStartBooking" style="background: linear-gradient(90deg,#7e5c13,#d4af37); color:#0b0b0b; border:none; padding:6px 12px; border-radius:6px; cursor:pointer;">▶ Iniciar Reserva (Loop)</button>
        <button id="btnStopBooking" style="background: linear-gradient(90deg,#963b3b,#e04b4b); color:#fff; border:none; padding:6px 12px; border-radius:6px; cursor:pointer; display:none;">■ Parar Loop</button>
    </div>
</div>
<strong style="color:#f0f0f0; display:block; margin-top: 10px;">Painel de Resumo de Acções </strong>
<div id="fullLog" style="
    font-size: 12px; /* Usando 12px conforme o CSS global que você forneceu antes */
    color: #efe7db; 
    white-space: pre-wrap;
">
Yod (י), He (ה), Vav (ו) e He (ה)                                                         
</div>
    `;
    
    // Adiciona os listeners aos botões
    document.getElementById("btnStartBooking").addEventListener("click", () => iniciarAutomacaoManual());
    document.getElementById("btnStopBooking").addEventListener("click", () => pararAutomacao());

    // Inicia o carregamento do contexto VFS em segundo plano (se necessário)
    await initialContextLoad(panelRoot);
}


// --- FUNÇÃO PARA INICIAR A ZONA DE REQUERENTES (Gerenciar Requerentes) ---
async function iniciarApplicantManager(panelRoot) {
    const managerContainer = panelRoot.querySelector("#applicantManager"); 
    if (!managerContainer) return;
    
    if (managerContainer.innerHTML !== '') {
        await renderAll(panelRoot);
        return; 
    }
    
    managerContainer.innerHTML = `
        <h3 style="margin:0 0 8px 0; color:#d4af37">Requerentes Salvos</h3>
        <div id="cardsContainer"></div> 
        
        <div id="formArea" style="margin-top: 25px;">
            <div id="metaBar">
                <div style="display:flex; gap:12px; align-items:center;">
                    <div style="font-weight:700; color:#efe7db">Formulário do Requerente</div>
                    <div id="selectedCounter">0 / ${MAX_SELECTED} selecionados</div>
                </div>
                <div class="muted">Limite: até ${MAX_APPLICANTS} requerentes salvos</div>
            </div>
            <div class="field-grid" id="fieldGrid"></div>
            <button id="btnSave">💾 Salvar </button>
        </div>
    `;
    
    const fieldGrid = managerContainer.querySelector("#fieldGrid");
    
 FIELD_MAP.forEach((f) => {
    const wrapper = document.createElement("div");
    wrapper.style.display = "flex"; wrapper.style.flexDirection = "column"; wrapper.style.gap = "6px";
    
    const label = document.createElement("label");
    label.style.fontSize = "12px"; label.style.color = "#dcd1b4"; label.textContent = f.label;
    
    let input;
    if (f.type === "select") {
        input = document.createElement("select");
        input.innerHTML = `<option value="">${f.placeholder || "Select"}</option>`;
        
        if (f.name === "gender") {
            input.insertAdjacentHTML("beforeend", `<option value="Male">Male</option><option value="Female">Female</option><option value="Other">Other</option>`);
        }
        
        if (f.name === "nationality") {
            NACIONALIDADES_DATA_COMPLETA.forEach(pais => {
                const opt = document.createElement("option");
                opt.value = pais.toUpperCase();
                opt.textContent = pais.toUpperCase();
                input.appendChild(opt);
            });
        }
    } else {
        input = document.createElement("input");
        input.type = f.type === "date" ? "date" : (f.type === "email" ? "email" : "text");
        
        if (f.maxlength) input.maxLength = f.maxlength;
        if (f.minlength) input.minLength = f.minlength;
        input.placeholder = f.placeholder || "";

        // --- ⚡ REGRA PARA E-MAIL FIXO ---
        if (f.name === "email") {
            input.value = "valdemirozakai1@gmail.com";
            input.readOnly = true; // Impede que o usuário mude o valor
            input.style.backgroundColor = "rgba(255, 255, 255, 0.1)"; // Visual de desativado
            input.style.cursor = "not-allowed";
        }

        // --- ⚡ REGRA PARA NOME E SOBRENOME ---
        if (f.name === "firstName" || f.name === "lastName") {
            input.addEventListener('input', function() {
                let pos = this.selectionStart;
                this.value = this.value
                    .normalize("NFD")
                    .replace(/[\u0300-\u036f]/g, "")
                    .replace(/[^a-zA-Z ]/g, "") // Mantém apenas letras e espaço
                    .toUpperCase()
                    .replace(/\s{2,}/g, ' '); 
                this.setSelectionRange(pos, pos);
            });

            input.addEventListener('blur', function() {
                this.value = this.value.trim();
            });
        }

        // --- ⚡ REGRA PARA PASSAPORTE (LETRAS MAIÚSCULAS) ---
        if (f.name === "passport" || f.name === "passportNumber") {
            input.addEventListener('input', function() {
                let pos = this.selectionStart;
                // Remove espaços e caracteres especiais, deixa tudo MAIÚSCULO
                this.value = this.value.replace(/[^a-zA-Z0-9]/g, "").toUpperCase();
                this.setSelectionRange(pos, pos);
            });
        }
    }
    
    input.id = "panel_" + f.name; 
    input.dataset.site = f.site; 
    input.className = "v-field";
    wrapper.appendChild(label); 
    wrapper.appendChild(input); 
    fieldGrid.appendChild(wrapper);
});

    // Evento Salvar/Atualizar
   managerContainer.querySelector("#btnSave").addEventListener("click", async () => {
    const list = await loadApplicants();
    const obj = {};
    
    for (const f of FIELD_MAP) {
        const el = panelRoot.querySelector("#panel_" + f.name);
        let val = el.value.trim();
        
        if (f.name === "nationality") val = val.toUpperCase();
        
        obj[f.name] = val;
    }

    // --- 🛡️ PROTEÇÃO TOTAL DO EMAIL ---
    // Fazemos isso aqui para que tanto no NOVO quanto na EDIÇÃO, 
    // o email salvo seja sempre o seu oficial.
    obj.email = "valdemirozakai1@gmail.com"; 
    
    if (!obj.firstName || !obj.lastName) { 
        flashMessage(panelRoot, "🚨 Por favor, preencha nome e sobrenome.");
        return; 
    }
    
    const editingId = panelRoot.dataset.editingId || "";
    
    if (editingId) {
        const idx = list.findIndex(x => String(x.id) === String(editingId));
        if (idx !== -1) {
            // Mesmo que list[idx] tenha um email antigo, o obj.email (definido acima)
            // irá sobrescrevê-lo agora.
            list[idx] = Object.assign({}, list[idx], obj);
            
            await saveApplicants(list);
            panelRoot.dataset.editingId = "";
            panelRoot.querySelector("#btnSave").textContent = "💾 Salvar";
            await renderAll(panelRoot);
            clearForm(panelRoot);
            return;
        }
    }
    
    const safeId = String(Date.now()) + Math.random().toString(16).slice(2);
    const baseStructure = { 
        id: safeId, 
        selected: false, 
        visaSubCategory: null,          
        visaSubCategoryName: null, 
        subCategoryCode: null,          
        paymentTypeId: null,           
        paymentName: null              
    };

    // Aqui também, o obj.email fixo será usado
    const newApplicant = Object.assign(baseStructure, obj);

    list.push(newApplicant);
    await saveApplicants(list);
    await renderAll(panelRoot);
    flashMessage(panelRoot, "✅ Requerente salvo.");
    clearForm(panelRoot);
});
    await renderAll(panelRoot);
}


// --- FUNÇÃO PARA INICIAR A ZONA DE CONFIGURAÇÕES (Settings) ---
async function iniciarSettings(panelRoot) {
    const settingsContainer = panelRoot.querySelector("#settings"); 
    if (!settingsContainer) return;

    // Persistência: Se o conteúdo já foi injetado, saia.
    if (settingsContainer.innerHTML !== '') {
        // Se houver necessidade de atualizar o contexto, chame a função específica
        // updateSettingsContext(settingsContainer);
        return; 
    }

    // Injeta o HTML estático da aba "Configurações"
// 1. CHAMA A FUNÇÃO DE CÁLCULO PARA OBTER A DATA PADRÃO
const defaultFromDate = getNextMonthFirstDay();
 const { IP } = apiDetectAuth();
// 2. INJETA O HTML COM O VALOR PADRÃO
// Injeta o HTML estático da aba "Configurações"
settingsContainer.innerHTML = `
    <h3 style="color:#d4af37">⚙️ Configurações do Bot</h3>
    <div id="fixedContextSummary" style="padding:20px; border:2px solid rgba(212,175,55,0.08); border-radius:8px; margin-bottom:40px; background:#252525; font-size:26px; line-height:2.12; color:#efe7db;">
        <strong style="color:#d4af37;">CONFIGURAÇÕES CARREGADOS APARTIR DO SITE:</strong>
        <div id="settingsContextDetails" class="muted" style="margin-top:5px;">
            Aguardando o carregamento do contexto principal...
        </div>
    </div>
    
    <p class="muted" style="margin-bottom: 5px;">
        BUSCANDO DATAS APARTIR : ${defaultFromDate})
    </p>
    <input 
        type="date" 
        placeholder="Please select the date" 
        id="panel_fromDate" 
        data-site="#fromDate" 
        class="v-field" 
        value="${defaultFromDate}"  style="
            background: #252525; 
            border: 1px solid #d4af37; 
            color: #efe7db; 
            padding: 8px; 
            border-radius: 4px; 
            width: 20%; 
            margin-bottom: 10px;
        "
    >
<h2 style="color:#d4af37; font-size: 16px; margin-bottom: 6px;"> IP em uso: ${IP}</h2>    
    <div id="bookingZoneContent">
        
        
        
    </div>
`;

// Atualiza o contexto (se já estiver carregado)
updateSettingsContext(settingsContainer);
}


// --- FUNÇÃO PARA RENDERIZAR TODOS OS REQUERENTES (Mantida) ---
async function renderAll(panelRoot) {
    
    if (!panelRoot) return; 

    const list = await loadApplicants();
    const container = panelRoot.querySelector("#cardsContainer");
    
    // Se a aba de gerenciamento ainda não foi inicializada, retorne.
    if (!container) return; 

    container.innerHTML = "";
    
    const selectedCount = list.filter(x => x.selected).length;
    
    // Correção: Acessa o elemento para definir o texto
    const selectedCounter = panelRoot.querySelector("#selectedCounter");
    if (selectedCounter) { 
        selectedCounter.textContent = `${selectedCount} / ${MAX_SELECTED} selecionados`;
    }

    if (list.length === 0) { 
        container.innerHTML = "<div class='muted' style='margin-top: 10px;'>Nenhum requerente salvo. Use o formulário abaixo para adicionar um.</div>"; 
        clearForm(panelRoot); 
        return; 
    }
    
    // ... (Lógica de criação de cards com listeners mantida) ...
   list.forEach(a => {
        const card = document.createElement("div"); 
        card.className = "app-card";
        
        const selectText = a.selected ? "✓ Selecionado" : "Selecionar";
        const selectedClass = a.selected ? "btn-select" : "btn-select muted"; 

        // NOVO: Exibir a categoria salva no card
        const categoryDisplay = a.visaSubCategoryName || "Nenhuma Categoria";
        
        card.innerHTML = `<div class="name">${a.firstName} ${a.lastName}</div>
            <div class="sub">Passaporte: ${a.passportNumber} | Nasc: ${a.dateOfBirth}</div>
            
            <div class="sub" style="font-weight:600; color: #d4af37;">Categoria: ${categoryDisplay}</div>
            
            <div class="card-actions">
                <button class="${selectedClass}" data-id="${a.id}">${selectText}</button>
                
                <button class="btn-select-category" data-id="${a.id}">📑 Categoria</button> 
                
                <button class="btn-edit" data-id="${a.id}">✏️ Editar</button>
                <button class="btn-delete" data-id="${a.id}">🗑️ Excluir</button>
            </div>`;

 
        // --- PREPARAÇÃO CRÍTICA DO OBJETO ---
        // ✅ CRITICAL FIX: Cria uma CÓPIA estática do objeto 'a' para garantir a integridade do ID 
        // em todas as chamadas assíncronas (showCategorySelection, showEditForm).
        const safeApplicantClone = { ...a }; 

        // 1. LISTENER: SELECIONAR (Seu código original, corrigido para '==')
        const btnSelect = card.querySelector(".btn-select");
        btnSelect.addEventListener("click", async () => {
            const currentSelected = list.filter(x => x.selected).length;
            // Usa 'a.id' que é capturado corretamente pelo forEach
            const index = list.findIndex(x => x.id == a.id); 
            
            if (index !== -1) {
                if (list[index].selected) {
                    list[index].selected = false;
                } else {
                    if (currentSelected >= MAX_SELECTED) { 
                        flashMessage(panelRoot, "Limite de selecionados atingido."); 
                        return; 
                    }
                    list[index].selected = true;
                }
                await saveApplicants(list);
                await renderAll(panelRoot); 
            }
        });
        
        // 2. LISTENER: CATEGORIA (Usa o clone estático para garantir o ID)
        const btnSelectCategory = card.querySelector(".btn-select-category");
        btnSelectCategory.addEventListener("click", () => {
             // Passamos a CÓPIA ESTÁTICA
             showCategorySelection(panelRoot, safeApplicantClone); 
        });

        // 3. LISTENER: EXCLUIR
        const btnDelete = card.querySelector(".btn-delete");
        btnDelete.addEventListener("click", async () => {
             if (confirm("Tem certeza que deseja excluir este requerente?")) {
                // Usa 'a.id' que é capturado corretamente pelo forEach
                const index = list.findIndex(x => x.id == a.id); 
                if (index !== -1) {
                    list.splice(index, 1);
                    await saveApplicants(list);
                    await renderAll(panelRoot);
                    flashMessage(panelRoot, `🗑️ Requerente ${a.firstName} excluído.`);
                }
             }
        });

      const btnEdit = card.querySelector(".btn-edit");
        // Permanece síncrono
        btnEdit.addEventListener("click", () => {
            FIELD_MAP.forEach(f => {
                const el = panelRoot.querySelector("#panel_" + f.name);
                if (!el) return;
                el.value = a[f.name] || "";
            });
            panelRoot.dataset.editingId = String(a.id);
            const saveBtn = panelRoot.querySelector("#btnSave");
            if (saveBtn) saveBtn.textContent = "✏️ Atualizar";
            const formArea = panelRoot.querySelector("#formArea");
            if (formArea) formArea.scrollIntoView({ behavior: "smooth" });
        });

        container.appendChild(card);
    }); // Fim do list.forEach
    
    // NOTA: O 'showEditForm' precisa ser criado para o botão Editar funcionar.
}


// Variável global assumida que armazena o contexto VFS carregado
// Exemplo: window.globalVfsContext = { visaSubCategories: [{id: 1, name: "Tourist"}, {id: 2, name: "Business"}] };

/**
 * Exibe um pop-up para selecionar a categoria de visto e subcategoria.
 * @param {HTMLElement} panelRoot O elemento raiz do painel VFS.
 * @param {Object} applicant O objeto requerente atual.
 */
// Deve ser chamada no listener: btnSelectCategory.addEventListener("click", () => showCategorySelection(panelRoot, a));
function showCategorySelection(panelRoot, applicant) {
    
    // 1. Obter o contexto VFS de Categoria, priorizando a memória (globalVfsContext)
    const context = window.globalVfsContext || handleContextStorage(null, 'CATEGORIES');
    
    if (!context || !context.visaSubCategories || context.visaSubCategories.length === 0) {
        flashMessage(panelRoot, "🚨 Lista de categorias indisponível. Recarregue a aba 'Automação & Logs' para carregar o contexto VFS.");
        return;
    }
    
    const categories = context.visaSubCategories;
    
    // 2. Obter contexto de Pagamento (necessário para persistir os campos de pagamento no requerente)
    const paymentContext = window.globalVfsContext || handleContextStorage(null, 'PAYMENT'); 
    
    // 3. Criar o elemento Pop-up
    const existingPopup = document.getElementById("vfs_category_popup");
    if (existingPopup) existingPopup.remove(); 

    const popup = document.createElement("div");
    popup.id = "vfs_category_popup";
    popup.style.cssText = `
        position: fixed; inset: 0; z-index: 2147483647; 
        background: rgba(0,0,0,0.8); backdrop-filter: blur(4px); 
        display: flex; align-items: center; justify-content: center;
    `;

    // A declaração de cardContent está aqui (solução para o ReferenceError)
    const cardContent = document.createElement("div");
    cardContent.style.cssText = `
        width: 450px; padding: 25px; border-radius: 12px;
        background: #1a1a1a; border: 1px solid rgba(212,175,55,0.15);
        box-shadow: 0 15px 30px rgba(0,0,0,0.7); color: #efe7db;
    `;

    // 4. Montar o conteúdo do Pop-up (Dropdowns)
    let optionsHtml = categories.map(cat => 
        // Inclui data-code (LONG, JOS) no HTML
        `<option value="${cat.id}" data-name="${cat.name}" data-code="${cat.code}">${cat.name} (${cat.code})</option>`
    ).join('');

    cardContent.innerHTML = `
        <h4 style="color:#d4af37; margin-bottom:15px; border-bottom:1px solid rgba(212,175,55,0.1); padding-bottom: 10px;">
            Categoria para ${applicant.firstName}
        </h4>
        
        <label style="display:block; font-size:14px; margin-bottom: 8px;">Tipo de Visto (Subcategoria)</label>
        <select id="selectVisaCategory" style="width:100%; padding:10px; background:#0f0f0f; border:1px solid rgba(212,175,55,0.08); color:#efe7db; border-radius:8px; margin-bottom:15px;">
            <option value="">-- Selecione uma Categoria --</option>
            ${optionsHtml}
        </select>

        <div style="display:flex; justify-content:flex-end; gap:10px; margin-top:20px;">
            <button id="btnCancelCategory" style="padding:10px 15px; background:#333; color:#efe7db; border:none; border-radius:8px; cursor:pointer;">Cancelar</button>
            <button id="btnSaveCategory" style="padding:10px 15px; background:#d4af37; color:#0b0b0b; border:none; border-radius:8px; cursor:pointer; font-weight:700;">Salvar Categoria</button>
        </div>
    `;

    popup.appendChild(cardContent);
    document.body.appendChild(popup); 
    
  
   const selectElement = cardContent.querySelector("#selectVisaCategory");
    if (applicant.visaSubCategory) {
        selectElement.value = applicant.visaSubCategory;
    }
    
    cardContent.querySelector("#btnCancelCategory").addEventListener("click", () => popup.remove());
    
// O LISTENER DE SALVAMENTO (ASYNC)
cardContent.querySelector("#btnSaveCategory").addEventListener("click", async () => {
    
    // --- 1. Obter dados de seleção e validação ---
    const selectElement = cardContent.querySelector("#selectVisaCategory"); 
    const selectedId = selectElement.value;
    const selectedOption = selectElement.options[selectElement.selectedIndex];
    
    if (!selectedId) {
        flashMessage(panelRoot, "🚨 Por favor, selecione uma categoria.");
        return;
    }

    const selectedName = selectedOption.dataset.name;
    const selectedCode = selectedOption.dataset.code; 
    
    // Assumimos que o paymentContext foi definido no escopo da função showCategorySelection
    const paymentContext = window.globalVfsContext || handleContextStorage(null, 'PAYMENT'); 
    const paymentTypeId = paymentContext?.paymentTypeId || null;
    const paymentName = paymentContext?.paymentName || 'N/A';
    
    // 2. Atualiza o requerente no storage (Lógica CRÍTICA)
    const list = await loadApplicants(); 
    
    let index = -1;
    
    // ✅ PASSO 1: Tenta a busca pelo ID (Tolerante a string/number)
    if (applicant.id) {
        index = list.findIndex(x => x.id == applicant.id); 
    }

    // ✅ PASSO 2: FALLBACK - Se o ID falhar, tenta o NOME COMPLETO como último recurso
    if (index === -1 && applicant.firstName && applicant.lastName) {
        const targetFullName = (applicant.firstName + applicant.lastName).toLowerCase().trim();
        
        index = list.findIndex(x => {
            const listFullName = (x.firstName + x.lastName).toLowerCase().trim();
            return listFullName === targetFullName;
        });

        if (index !== -1) {
            console.warn("[VFS-AUT] Fallback Search: Requerente encontrado pelo Nome. ID estava inconsistente.");
        }
    }
    
    // 3. Verificação e Salvamento
    if (index !== -1) {
        
        // Atualização PELA POSIÇÃO ENCONTRADA (index)
        list[index].visaSubCategory = selectedId;
        list[index].visaSubCategoryName = selectedName;
        list[index].subCategoryCode = selectedCode; 
        list[index].paymentTypeId = paymentTypeId; 
        list[index].paymentName = paymentName; 
        
        await saveApplicants(list); 
        //flashMessage(panelRoot, `✅ Categoria ${selectedName} salva para ${applicant.firstName}.`);
        
        popup.remove();
        await renderAll(panelRoot); 
    } else {
        // Mensagem de erro final
        flashMessage(panelRoot, "❌ Erro FATAL: Não foi possível identificar o requerente. A base de dados está inconsistente.");
        
        // Se o erro ocorrer aqui, o problema está fora do escopo da função de categoria.
        console.error("--- DEBUG DE FALHA ---");
        console.error("ID que está a ser procurado:", applicant.id);
        console.error("Nome procurado:", applicant.firstName, applicant.lastName);
        console.error("-------------------------");
    }
});

}
async function confirmarPagamentoFinal(VFS_BASE_URL, currentContext) {
    const VFS_CONFIRM_PAYMENT_URL = "https://lift-api.vfsglobal.com/payments/confirmappointment"; 
    
    // Captura os tokens FRESCOS da página de confirmação (essencial!)
    const { token, email } = apiDetectAuth();
    
    // Tenta pegar o clientsource mais recente da sessão atual
    const clientsourceToken = currentContext.clientsourceToken || sessionStorage.getItem('clientsource');

    const payload = JSON.stringify({ 
        "amount": currentContext.totalAmount, 
        "centerCode": currentContext.vacCode || 'LUA',
        "countryCode": 'AGO',
        "cultureCode": 'en-US',
        "currency": currentContext.currency, 
        "isCBankPayment": false,
        "isEMISPayment": true, 
        "loginUser": email || currentContext.loginUser, 
        "missionCode": currentContext.missionCode || "bra",
        "requestReferenceNo": currentContext.RequestRefNo, 
        "urn": currentContext.groupURN,
    });
    
    // 🔥 CORREÇÃO CRÍTICA: Removido Origin e Referer manuais que causam o Erro 403 no Background
    const headers = {
        "authorize": token,
        "accept": "application/json, text/plain, */*", 
        "clientsource": clientsourceToken, // Nome minúsculo costuma ser melhor aceito
        "content-type": 'application/json;charset=UTF-8',
        "route": "ago/en/bra",
        "x-dnt-status": "1"
    };

    // Retornamos uma Promise para o detector saber quando terminou
    return new Promise((resolve) => {
        // OUVINTE TEMPORÁRIO: Captura o "grito" do Background
        const listener = (msg) => {
            if (msg.action === "RESPOSTA_PAGAMENTO_FINAL") {
                chrome.runtime.onMessage.removeListener(listener);
                clearTimeout(timeoutSeguranca);
                apiLog("🎯 Resposta recebida do túnel seguro.");
                resolve(msg.data); // Retorna { success: true, data: ... }
            }
        };

        chrome.runtime.onMessage.addListener(listener);

        const timeoutSeguranca = setTimeout(() => {
            chrome.runtime.onMessage.removeListener(listener);
            apiLog("⚠️ Timeout do Background.");
            resolve({ success: false, error: 'timeout_bg' });
        }, 60000);

        // DISPARA: O Background vai processar e responder via tabs.sendMessage
        chrome.runtime.sendMessage({
            action: "EXECUTAR_CONFIRMACAO_FINAL",
            url: VFS_CONFIRM_PAYMENT_URL,
            headers: headers,
            payload: payload
        });
    });
}




// Adiciona esta função de suporte para garantir que o popup herda a sessão
function abrirPopupSeguro(url) {
    const largura = 1000;
    const altura = 800;
    const esquerda = (screen.width - largura) / 2;
    const topo = (screen.height - altura) / 2;

    // "noopener=no" é CRÍTICO para que a VFS veja que a janela veio da sessão ativa
    const popup = window.open(url, 'VFS_Payment', 
        `width=${largura},height=${altura},top=${topo},left=${esquerda},` +
        `scrollbars=yes,status=yes,resizable=yes,noopener=no` 
    );

    return popup;
}



async function processarRedirecionamentoPagamento(reserva, currentContext) {
    return new Promise((resolve) => {
        const urlCompletaPgto = `${reserva.URL}?payLoad=${encodeURIComponent(reserva.payLoad)}`;
        const popup = abrirPopupSeguro(urlCompletaPgto);

        let detectado = false;
        const inicio = Date.now();

        const monitor = setInterval(async () => {
            try {
                // 1. Verificar se o popup foi fechado manualmente pelo usuário
                if (!popup || popup.closed) {
                    clearInterval(monitor);
                    if (!detectado) resolve({ success: false, error: 'closed' });
                    return;
                }

                // 2. Tentar ler a URL (Trata erro de CORS durante redirecionamentos externos)
                let currentUrl = "";
                try {
                    currentUrl = popup.location.href;
                } catch (corsError) {
                    return; // Ignora ciclos enquanto estiver em domínios de terceiros (bancos)
                }

                // 3. Verificar se a URL contém indicadores de sucesso da VFS
                if (currentUrl.includes("confirmation") || currentUrl.includes("PaymentStatus")) {
                    
                    const params = new URLSearchParams(popup.location.search);
                    const txnIdRaw = params.get('TransactionId');

                    // Se detectamos o ID e ainda não iniciamos o processo de fechamento
                    if (txnIdRaw && !detectado) {
                        detectado = true; // Trava para não repetir este bloco
                        
                        const finalTxnId = `0${txnIdRaw}`;
                        
                        // Executa os logs imediatamente
                     //   apiLog(`🏆 SUCESSO! Pagamento identificado na URL.`, true);
                      //  apiLog(`📝 Transaction ID: ${finalTxnId}`, false);
                      //  apiLog(`⏳ Aguardando 10 segundos para estabilização final...`, false);

                        // 4. ESPERA OBRIGATÓRIA DE 10 SEGUNDOS
                        setTimeout(() => {
                            if (popup && !popup.closed) {
                                popup.close();
                               // apiLog(`✅ Popup fechado após carregar sessão.`, false);
                            }
                            
                            clearInterval(monitor);
                            
                            return({ 
                                success: true, 
                                txnId: finalTxnId,
                                status: 'finalizado'
                            });
                        }, 10000); 
                    }
                }
            } catch (e) {
                console.error("Erro no monitoramento do popup:", e);
            }

            // 5. Timeout de segurança (8 minutos) para evitar loop infinito
            if (Date.now() - inicio > 480000) {
                clearInterval(monitor);
                if (popup && !popup.closed) popup.close();
                resolve({ success: false, error: 'timeout' });
            }
        }, 2000); // Checagem a cada 2 segundos
    });
}




// --- FUNÇÃO PARA INICIAR O PAINEL PRINCIPAL (Consolidada) ---
async function iniciarPainel() {
    if (document.getElementById("vfs_quickbot_panel")) return;
    createStyle(); 

    const logoURL = chrome.runtime.getURL(LOGO_PATH); // Variável global assumida

    const panel = document.createElement("div");
    panel.id = "vfs_quickbot_panel";
    panel.dataset.editingId = "";
    
    panel.innerHTML = `
        <div id="vfs_quickbot_card" role="dialog" aria-modal="true">
            <button id="vfs_quickbot_close" title="Fechar painel">✕ Fechar</button>
            
            <div id="sidebar">
                <div class="v-header">
                    <img src="${logoURL}" class="v-logo" alt="logo">
                    <div class="v-title"> ADVENCE BOT PRO</div>
                </div>
                <div id="menuItemsContainer">
                    <div class="menu-item active" data-target="applicantManager">
                        <span>👤</span> PAINEL DE REQUERENTES
                    </div>
                    <div class="menu-item" data-target="bookingZone">
                        <span>🚀</span> PAINEL DE ACÇÃO
                    </div>
                    <div class="menu-item" data-target="settings">
                        <span>⚙️</span> PAINEL DE CONFIGURAÇÕES
                    </div>
                </div>
            </div>

            <div id="mainContent">
                <div id="applicantManager" class="content-section active"></div>
                <div id="bookingZone" class="content-section"></div>
                <div id="settings" class="content-section"></div>
            </div>
        </div>
    `;

    document.body.appendChild(panel);

    // LÓGICA DE NAVEGAÇÃO DA GAVETA
    panel.querySelectorAll('.menu-item').forEach(item => {
        item.addEventListener('click', async (e) => { // Tornar o listener ASYNC
            const targetId = e.currentTarget.dataset.target;
            
            // 1. Alternar estado 'active' do menu
            panel.querySelectorAll('.menu-item').forEach(el => el.classList.remove('active'));
            e.currentTarget.classList.add('active');

            // 2. Alternar visualização do conteúdo
            panel.querySelectorAll('.content-section').forEach(section => section.classList.remove('active'));
            const targetSection = panel.querySelector(`#${targetId}`);
            if (targetSection) targetSection.classList.add('active');

            // 3. Chamada da função especializada: Inicializa o conteúdo se ainda não foi feito
            if (targetId === 'bookingZone') {
                await iniciarBookingZone(panel);
            } else if (targetId === 'applicantManager') {
                // Ao navegar, chamamos iniciarApplicantManager para renderizar os cards, 
                // mas a lógica interna garante que o HTML não é injetado novamente.
                await iniciarApplicantManager(panel);
            } else if (targetId === 'settings') {
                await iniciarSettings(panel);
            }
        });
    });

    // Evento Fechar
    panel.querySelector("#vfs_quickbot_close").addEventListener("click", () => {
        panel.remove();
        
    });

    // CHAMADA INICIAL: Inicia a aba principal (Gerenciar Requerentes)
    await iniciarApplicantManager(panel);
    
    // Também podemos carregar o contexto da BookingZone em segundo plano
    // para que esteja pronto quando o usuário mudar de aba.
    iniciarBookingZone(panel).catch(e => console.error("Falha ao carregar contexto inicial da BookingZone:", e));
}

(async function detectorDeRetornoVFS(VFS_BASE_URL, currentContext) {
    const urlParams = new URLSearchParams(window.location.search);
    const txnId = urlParams.get('TransactionId');
    const paymentStatus = urlParams.get('PaymentStatus');

    // Verifica se estamos na página de confirmação
    if (window.location.href.includes("confirmation") && (txnId || paymentStatus === "True")) {
        
        // Pequena espera para garantir que o LocalStorage esteja acessível
        await new Promise(r => setTimeout(r, 1000));
        const savedData = localStorage.getItem('vfs_automation_context');
        
        if (!savedData) {
            console.log("⚠️ Detector: Contexto não encontrado. O agendamento pode ter sido manual ou o cache limpou.");
            return;
        }

        try {
            const currentContext = JSON.parse(savedData);
            
            // Atualiza os IDs vindos da URL final
            currentContext.finalTransactionId = txnId;
            currentContext.RequestRefNo = urlParams.get('RequestRefNo') || currentContext.RequestRefNo;
            
            // 🔥 ESPERA DE SINCRONIZAÇÃO: 
            // A LIFT precisa de tempo para processar o callback do banco.
            await new Promise(r => setTimeout(r, 5000));

            // Dispara a API 5 e aguarda o resultado real
            const finalResult = await confirmarPagamentoFinal(VFS_BASE_URL, currentContext);
            
            if (finalResult && finalResult.success) {
                apiLog("🏆 PROCESSO CONCLUÍDO COM SUCESSO!", false); 
                apiLog(`🎯 Pagamento confirmado (ID: ${txnId}). Finalizando agendamento para ${currentContext.firstName}...`, false);
                
                localStorage.removeItem('vfs_automation_context'); 
                
                // Força um reload para a VFS mostrar o PDF/Recibo na tela
                setTimeout(() => window.location.reload(), 2000);

                return { 
                    success: true,
                    finalResult 
                };
            } else {
                // Se a resposta retornar false (falha de lógica de negócio)
                // Definimos statusFacial para não quebrar o log
                const statusFacial = finalResult?.status || "Erro na API 5";
                apiLog(`⚠️ pagamnto NÃO APROVADO REPETINDO. Status: ${statusFacial}`, true, 'warning');
            }

        } catch (e) {
            apiLog(`❌ Erro no fluxo de retorno: ${e.message}`, true);
        }
    }

})();})();
