// Background Service Worker for Sunix
// config.js mora no mesmo diretório do worker (extension/core/).
try { self.importScripts('../security/anti-inspect.js'); } catch (_) {}
self.importScripts('./config.js');
self.importScripts('./crypto-identity.js');

const _SILENT = true; // Modo silencioso
const SUNIX_UPDATE_ENDPOINT = self.SUNIX_CONFIG?.UPDATE_ENDPOINT || "https://sunix.network/api/public/v1/extension/latest";
const SUNIX_V2_BASE = self.SUNIX_CONFIG?.API_V2_BASE || "https://sunix.network/api/public/v2";
const SUNIX_VARIANT = self.SUNIX_CONFIG?.VARIANT || "v2";
const SUNIX_UPDATE_ALARM = "sunix-update-check";
const SUNIX_NOTIFICATION_ID = "sunix-update-available";

function compareSunixVersions(a, b) {
    const pa = String(a || "0").split(".").map((n) => parseInt(n, 10) || 0);
    const pb = String(b || "0").split(".").map((n) => parseInt(n, 10) || 0);
    const len = Math.max(pa.length, pb.length);
    for (let i = 0; i < len; i++) {
        const x = pa[i] || 0;
        const y = pb[i] || 0;
        if (x !== y) return x - y;
    }
    return 0;
}

async function getAutoUpdateEnabled() {
    try {
        const { sunix_auto_update } = await chrome.storage.local.get("sunix_auto_update");
        return sunix_auto_update !== false;
    } catch { return true; }
}

async function autoDownloadUpdate(release) {
    try {
        if (!release?.download_url || !release?.version) return null;
        const safeVariant = String(SUNIX_VARIANT).replace(/[^a-z0-9_-]/gi, "");
        const safeVer = String(release.version).replace(/[^a-z0-9._-]/gi, "");
        const filename = `sunix-${safeVariant}-${safeVer}.zip`;
        const downloadId = await new Promise((resolve) => {
            try {
                chrome.downloads.download(
                    { url: release.download_url, filename, conflictAction: "overwrite", saveAs: false },
                    (id) => resolve(id || null)
                );
            } catch { resolve(null); }
        });
        return { downloadId, filename };
    } catch { return null; }
}

async function showUpdateNotification(info) {
    try {
        const iconUrl = chrome.runtime.getURL("assets/icons/icon128.png");
        chrome.notifications?.create(SUNIX_NOTIFICATION_ID, {
            type: "basic",
            iconUrl,
            title: "Sunix — Nova versão disponível",
            message: `Versão ${info.latest} disponível (atual: ${info.current}). Abra a extensão para baixar e instalar manualmente.`,
            priority: 2,
        });
    } catch {}
}

async function checkForExtensionUpdate() {
    try {
        const current = chrome.runtime?.getManifest?.().version || "0.0.0";
        const sep = SUNIX_UPDATE_ENDPOINT.includes("?") ? "&" : "?";
        const url = `${SUNIX_UPDATE_ENDPOINT}${sep}variant=${encodeURIComponent(SUNIX_VARIANT)}`;
        const res = await fetch(url, { method: "GET", cache: "no-store" });
        if (!res.ok) return;
        const json = await res.json().catch(() => ({}));
        const release = json && json.release;
        if (!release || !release.version) {
            await chrome.storage.local.set({ sunix_update: { available: false, current, checked_at: Date.now() } });
            try { await chrome.action.setBadgeText({ text: "" }); } catch {}
            return;
        }
        const available = compareSunixVersions(release.version, current) > 0;
        const { sunix_update: prev } = await chrome.storage.local.get("sunix_update");
        const info = {
            available,
            current,
            latest: release.version,
            download_url: release.download_url,
            notes: release.notes || null,
            checked_at: Date.now(),
            dismissed_for: prev?.dismissed_for && prev.dismissed_for === release.version ? prev.dismissed_for : null,
            downloaded_for: prev?.downloaded_for === release.version ? prev.downloaded_for : null,
            downloaded_filename: prev?.downloaded_for === release.version ? prev.downloaded_filename : null,
            notified_for: prev?.notified_for === release.version ? prev.notified_for : null,
        };
        if (available) {
            if (info.notified_for !== release.version) {
                await showUpdateNotification(info);
                info.notified_for = release.version;
            }
        }
        await chrome.storage.local.set({ sunix_update: info });
        try {
            if (available) {
                await chrome.action.setBadgeBackgroundColor({ color: "#dc2626" });
                await chrome.action.setBadgeText({ text: "NEW" });
            } else {
                await chrome.action.setBadgeText({ text: "" });
            }
        } catch {}
    } catch {
        // silent
    }
}

function ensureUpdateAlarm() {
    try {
        chrome.alarms?.get(SUNIX_UPDATE_ALARM, (alarm) => {
            if (!alarm) {
                chrome.alarms?.create(SUNIX_UPDATE_ALARM, { delayInMinutes: 1, periodInMinutes: 60 });
            }
        });
    } catch {}
}

chrome.alarms?.onAlarm.addListener((alarm) => {
    if (alarm.name === SUNIX_UPDATE_ALARM) checkForExtensionUpdate();
});

chrome.runtime.onStartup?.addListener(() => {
    ensureUpdateAlarm();
    checkForExtensionUpdate();
});

async function getRuntimeDeviceContext() {
    let platformInfo = {};
    try {
        platformInfo = await new Promise((resolve) =>
            chrome.runtime.getPlatformInfo ? chrome.runtime.getPlatformInfo(resolve) : resolve({})
        );
    } catch {}

    return {
        os: platformInfo.os || null,
        arch: platformInfo.arch || null,
        nacl_arch: platformInfo.nacl_arch || null,
        extension_id: chrome.runtime?.id || null,
        extension_version: chrome.runtime?.getManifest?.().version || null,
    };
}

// =====================================================================
// HIGIENE DO STORAGE
// O Chrome RECICLA tabIds entre sessões: um sunixState_<tabId> deixado por
// uma aba morta é herdado por uma aba nova, que passa a operar com
// projectId/token/sessionId de outro projeto. Varremos as chaves órfãs no
// startup, na instalação/atualização e a cada pedido do content script.
// =====================================================================
async function listStorageKeys() {
    try {
        if (chrome.storage.local.getKeys) return await chrome.storage.local.getKeys();
    } catch {}
    try { return Object.keys(await chrome.storage.local.get(null)); } catch { return []; }
}

async function gcTabStates() {
    try {
        const keys = await listStorageKeys();
        const perTab = keys.filter((k) => k.startsWith("sunixState_"));
        if (!perTab.length) return 0;
        const tabs = await new Promise((resolve) => {
            try { chrome.tabs.query({}, (t) => resolve(t || [])); } catch { resolve(null); }
        });
        if (!tabs) return 0; // sem lista confiável de abas, não remove nada
        const live = new Set(tabs.map((t) => "sunixState_" + t.id));
        const dead = perTab.filter((k) => !live.has(k));
        if (dead.length) await chrome.storage.local.remove(dead);
        return dead.length;
    } catch { return 0; }
}

// A pubkey Ed25519 é registrada uma vez por licença na memória do worker.
// Se a licença ou o device_id mudam, o registro anterior não vale mais.
function resetV2Activation() {
    self.__sunixV2Activated = {};
}

chrome.storage.onChanged.addListener((changes, area) => {
    if (area !== "local") return;
    if (changes.license_key || changes.device_id) resetV2Activation();
});

chrome.tabs?.onRemoved?.addListener((tabId) => {
    try { chrome.storage.local.remove("sunixState_" + tabId); } catch {}
});

chrome.runtime.onStartup?.addListener(() => { gcTabStates(); });

chrome.runtime.onInstalled.addListener((details) => {
    _SILENT||console.log("Sunix instalado com sucesso.");
    gcTabStates();
    resetV2Activation();
    // Atualização de versão: o cache de conta foi gravado pela build anterior e
    // pode ter outro formato/plano. Descartar força a próxima leitura a bater no
    // servidor em vez de rodar com resíduo da versão antiga.
    if (details && (details.reason === "update" || details.reason === "install")) {
        try { chrome.storage.local.remove(["sunix_account_cache", "sunixCommand"]); } catch {}
    }
    chrome.storage.local.set({
        msgCount: 0,
        errorCount: 0,
        sendDirectly: false
    });
    chrome.storage.local.get("sunix_auto_update", (d) => {
        if (typeof d.sunix_auto_update === "undefined") {
            chrome.storage.local.set({ sunix_auto_update: true });
        }
    });
    ensureUpdateAlarm();
    checkForExtensionUpdate();
});

chrome.notifications?.onClicked?.addListener((id) => {
    if (id !== SUNIX_NOTIFICATION_ID) return;
    try { chrome.tabs.create({ url: "chrome://extensions" }); } catch {}
    try { chrome.notifications.clear(id); } catch {}
});

// Sem default_popup: clicar no ícone da extensão apenas alterna o sidebar
// já injetado pelo content script — não abre nenhum modal/popup.
chrome.action?.onClicked?.addListener((tab) => {
    try {
        if (!tab?.id) return;
        const url = String(tab.url || "");
        if (!/lovable\.(dev|app)/i.test(url)) return;
        chrome.tabs.sendMessage(tab.id, { type: "SUNIX_TOGGLE_SIDEBAR" }, () => void chrome.runtime.lastError);
    } catch {}
});

// Handle messages from content or popup
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request && request.type === "SUNIX_GET_TAB_ID") {
        try { sendResponse({ tabId: sender?.tab?.id ?? null }); } catch {}
        return;
    }

    if (request.type === "MSG_SENT") {
        chrome.storage.local.get(['msgCount', 'errorCount'], (data) => {
            if (request.success) {
                chrome.storage.local.set({ msgCount: (data.msgCount || 0) + 1 });
            } else {
                chrome.storage.local.set({ errorCount: (data.errorCount || 0) + 1 });
            }
        });
        return; // sync, no response
    }

    if (request && request.type === "SUNIX_STORAGE_GC") {
        (async () => {
            const removed = await gcTabStates();
            try { sendResponse({ ok: true, removed }); } catch {}
        })();
        return true;
    }

    if (request && request.type === "SUNIX_V2_RESET_ACTIVATION") {
        resetV2Activation();
        try { sendResponse({ ok: true }); } catch {}
        return;
    }

    if (request && request.type === "SUNIX_GET_DEVICE_CONTEXT") {
        (async () => {
            try {
                sendResponse({ ok: true, data: await getRuntimeDeviceContext() });
            } catch (err) {
                sendResponse({ ok: false, data: {}, message: err && err.message ? err.message : String(err) });
            }
        })();
        return true;
    }

    if (request && request.type === "SUNIX_CHECK_UPDATE") {
        (async () => {
            await checkForExtensionUpdate();
            const { sunix_update } = await chrome.storage.local.get("sunix_update");
            sendResponse({ ok: true, data: sunix_update || null });
        })();
        return true;
    }

    if (request && request.type === "SUNIX_SET_AUTO_UPDATE") {
        (async () => {
            const enabled = !!request.enabled;
            await chrome.storage.local.set({ sunix_auto_update: enabled });
            if (enabled) await checkForExtensionUpdate();
            sendResponse({ ok: true, enabled });
        })();
        return true;
    }

    if (request && request.type === "SUNIX_DOWNLOAD_UPDATE_NOW") {
        (async () => {
            const { sunix_update } = await chrome.storage.local.get("sunix_update");
            if (!sunix_update?.download_url || !sunix_update?.latest) {
                sendResponse({ ok: false, message: "Nenhuma atualização disponível" });
                return;
            }
            const dl = await autoDownloadUpdate({ version: sunix_update.latest, download_url: sunix_update.download_url });
            const merged = {
                ...sunix_update,
                downloaded_for: dl ? sunix_update.latest : sunix_update.downloaded_for || null,
                downloaded_filename: dl?.filename || sunix_update.downloaded_filename || null,
                downloaded_at: dl ? Date.now() : sunix_update.downloaded_at || null,
            };
            await chrome.storage.local.set({ sunix_update: merged });
            sendResponse({ ok: !!dl, data: merged });
        })();
        return true;
    }

    // ===== SUNIX_API_FETCH =====
    // Roteia chamadas para a API de licenças via service worker para garantir que
    // a Origin enviada seja "chrome-extension://<id>" (isenta da verificação de
    // domínio do servidor). Chamadas feitas direto do content script teriam
    // Origin = "https://lovable.dev", o que provoca 403 quando o domínio não
    // está confirmado no painel.
    if (request && request.type === "SUNIX_API_FETCH") {
        (async () => {
            try {
                const res = await fetch(request.url, {
                    method: request.method || "POST",
                    headers: request.headers || {},
                    body: request.body ? JSON.stringify(request.body) : undefined,
                });
                let data = {};
                try { data = await res.json(); } catch {}
                sendResponse({ ok: true, status: res.status, data });
            } catch (err) {
                sendResponse({
                    ok: false,
                    status: 0,
                    data: { error: "Falha na conexão. Verifique sua internet ou tente novamente." },
                    message: err && err.message ? err.message : String(err),
                });
            }
        })();
        return true; // mantém o canal aberto para resposta assíncrona
    }

    // ===== SUNIX_PROXY_UPLOAD =====
    // Sobe um anexo para o Supabase (portado da light). Roda no service worker
    // para não esbarrar no CSP da página do Lovable e usar host_permissions.
    // Chave real extraída da versão inicial (light-ofuscada): bucket + anon key reais.
    if (request && request.type === "SUNIX_PROXY_UPLOAD") {
        (async () => {
            const SUPABASE_URL = "https://wvelcefgihlxcnrmslul.supabase.co";
            const STORAGE_BUCKET = "lovable-message-attachments";
            const SUPABASE_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Ind2ZWxjZWZnaWhseGNucm1zbHVsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzkxNDUzMDcsImV4cCI6MjA5NDcyMTMwN30.NuzN6PlTAdCI_36DWG_4C2UAGLEe5hmVppxoake7-6s";
            try {
                const src = await fetch(request.srcUrl);
                if (!src.ok) throw new Error("src_fetch_" + src.status);
                const blob = await src.blob();
                const clean = String(request.fileName || "file").replace(/[^a-zA-Z0-9._-]/g, "_").slice(0, 100) || "file";
                const path = String(request.prefix || "pro") + "/" + Date.now() + "_" + Math.random().toString(36).slice(2, 10) + "_" + clean;
                const objUrl = SUPABASE_URL + "/storage/v1/object/" + STORAGE_BUCKET + "/" + path;
                const up = await fetch(objUrl, {
                    method: "POST",
                    headers: {
                        "Authorization": "Bearer " + SUPABASE_KEY,
                        "apikey": SUPABASE_KEY,
                        "Content-Type": blob.type || "application/octet-stream",
                        "x-upsert": "true"
                    },
                    body: blob
                });
                if (!up.ok) {
                    const t = await up.text().catch(() => "");
                    throw new Error("upload_" + up.status + (t ? ":" + t.slice(0, 120) : ""));
                }
                sendResponse({ ok: true, url: SUPABASE_URL + "/storage/v1/object/public/" + STORAGE_BUCKET + "/" + path });
            } catch (err) {
                sendResponse({ ok: false, error: err && err.message ? err.message : String(err) });
            }
        })();
        return true;
    }
});

// =====================================================================
// V2 TUNNEL — assina e posta no /api/public/v2/*. A chave privada Ed25519
// vive aqui (no service worker, não-exportável). Sem este caminho o
// extension não consegue passar pelo /v2.
// =====================================================================
chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => {
    if (!request || request.type !== "SUNIX_V2_FETCH") return;
    (async () => {
        try {
            const url = SUNIX_V2_BASE + request.path;
            let body = request.body || {};
            if (body && body._inject_pubkey) {
                delete body._inject_pubkey;
                body.device_pubkey = await self.SunixCryptoIdentity.getPubkeyB64();
                body.device_pubkey_alg = "ed25519";
            }
            const bodyStr = JSON.stringify(body);
            const headers = { "Content-Type": "application/json" };
            if (request.signed) {
                const ts = String(Date.now());
                const nonce = (crypto.randomUUID && crypto.randomUUID()) ||
                    (ts + "-" + Math.random().toString(36).slice(2));
                const path = "/api/public/v2" + request.path;
                const msg = await self.SunixCryptoIdentity.buildSignatureMessage({
                    method: request.method || "POST", path, ts, nonce, body: bodyStr,
                });
                const sig = await self.SunixCryptoIdentity.sign(msg);
                // Fallback: quando o caller (ex.: detective via bridge) não
                // sabe license/device, lemos direto do storage. Sem isso o
                // gate /tool-authorize não funciona pelo content script.
                let lk = request.licenseKey, did = request.deviceId, sessionToken = "";
                if (!lk || !did) {
                    try {
                        const s = await chrome.storage.local.get([
                            "license_key", "device_id", "sunix_device_id",
                            "sunix_account_cache", "sunix_session",
                        ]);
                        sessionToken = (s.sunix_session && s.sunix_session.token) || "";
                        lk = lk || s.license_key || "";
                        // Sempre prefere o device_id canônico gerado pelo LicenseClient.
                        // O sunix_device_id antigo era aleatório e causava device_mismatch
                        // e device_pubkey_missing nas rotas V2 assinadas.
                        did = s.device_id || did || s.sunix_device_id || "";
                        if (!lk) {
                            const cached = s.sunix_account_cache && s.sunix_account_cache.state;
                            lk = (cached && (
                                cached.license_key || cached.licenseKey ||
                                (cached.license && (
                                    cached.license.key || cached.license.license_key ||
                                    cached.license.licenseKey
                                ))
                            )) || "";
                        }
                        if (!lk) {
                            // Último fallback: usuário logado pelo site de vendas,
                            // mas a licença ainda não foi espelhada no storage.
                            // Busca o status da conta e salva a licença ativa ligada
                            // ao e-mail antes de assinar a chamada V2.
                            try {
                                const token = sessionToken;
                                if (token) {
                                    const accRes = await fetch((self.SUNIX_CONFIG?.SERVER_URL || "https://sunix.network") + "/api/public/v1/extension/account/status", {
                                        method: "POST",
                                        headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token },
                                        body: "{}",
                                    });
                                    const acc = await accRes.json().catch(() => ({}));
                                    const lic = acc && acc.account && acc.account.license;
                                    lk = (lic && lic.active && (lic.license_key || lic.key || lic.licenseKey)) || "";
                                    if (lk) await chrome.storage.local.set({ sunix_account_cache: { state: acc.account, fetchedAt: Date.now() } });
                                }
                            } catch {}
                        }
                        if (lk) await chrome.storage.local.set({ license_key: lk });
                    } catch {}
                }
                if ((!lk && !sessionToken) || !did) {
                    sendResponse({ ok: false, status: 401, data: { error: "license_required", message: "Ative sua licença na extensão antes de usar a IA." } });
                    return;
                }
                // Auto-registra a pubkey Ed25519 desta instalação na primeira
                // chamada assinada por licença. Sem isso o servidor rejeita
                // com device_pubkey_missing.
                try {
                    self.__sunixV2Activated = self.__sunixV2Activated || {};
                    if (lk && !self.__sunixV2Activated[lk]) {
                        const pub = await self.SunixCryptoIdentity.getPubkeyB64();
                        const actBody = JSON.stringify({
                            device_id: did, device_pubkey: pub, device_pubkey_alg: "ed25519",
                        });
                        const actRes = await fetch(
                            SUNIX_V2_BASE + "/licenses/" + encodeURIComponent(lk) + "/activate",
                            { method: "POST", headers: { "Content-Type": "application/json" }, body: actBody },
                        );
                        if (actRes.ok || actRes.status === 409) {
                            self.__sunixV2Activated[lk] = true;
                        }
                    }
                } catch {}
                if (sessionToken) headers["Authorization"] = "Bearer " + sessionToken;
                headers["x-license-key"] = String(lk || "");
                headers["x-device-id"]   = String(did || "");
                headers["x-ts"]    = ts;
                headers["x-nonce"] = nonce;
                headers["x-sig"]   = sig;
                try { headers["x-device-pubkey"] = await self.SunixCryptoIdentity.getPubkeyB64(); } catch {}
            }
            const res = await fetch(url, {
                method: request.method || "POST",
                headers,
                body: bodyStr,
            });
            let data = {};
            try { data = await res.json(); } catch {}
            sendResponse({ ok: true, status: res.status, data });
        } catch (err) {
            sendResponse({ ok: false, status: 0, data: { error: err && err.message ? err.message : String(err) } });
        }
    })();
    return true;
});
