import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.39.7";
import { GoogleGenerativeAI } from "https://esm.sh/@google/generative-ai@0.21.0";
import { SYSTEM_PROMPT, COACH_SYSTEM_PROMPT } from "./prompt.ts";
import { buildCoachPdfHtml } from "./pdf-template.ts";

// ─── Config ────────────────────────────────────────────────────────
const SUPABASE_URL = Deno.env.get("SUPABASE_URL") as string;
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") as string;
const GEMINI_API_KEY = Deno.env.get("GEMINI_API_KEY") as string;
const META_VERIFY_TOKEN = Deno.env.get("META_VERIFY_TOKEN") as string;
const META_ACCESS_TOKEN = Deno.env.get("META_ACCESS_TOKEN") as string;
const META_PHONE_NUMBER_ID = Deno.env.get("META_PHONE_NUMBER_ID") as string;
const IMAGE_RENDERER_URL = Deno.env.get("IMAGE_RENDERER_URL");

const GRAPH_API_URL = "https://graph.facebook.com/v19.0";
const FREE_FOOD_LIMIT = 5;

// ─── Types ─────────────────────────────────────────────────────────
interface MetaWebhookPayload {
    object: string;
    entry?: {
        id: string;
        changes?: {
            value?: {
                messaging_product: string;
                metadata?: { display_phone_number: string; phone_number_id: string };
                contacts?: { profile: { name: string }; wa_id: string }[];
                messages?: {
                    from: string;
                    id: string;
                    timestamp: string;
                    type: string;
                    text?: { body: string };
                    button?: { payload: string, text: string };
                }[];
                statuses?: {
                    id: string;
                    status: string;
                    timestamp: string;
                    recipient_id: string;
                    errors?: {
                        code: number;
                        title: string;
                        message: string;
                        error_data?: { details: string };
                    }[];
                }[];
            };
            field: string;
        }[];
    }[];
}

// ─── Helpers ───────────────────────────────────────────────────────

/** Remove tudo que não é dígito */
const onlyDigits = (s: string) => s.replace(/\D/g, "");

/**
 * Gera candidatos de número brasileiro (com/sem DDI 55, com/sem 9º dígito).
 * Usado para fazer match com profiles.phone_e164 e profiles.phone.
 */
function generatePhoneCandidates(raw: string): string[] {
    const candidates: string[] = [];
    const num = onlyDigits(raw);
    if (!num) return candidates;

    candidates.push(num);

    const withoutDDI = num.startsWith("55") ? num.slice(2) : num;
    if (withoutDDI !== num) candidates.push(withoutDDI);
    if (!num.startsWith("55")) candidates.push("55" + num);

    const ddd = withoutDDI.slice(0, 2);
    const rest = withoutDDI.slice(2);

    // Adiciona 9º dígito se tem 8 dígitos após DDD
    if (rest.length === 8) {
        const with9 = ddd + "9" + rest;
        candidates.push(with9);
        candidates.push("55" + with9);
    }

    // Remove 9º dígito se tem 9 dígitos após DDD
    if (rest.length === 9 && rest.startsWith("9")) {
        const without9 = ddd + rest.slice(1);
        candidates.push(without9);
        candidates.push("55" + without9);
    }

    return candidates;
}

/** Envia mensagem de texto via Meta Cloud API */
async function sendWhatsAppMessage(remoteJid: string, text: string) {
    if (!META_ACCESS_TOKEN || !META_PHONE_NUMBER_ID) return;
    try {
        const url = `${GRAPH_API_URL}/${META_PHONE_NUMBER_ID}/messages`;
        const payload = {
            messaging_product: "whatsapp",
            recipient_type: "individual",
            to: remoteJid,
            type: "text",
            text: { preview_url: false, body: text }
        };
        const res = await fetch(url, {
            method: "POST",
            headers: { "Content-Type": "application/json", Authorization: `Bearer ${META_ACCESS_TOKEN}` },
            body: JSON.stringify(payload)
        });
        const resBody = await res.text();
        console.log(`[META-WH] SendText status: ${res.status}`);
    } catch (err) {
        console.error("[META-WH] Error sending text:", err);
    }
}

/** Envia mensagem interativa CTA via Meta Cloud API */
async function sendWhatsAppInteractiveMessage(remoteJid: string, text: string, buttonText: string, linkUrl: string) {
    if (!META_ACCESS_TOKEN || !META_PHONE_NUMBER_ID) return;
    try {
        const url = `${GRAPH_API_URL}/${META_PHONE_NUMBER_ID}/messages`;
        const payload = {
            messaging_product: "whatsapp",
            recipient_type: "individual",
            to: remoteJid,
            type: "interactive",
            interactive: {
                type: "cta_url",
                body: { text: text },
                footer: { text: "FoodSnap PRO" },
                action: {
                    name: "cta_url",
                    parameters: {
                        display_text: buttonText,
                        url: linkUrl
                    }
                }
            }
        };
        const res = await fetch(url, {
            method: "POST",
            headers: { "Content-Type": "application/json", Authorization: `Bearer ${META_ACCESS_TOKEN}` },
            body: JSON.stringify(payload)
        });
        if (!res.ok) {
            await sendWhatsAppMessage(remoteJid, `${text}\n\n👉 Acesse: ${linkUrl}`);
        }
    } catch (err) {
        await sendWhatsAppMessage(remoteJid, `${text}\n\n👉 Acesse: ${linkUrl}`);
    }
}

/** Envia botões de resposta rápida via Meta Cloud API (Máx 3 botões) */
async function sendWhatsAppInteractiveButtons(remoteJid: string, text: string, buttons: { id: string, title: string }[]) {
    if (!META_ACCESS_TOKEN || !META_PHONE_NUMBER_ID) return;
    try {
        const url = `${GRAPH_API_URL}/${META_PHONE_NUMBER_ID}/messages`;
        const payload = {
            messaging_product: "whatsapp",
            recipient_type: "individual",
            to: remoteJid,
            type: "interactive",
            interactive: {
                type: "button",
                body: { text: text },
                action: {
                    buttons: buttons.map(b => ({
                        type: "reply",
                        reply: { id: b.id, title: b.title.substring(0, 20) }
                    }))
                }
            }
        };
        const res = await fetch(url, {
            method: "POST",
            headers: { "Content-Type": "application/json", Authorization: `Bearer ${META_ACCESS_TOKEN}` },
            body: JSON.stringify(payload)
        });
        console.log(`[META-WH] SendButtons status: ${res.status}`);
    } catch (err) {
        console.error("[META-WH] Error sending buttons:", err);
    }
}

/** Envia documento (PDF) via Meta Cloud API (Link) */
async function sendWhatsAppDocument(remoteJid: string, mediaUrl: string, fileName: string, caption?: string) {
    if (!META_ACCESS_TOKEN || !META_PHONE_NUMBER_ID) return;
    try {
        const url = `${GRAPH_API_URL}/${META_PHONE_NUMBER_ID}/messages`;
        const payload = {
            messaging_product: "whatsapp",
            recipient_type: "individual",
            to: remoteJid,
            type: "document",
            document: { link: mediaUrl, filename: fileName, caption: caption || "" }
        };
        const res = await fetch(url, {
            method: "POST",
            headers: { "Content-Type": "application/json", Authorization: `Bearer ${META_ACCESS_TOKEN}` },
            body: JSON.stringify(payload)
        });
        console.log(`[META-WH] SendDoc status: ${res.status}`);
    } catch (err) {
        console.error("[META-WH] Error sending document:", err);
    }
}

/** Envia imagem real via Meta Cloud API (Link) */
async function sendWhatsAppImage(remoteJid: string, imageUrl: string, caption?: string) {
    if (!META_ACCESS_TOKEN || !META_PHONE_NUMBER_ID) return;
    try {
        const url = `${GRAPH_API_URL}/${META_PHONE_NUMBER_ID}/messages`;
        const payload = {
            messaging_product: "whatsapp",
            recipient_type: "individual",
            to: remoteJid,
            type: "image",
            image: { link: imageUrl, caption: caption || "" }
        };
        const res = await fetch(url, {
            method: "POST",
            headers: { "Content-Type": "application/json", Authorization: `Bearer ${META_ACCESS_TOKEN}` },
            body: JSON.stringify(payload)
        });
        console.log(`[META-WH] SendImage status: ${res.status}`);
    } catch (err) {
        console.error("[META-WH] Error sending image:", err);
    }
}

/** Busca imagem em base64 da Meta API através do Media ID */
async function getWhatsAppMedia(mediaId: string): Promise<string | null> {
    if (!META_ACCESS_TOKEN) return null;
    try {
        // Passo 1: Obter URL da mídia
        const urlRes = await fetch(`${GRAPH_API_URL}/${mediaId}`, {
            headers: { Authorization: `Bearer ${META_ACCESS_TOKEN}` }
        });
        if (!urlRes.ok) return null;
        const urlData = await urlRes.json();
        const mediaUrl = urlData.url;

        // Passo 2: Baixar a mídia real como binário
        const mediaRes = await fetch(mediaUrl, {
            headers: { Authorization: `Bearer ${META_ACCESS_TOKEN}` }
        });
        if (!mediaRes.ok) return null;

        const arrayBuffer = await mediaRes.arrayBuffer();
        const buffer = new Uint8Array(arrayBuffer);

        // Convert to Base64 in chunks to avoid call stack limits
        let binary = '';
        const len = buffer.byteLength;
        for (let i = 0; i < len; i++) {
            binary += String.fromCharCode(buffer[i]);
        }
        return btoa(binary);
    } catch (err) {
        console.error("[META-WH] Error fetching media:", err);
        return null;
    }
}

/** Converte base64 → Uint8Array (para upload storage) */
function base64ToUint8Array(base64: string): Uint8Array {
    const bin = atob(base64);
    const bytes = new Uint8Array(bin.length);
    for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
    return bytes;
}

// ─── Geração de HTML para PDF do Coach ────────────────────────────
// (Movido para pdf-template.ts)

// ─── Normalização e limpeza do JSON do Gemini (portado do n8n) ────

const toNum = (v: unknown): number => {
    if (typeof v === "number") return v;
    if (typeof v === "string") {
        const n = Number(v.replace(",", ".").trim());
        return Number.isFinite(n) ? n : 0;
    }
    return 0;
};

const ensureArray = (v: unknown): any[] => (Array.isArray(v) ? v : []);

const keyName = (s: string) =>
    (s || "")
        .trim()
        .toLowerCase()
        .normalize("NFD")
        .replace(/[\u0300-\u036f]/g, "");

const clampConfidence = (c: string) => {
    const k = keyName(c);
    if (k.includes("alta")) return "alta";
    if (k.includes("baixa")) return "baixa";
    return "media";
};

const CITRUS_VARIANTS = /^(tangerina|bergamota|mandarina|clementina|mexerica)/;

const CANONICAL_MAP = [
    { match: /^laranja/, canonical: "Laranja" },
    { match: /^banana/, canonical: "Banana" },
    { match: /^maca|^maçã/, canonical: "Maçã" },
    { match: /^pera/, canonical: "Pera" },
    { match: /^uva/, canonical: "Uva" },
    { match: /^abacaxi/, canonical: "Abacaxi" },
    { match: /^melancia/, canonical: "Melancia" },
    { match: /^melao|^melão/, canonical: "Melão" },
];

function canonicalizeName(name: string): string {
    const k = keyName(name);
    if (CITRUS_VARIANTS.test(k)) return "Laranja";
    for (const rule of CANONICAL_MAP) {
        if (rule.match.test(k)) return rule.canonical;
    }
    return (name || "").trim();
}

const stripCitrusMention = (s: string) => {
    const k = keyName(s);
    if (/(tangerina|bergamota|mandarina|clementina|mexerica)/.test(k)) {
        return s
            .replace(/tangerina\/bergamota/gi, "laranja")
            .replace(/tangerina|bergamota|mandarina|clementina|mexerica/gi, "laranja")
            .trim();
    }
    return s;
};

const parseUnitsPortion = (portion: string) => {
    const p = (portion || "").toLowerCase().replace(",", ".");
    const um = p.match(/(\d+)\s*unidades?/);
    const g = p.match(/(\d+(\.\d+)?)\s*g/);
    return {
        units: um ? Number(um[1]) : null,
        grams: g ? Math.round(Number(g[1])) : null,
    };
};

const buildUnitsPortion = (units: number | null, grams: number | null) => {
    const u = units && units > 0 ? units : null;
    const g = grams && grams > 0 ? grams : null;
    if (u && g) return `${u} unidades (${g}g)`;
    if (u) return `${u} unidades`;
    if (g) return `${g}g`;
    return "";
};

/**
 * Recebe o texto cru do Gemini e retorna o objeto normalizado
 * (portado do nó "Limpar Resultado" do n8n)
 */
function parseAndCleanGeminiResponse(rawText: string): any {
    // Limpa markdown
    let cleaned = rawText.replace(/```json/gi, "").replace(/```/g, "").trim();

    // Extrai JSON
    const m = cleaned.match(/\{[\s\S]*\}/);
    if (!m) throw new Error("JSON não encontrado na resposta do Gemini.");
    let jsonStr = m[0];

    // Corrige JSON mal formado
    jsonStr = jsonStr.replace(/:\s*\+(\d+(\.\d+)?)/g, ": $1");
    jsonStr = jsonStr.replace(/,\s*([}\]])/g, "$1");

    const parsed = JSON.parse(jsonStr);

    // Normaliza items
    parsed.items = ensureArray(parsed.items).map((it: any) => {
        const rawName = (it.name || "").trim();
        const k = keyName(rawName);
        const flags = ensureArray(it.flags);
        const name = canonicalizeName(rawName);
        const nextFlags = CITRUS_VARIANTS.test(k)
            ? Array.from(new Set([...flags, "tipo_duvidoso"]))
            : flags;

        return {
            ...it,
            name,
            portion: (it.portion || "").trim(),
            calories: toNum(it.calories),
            protein: toNum(it.protein),
            carbs: toNum(it.carbs),
            fat: toNum(it.fat),
            fiber: toNum(it.fiber),
            sugar: toNum(it.sugar),
            sodium_mg: toNum(it.sodium_mg),
            flags: nextFlags,
        };
    });

    // Deduplica por nome
    const byName = new Map<string, any>();
    for (const it of parsed.items) {
        const k = keyName(it.name);
        if (!k) continue;

        if (!byName.has(k)) {
            byName.set(k, it);
            continue;
        }

        const cur = byName.get(k);
        const a = parseUnitsPortion(cur.portion);
        const b = parseUnitsPortion(it.portion);
        let mergedPortion = cur.portion;
        if (a.units !== null || b.units !== null || a.grams !== null || b.grams !== null) {
            const units = (a.units || 0) + (b.units || 0);
            const grams = (a.grams || 0) + (b.grams || 0);
            const rebuilt = buildUnitsPortion(units || null, grams || null);
            if (rebuilt) mergedPortion = rebuilt;
        }

        byName.set(k, {
            ...cur,
            portion: mergedPortion,
            calories: toNum(cur.calories) + toNum(it.calories),
            protein: toNum(cur.protein) + toNum(it.protein),
            carbs: toNum(cur.carbs) + toNum(it.carbs),
            fat: toNum(cur.fat) + toNum(it.fat),
            fiber: toNum(cur.fiber) + toNum(it.fiber),
            sugar: toNum(cur.sugar) + toNum(it.sugar),
            sodium_mg: toNum(cur.sodium_mg) + toNum(it.sodium_mg),
            flags: Array.from(
                new Set([...ensureArray(cur.flags), ...ensureArray(it.flags), "deduplicado"])
            ),
        });
    }
    parsed.items = Array.from(byName.values());

    // Recalcula totais
    const sum = (arr: any[], f: string) => arr.reduce((a: number, b: any) => a + toNum(b[f]), 0);
    parsed.total = {
        calories: Math.round(sum(parsed.items, "calories")),
        protein: +sum(parsed.items, "protein").toFixed(1),
        carbs: +sum(parsed.items, "carbs").toFixed(1),
        fat: +sum(parsed.items, "fat").toFixed(1),
        fiber: +sum(parsed.items, "fiber").toFixed(1),
        sugar: +sum(parsed.items, "sugar").toFixed(1),
        sodium_mg: Math.round(sum(parsed.items, "sodium_mg")),
    };

    // Outros campos
    parsed.health_score = toNum(parsed.health_score);
    parsed.confidence = clampConfidence(parsed.confidence || "");
    parsed.assumptions = ensureArray(parsed.assumptions).map(stripCitrusMention);
    parsed.questions = ensureArray(parsed.questions);
    parsed.insights = ensureArray(parsed.insights).map(stripCitrusMention);
    parsed.swap_suggestions = ensureArray(parsed.swap_suggestions);
    parsed.next_best_actions = ensureArray(parsed.next_best_actions);

    parsed.tip =
        parsed.tip && typeof parsed.tip === "object"
            ? parsed.tip
            : { title: "", text: "", reason: "" };
    parsed.tip.title = String(parsed.tip.title || "");
    parsed.tip.text = stripCitrusMention(String(parsed.tip.text || ""));
    parsed.tip.reason = stripCitrusMention(String(parsed.tip.reason || ""));

    return parsed;
}

/**
 * Formata a análise em mensagem rica para WhatsApp
 * (portado do nó "Formatar Resposta WHATS" do n8n)
 */
function formatWhatsAppResponse(analysis: any): string {
    if (!analysis || !Array.isArray(analysis.items) || !analysis.items.length) {
        return "⚠️ Não foi possível identificar um alimento válido na imagem. Tente uma foto com melhor iluminação ou de um ângulo diferente.";
    }

    const items = analysis.items;
    const total = analysis.total || {};

    const fmt = (n: unknown) => {
        if (n === undefined || n === null || n === "") return "—";
        const num = Number(n);
        if (!Number.isFinite(num)) return String(n);
        return (Math.round(num * 10) / 10).toString();
    };

    const v = (x: unknown) => (x === undefined || x === null || x === "" ? "—" : x);
    const lines: string[] = [];

    let scoreEmoji = "🟢"; // High score
    if (analysis.health_score !== undefined) {
        const score = Number(analysis.health_score);
        if (score < 50) scoreEmoji = "🔴";
        else if (score < 80) scoreEmoji = "🟡";
    }

    // PREMIUM LAYOUT
    lines.push("📱 *FOODSNAP ANALYTICS*");
    lines.push("");
    lines.push(`🔥 *CALORIAS:* ${fmt(total.calories)} kcal`);
    if (analysis.health_score !== undefined) {
        lines.push(`🏆 *SCORE:* ${fmt(analysis.health_score)}/100 ${scoreEmoji}`);
    }
    lines.push("");
    lines.push("🧬 *MACROS*");
    lines.push(`▪ *Proteína:* ${fmt(total.protein)}g`);
    lines.push(`▪ *Carboidrato:* ${fmt(total.carbs)}g`);
    lines.push(`▪ *Gordura:* ${fmt(total.fat)}g`);
    lines.push("");
    lines.push("🥗 *O QUE ENCONTREI*");
    items.forEach((it: any) => {
        lines.push(`• ${v(it.name)} (${v(it.portion)})`);
    });
    lines.push("");
    lines.push(`📌 *Fibras:* ${fmt(total.fiber)}g | *Açúcares:* ${fmt(total.sugar)}g | *Sódio:* ${fmt(total.sodium_mg)}mg`);

    if (analysis.insights && Array.isArray(analysis.insights) && analysis.insights.length > 0) {
        lines.push("");
        lines.push("🔎 *VEREDITO:*");
        analysis.insights.forEach((insight: string) => {
            lines.push(`• ${insight}`);
        });
    }

    if (analysis.tip && analysis.tip.text) {
        lines.push("");
        lines.push(`💡 _DICA:_ ${analysis.tip.text}`);
    }

    return lines.join("\n");
}

// ─── Main Handler ──────────────────────────────────────────────────

serve(async (req) => {
    // ── 0. Verificação do Webhook (GET) ───────────
    if (req.method === "GET") {
        const url = new URL(req.url);
        const mode = url.searchParams.get("hub.mode");
        const token = url.searchParams.get("hub.verify_token");
        const challenge = url.searchParams.get("hub.challenge");

        if (mode === "subscribe" && token === META_VERIFY_TOKEN) {
            console.log("[META-WH] Webhook concluído e certificado pela Meta!");
            return new Response(challenge, { status: 200 });
        } else {
            return new Response("Forbidden", { status: 403 });
        }
    }

    if (req.method !== "POST") {
        return new Response("Method not allowed", { status: 405 });
    }

    try {
        const payload: MetaWebhookPayload = await req.json();

        if (payload.object !== "whatsapp_business_account") {
            return new Response("Ignored", { status: 200 });
        }

        const entry = payload.entry?.[0];
        const changes = entry?.changes?.[0];
        const value = changes?.value;
        const messages = value?.messages;
        const statuses = value?.statuses;

        // Se for apenas status lido/entregue, ignora
        if (!messages || !messages[0]) {
            if (statuses && statuses[0]?.status === "failed") {
                console.error("[META-WH] MESSAGE DELIVERY FAILED:", JSON.stringify(statuses[0].errors));
            } else if (statuses) {
                console.log("[META-WH] Message Status Update:", statuses[0]?.status);
            }
            return new Response("No message to process", { status: 200 });
        }

        // --- ASYNC PROCESS TO PREVENT META 3s TIMEOUT --- //
        // A Meta precisa que respondamos 200 OK quase imediatamente.
        // Se a gente prender o hook chamando a API do Gemini,
        // a Meta acha que falhou e manda DE NOVO, duplicando a mensagem.
        // Então rodamos o bloco principal em background no Node (Promise flutuante),
        // sem usar "await" na borda.

        processMetaMessage(messages[0]);

        return new Response("Processing started", { status: 200 });

    } catch (err) {
        console.error("[WH] Fatal error", err);
        return new Response("Internal Server Error", { status: 500 });
    }
});

// ─── LÓGICA ASSÍNCRONA DESACOPLADA (Background) ──────────────────────
async function processMetaMessage(msg: any) {
    try {
        const remoteJid = msg.from; // Formato puro sem @s.whatsapp.net

        // ── 1. Extrair dados ────────────────────────────────────────
        const senderNumber = onlyDigits(remoteJid);
        const senderFromPayload = "";

        // Em vez de salvar o ID da mensagem como texto, salvamos o Media ID se for imagem
        const messageId = msg.image?.id || msg.id;
        const isImage = msg.type === "image";

        let textMessage = "";
        let interactiveId = "";
        if (msg.type === "text") textMessage = msg.text?.body || "";
        if (msg.type === "button") textMessage = msg.button?.text || "";
        if (msg.type === "interactive") {
            interactiveId = msg.interactive?.button_reply?.id || msg.interactive?.list_reply?.id || "";
            textMessage = msg.interactive?.button_reply?.title || msg.interactive?.list_reply?.title || "";
        }

        console.log(`[WH] sender=${senderNumber}, isImage=${isImage}, text="${textMessage.slice(0, 50)}", id="${interactiveId}"`);

        // Gerar candidatos de número BR
        const allCandidates = [
            ...generatePhoneCandidates(senderNumber),
            ...(senderFromPayload ? generatePhoneCandidates(senderFromPayload) : []),
        ];
        const phoneCandidates = [...new Set(allCandidates)];
        console.log(`[WH] phoneCandidates: ${JSON.stringify(phoneCandidates)}`);

        // ── 2. Init Supabase ────────────────────────────────────────
        const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);

        // ── 3. Buscar usuário com phone_candidates ──────────────────
        let user: { id: string, current_streak?: number, longest_streak?: number, last_scan_date?: string } | null = null;

        for (const candidate of phoneCandidates) {
            const { data: directMatch, error: matchErr } = await supabase
                .from("profiles")
                .select("id, current_streak, longest_streak, last_scan_date")
                .or(`phone_e164.eq.${candidate},phone.eq.${candidate}`)
                .maybeSingle();

            if (matchErr) {
                console.error(`[WH] DB error matching candidate ${candidate}:`, matchErr.message);
            }
            if (directMatch) {
                user = directMatch;
                console.log(`[WH] User found: ${user.id} (matched candidate: ${candidate})`);
                break;
            }
        }

        if (!user) {
            console.log(`[WH] User NOT found for candidates: ${phoneCandidates.join(", ")}`);
            await sendWhatsAppInteractiveMessage(
                remoteJid,
                "🚫 *Seu número não está cadastrado no FoodSnap*.\n\nPara usar a inteligência artificial via WhatsApp, você precisa ter uma conta ativa e o seu celular atualizado no perfil.",
                "📲 Criar Conta Grátis",
                "https://foodsnap.com.br"
            );
            return;
        }

        const userId = user.id;

        // ── 4. Estado da conversa (Coach state machine) ─────────────
        let { data: conv } = await supabase
            .from("whatsapp_conversations")
            .select("*")
            .eq("phone_number", senderNumber)
            .maybeSingle();

        // ── 3.5 Prevenir Mensagens Duplicadas (Retries da Meta) ──────
        if (conv && conv.last_msg_id === messageId) {
            console.log(`[WH] Repeated message ignored: ${messageId}`);
            return;
        }

        // ── 4. Atualizar Contexto (Upsert) ──────────────────────────
        if (!conv) {
            const { data: newConv } = await supabase
                .from("whatsapp_conversations")
                .insert({
                    phone_number: senderNumber,
                    contact_name: senderFromPayload,
                    state: "IDLE",
                    temp_data: {},
                    last_msg_id: messageId
                })
                .select()
                .single();
            conv = newConv;
            console.log(`[WH] New conversation for ${senderNumber}`);
        } else {
            // Atualizar o last_msg_id mesmo se não mudarmos o state agora
            await supabase
                .from("whatsapp_conversations")
                .update({ last_msg_id: messageId, updated_at: new Date().toISOString() })
                .eq("id", conv.id);
        }

        const state = conv?.state || "IDLE";
        console.log(`[WH] Conversation state: ${state}, conv exists: ${!!conv}`);

        // ── 5. Coach e Recomendações ────────────────────────────────

        // TRIGGER: action_recommend (O que mais comer?)
        if (state === "IDLE" && (interactiveId === "action_recommend" || /oque comer|o que comer|recomenda[çc]/i.test(textMessage))) {
            const lastAnalysisText = conv?.temp_data?.last_analysis;
            if (!lastAnalysisText) {
                await sendWhatsAppMessage(remoteJid, "Por favor, envie a foto de um prato primeiro.");
                return;
            }

            await sendWhatsAppMessage(remoteJid, "🧠 Analisando possíveis combinações...");

            try {
                const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);
                const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });

                const prompt = `Atue como um Nutricionista Clínico Premium de Elite. Aja com tom direto, profissional e direto ao ponto. Sem "Oi" nem "Que bom".
O paciente comeu:
${lastAnalysisText}

RETORNE estritamente 3 bullet points recomendando o que o paciente pode adicionar a esta refeição para otimizar os MACROS (com foco em saciedade ou perfil proteico). Destaque em negrito os alimentos que está sugerindo. Seja extremamente conciso. Ex:
• Adicione 1 dose de *Whey Protein* (faltou proteína)
• Inclua 1 porção de *Pasta de Amendoim* (faltou gordura boa)
`;

                const result = await model.generateContent({
                    contents: [{ role: "user", parts: [{ text: prompt }] }],
                    generationConfig: { temperature: 0.2 }
                });

                const recommendation = result.response.text();
                await sendWhatsAppMessage(remoteJid, `💡 *COMPLEMENTO RECOMENDADO:*\n\n${recommendation}`);
            } catch (err) {
                console.error("Error generating recommendation", err);
                await sendWhatsAppMessage(remoteJid, "Tive um problema de comunicação ao buscar dicas.");
            }
            return;
        }

        // TRIGGER: texto contendo palavras-chave coach
        if (
            state === "IDLE" &&
            (interactiveId === "action_coach" || (textMessage && /coach|treino|avalia[çc][aã]o/i.test(textMessage)))
        ) {
            // Offload long-running task to background
            setTimeout(async () => {
                // [STRICT VALIDATION] Check for active PAID plan
                const { data: entitlement } = await supabase
                    .from("user_entitlements")
                    .select("is_active, valid_until, entitlement_code")
                    .eq("user_id", userId)
                    .match({ is_active: true }) // Ensure active
                    .maybeSingle();

                const isPaid = entitlement &&
                    ['pro', 'mensal', 'trimestral', 'anual', 'trial', 'paid'].includes(entitlement.entitlement_code) &&
                    (!entitlement.valid_until || new Date(entitlement.valid_until) > new Date());

                if (!isPaid) {
                    await sendWhatsAppInteractiveMessage(
                        remoteJid,
                        "🔒 *Funcionalidade Exclusiva PRO*\n\nO *Personal Coach IA* está disponível apenas para assinantes Premium.\n\nCom o plano PRO você tem:\n✅ IA Analisadora de Físico (Fotos)\n✅ Treinos hiper-personalizados\n✅ Estratégia de Dieta com Macros",
                        "⭐ Desbloquear Coach",
                        "https://foodsnap.com.br"
                    );
                    return;
                }

                // [LOGIC START] Verificar última avaliação (Limite de 7 dias)
                const { data: lastAnalysis } = await supabase
                    .from("coach_analyses")
                    .select("created_at")
                    .eq("user_id", userId)
                    .order("created_at", { ascending: false })
                    .limit(1)
                    .maybeSingle();

                if (lastAnalysis && lastAnalysis.created_at) {
                    const lastDate = new Date(lastAnalysis.created_at);
                    const now = new Date();
                    const diffTime = Math.abs(now.getTime() - lastDate.getTime());
                    const sevenDaysInMs = 7 * 24 * 60 * 60 * 1000;

                    if (diffTime < sevenDaysInMs) {
                        const daysRemaining = Math.ceil((sevenDaysInMs - diffTime) / (1000 * 60 * 60 * 24));

                        await sendWhatsAppMessage(
                            remoteJid,
                            `⏳ *Calma, atleta!* O corpo precisa de tempo para evoluir.\n\nSua última avaliação foi há menos de uma semana.\nVocê poderá fazer uma nova avaliação em *${daysRemaining} dia(s)*.\n\nFoque no plano atual! 💪`
                        );
                        return;
                    }
                }
                // [LOGIC END]

                await supabase
                    .from("whatsapp_conversations")
                    .update({ state: "COACH_FRONT", temp_data: {} })
                    .eq("phone_number", senderNumber);

                await sendWhatsAppMessage(
                    remoteJid,
                    "🏋️‍♂️ *INICIANDO AVALIAÇÃO COM IA* 🏋️‍♀️\n\nVamos montar seu plano.\nPara eu calcular seu biotipo e recomendar o treino perfeito, por favor envie uma *FOTO DE FRENTE*.\n\n_(Importante: camiseta colada ou sem camisa e bermuda. Procure boa iluminação)_"
                );
            }, 0);
            return new Response("Coach Started", { status: 200 });
        }

        // COACH_FRONT
        if (state === "COACH_FRONT") {
            if (!isImage) {
                setTimeout(async () => {
                    await sendWhatsAppMessage(remoteJid, "⚠️ Por favor, envie a foto de *FRENTE* para continuarmos.");
                }, 0);
                return new Response("Waiting for image", { status: 200 });
            }

            // Offload long-running task to background
            setTimeout(async () => {
                const base64 = await getWhatsAppMedia(messageId);
                if (!base64) {
                    await sendWhatsAppMessage(remoteJid, "⚠️ Não consegui baixar a imagem. Tente enviar novamente.");
                    return;
                }

                const fileName = `${userId}_front_${Date.now()}.jpg`;
                await supabase.storage
                    .from("coach-uploads")
                    .upload(fileName, base64ToUint8Array(base64), { contentType: "image/jpeg" });

                await supabase
                    .from("whatsapp_conversations")
                    .update({ state: "COACH_SIDE", temp_data: { ...conv!.temp_data, front_image: fileName } })
                    .eq("phone_number", senderNumber);

                await sendWhatsAppMessage(remoteJid, "✅ Foto de frente recebida!\nAgora, envie uma foto de *LADO* (Perfil).");
            }, 0);
            return new Response("Coach Front image received", { status: 200 });
        }

        // COACH_SIDE
        if (state === "COACH_SIDE") {
            if (!isImage) {
                setTimeout(async () => {
                    await sendWhatsAppMessage(remoteJid, "⚠️ Por favor, envie a foto de *LADO*.");
                }, 0);
                return new Response("Waiting for image", { status: 200 });
            }

            // Offload long-running task to background
            setTimeout(async () => {
                const base64 = await getWhatsAppMedia(messageId);
                if (!base64) {
                    await sendWhatsAppMessage(remoteJid, "⚠️ Não consegui baixar a imagem. Tente enviar novamente.");
                    return;
                }

                const fileName = `${userId}_side_${Date.now()}.jpg`;
                await supabase.storage
                    .from("coach-uploads")
                    .upload(fileName, base64ToUint8Array(base64), { contentType: "image/jpeg" });

                await supabase
                    .from("whatsapp_conversations")
                    .update({ state: "COACH_BACK", temp_data: { ...conv!.temp_data, side_image: fileName } })
                    .eq("phone_number", senderNumber);

                await sendWhatsAppMessage(remoteJid, "✅ Perfil recebido!\nPor último, envie uma foto de *COSTAS*.");
            }, 0);
            return new Response("Coach Side image received", { status: 200 });
        }

        // COACH_BACK
        if (state === "COACH_BACK") {
            if (!isImage) {
                setTimeout(async () => {
                    await sendWhatsAppMessage(remoteJid, "⚠️ Por favor, envie a foto de *COSTAS*.");
                }, 0);
                return new Response("Waiting for image", { status: 200 });
            }

            // Offload long-running task to background
            setTimeout(async () => {
                const base64 = await getWhatsAppMedia(messageId);
                if (!base64) {
                    await sendWhatsAppMessage(remoteJid, "⚠️ Não consegui baixar a imagem. Tente enviar novamente.");
                    return;
                }

                const fileName = `${userId}_back_${Date.now()}.jpg`;
                await supabase.storage
                    .from("coach-uploads")
                    .upload(fileName, base64ToUint8Array(base64), { contentType: "image/jpeg" });

                await supabase
                    .from("whatsapp_conversations")
                    .update({ state: "COACH_GOAL", temp_data: { ...conv!.temp_data, back_image: fileName } })
                    .eq("phone_number", senderNumber);

                await sendWhatsAppInteractiveButtons(
                    remoteJid,
                    "📸 Todas as fotos recebidas!\n\nAgora escolha o seu principal objetivo para o protocolo:",
                    [
                        { id: "goal_hipertrofia", title: "💪 Hipertrofia" },
                        { id: "goal_emagrecer", title: "🔥 Emagrecimento" },
                        { id: "goal_definicao", title: "📐 Definição" }
                    ]
                );
            }, 0);
            return new Response("Coach Back image received", { status: 200 });
        }

        // COACH_GOAL
        if (state === "COACH_GOAL") {
            // Offload long-running task to background
            setTimeout(async () => {
                let goal = "Hipertrofia";
                if (interactiveId === "goal_emagrecer" || textMessage.includes("2") || /emagreci|secar/i.test(textMessage)) goal = "Emagrecimento";
                else if (interactiveId === "goal_definicao" || textMessage.includes("3") || /defini/i.test(textMessage)) goal = "Definição";
                else if (interactiveId === "goal_hipertrofia" || textMessage.includes("1") || /hiper/i.test(textMessage)) goal = "Hipertrofia";
                else {
                    await sendWhatsAppInteractiveButtons(
                        remoteJid,
                        "⚠️ Objetivo não reconhecido. Por favor, escolha uma das opções abaixo:",
                        [
                            { id: "goal_hipertrofia", title: "💪 Hipertrofia" },
                            { id: "goal_emagrecer", title: "🔥 Emagrecimento" },
                            { id: "goal_definicao", title: "📐 Definição" }
                        ]
                    );
                    return;
                }

                await sendWhatsAppMessage(
                    remoteJid,
                    "🤖 Estou analisando seu físico e montando o plano com a IA...\nIsso pode levar cerca de 10-15 segundos."
                );

                try {
                    const { front_image, side_image, back_image } = conv!.temp_data;
                    const images = [front_image, side_image, back_image];
                    const parts: any[] = [{ text: COACH_SYSTEM_PROMPT }, { text: `Objetivo: ${goal}` }];

                    for (const imgPath of images) {
                        if (imgPath) {
                            const { data: blob } = await supabase.storage.from("coach-uploads").download(imgPath);
                            if (blob) {
                                const buffer = await blob.arrayBuffer();
                                const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));
                                parts.push({ inlineData: { mimeType: "image/jpeg", data: base64 } });
                            }
                        }
                    }

                    const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);
                    const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });

                    const result = await model.generateContent({
                        contents: [{ role: "user", parts }],
                        generationConfig: { temperature: 0.2, responseMimeType: "application/json" },
                    });

                    const responseText = result.response.text();
                    const plan = JSON.parse(responseText);

                    // PREMIUM LAYOUT FOR COACH
                    const lines: string[] = [];
                    lines.push("📱 *SEU PROTOCOLO TITAN*");
                    lines.push("");
                    lines.push(`🧬 *BIÓTIPO*: ${plan.analysis?.somatotype} (${plan.analysis?.muscle_mass_level} massa muscular)`);
                    lines.push(`🎯 *FOCO*: ${plan.workout?.focus}`);
                    lines.push("");
                    lines.push("🏋️ *TREINO*");
                    lines.push(`▪ Divisão ${plan.workout?.split} (${plan.workout?.frequency_days}x/semana)`);
                    lines.push("");
                    lines.push("🥗 *DIETA*");
                    lines.push(`▪ ${Math.round(plan.diet?.total_calories)} kcal`);
                    lines.push(`▪ Proteína: ${plan.diet?.macros?.protein_g}g`);
                    lines.push(`▪ Carboidrato: ${plan.diet?.macros?.carbs_g}g`);
                    lines.push(`▪ Gordura: ${plan.diet?.macros?.fats_g}g`);
                    lines.push("");
                    lines.push("💊 *SUPLEMENTOS*");

                    const sups = plan.diet?.supplements?.map((s: any) => s.name).join(", ");
                    if (sups && sups.length > 0) {
                        lines.push(`▪ ${sups}`);
                    } else {
                        lines.push(`▪ Não há recomendações`);
                    }

                    lines.push("");
                    lines.push(`💡 _DICA:_ ${plan.motivation_quote}`);
                    lines.push("");
                    lines.push(`🚀 *Plano Completo Interativo:*`);
                    lines.push(`Acesse https://foodsnap.com.br/meu-plano`);

                    await sendWhatsAppMessage(remoteJid, lines.join('\n'));

                    // ── Gerar PDF e enviar via WhatsApp ─────────────────
                    try {
                        const pdfFileName = `FoodSnap_Titan_${new Date().toISOString().split("T")[0]}`;
                        const pdfHtml = buildCoachPdfHtml(plan);

                        console.log("[WH] Generating PDF via n8n/Gotenberg...");
                        const pdfResponse = await fetch("https://n8n.seureview.com.br/webhook/pdf-coach", {
                            method: "POST",
                            headers: { "Content-Type": "application/json" },
                            body: JSON.stringify({ html: pdfHtml, file_name: pdfFileName }),
                        });

                        if (pdfResponse.ok) {
                            const pdfBlob = await pdfResponse.arrayBuffer();
                            const pdfBytes = new Uint8Array(pdfBlob);
                            const storagePath = `${userId}/${pdfFileName}.pdf`;

                            // Upload para Supabase Storage
                            const { error: uploadErr } = await supabase.storage
                                .from("coach-pdfs")
                                .upload(storagePath, pdfBytes, {
                                    contentType: "application/pdf",
                                    upsert: true,
                                });

                            if (uploadErr) {
                                console.error("[WH] PDF upload error:", uploadErr);
                            } else {
                                // URL Assinada (funciona mesmo com bucket privado)
                                const { data: urlData, error: signErr } = await supabase.storage
                                    .from("coach-pdfs")
                                    .createSignedUrl(storagePath, 60 * 60); // 1 hora de validade

                                if (signErr || !urlData?.signedUrl) {
                                    console.error("[WH] Signed URL error:", signErr);
                                } else {
                                    await sendWhatsAppDocument(
                                        remoteJid,
                                        urlData.signedUrl,
                                        `${pdfFileName}.pdf`,
                                        "📄 Seu Protocolo Titan completo em PDF!"
                                    );
                                }
                            }
                        } else {
                            console.error("[WH] n8n PDF error:", pdfResponse.status, await pdfResponse.text());
                        }
                    } catch (pdfErr) {
                        console.error("[WH] PDF generation/send error (non-blocking):", pdfErr);
                        // PDF is non-blocking — user already got the text summary
                    }

                    // ── Salvar análise coach (enriquecido p/ dashboard) ─
                    const { error: saveCoachErr } = await supabase.from("coach_analyses").insert({
                        user_id: userId,
                        source: "whatsapp",
                        ai_raw_response: responseText,
                        ai_structured: plan,
                        goal_suggestion: goal,
                        biotype: plan.analysis?.somatotype || null,
                        estimated_body_fat: parseFloat(String(plan.analysis?.body_fat_percentage || 0)) || 0,
                        muscle_mass_level: plan.analysis?.muscle_mass_level || null,
                    });

                    if (saveCoachErr) {
                        console.error("[WH] Error saving coach analysis to DB:", saveCoachErr);
                    } else {
                        console.log("[WH] Coach analysis saved successfully for user:", userId);
                    }

                    // Reset state
                    await supabase
                        .from("whatsapp_conversations")
                        .update({ state: "IDLE", temp_data: {} })
                        .eq("phone_number", senderNumber);
                } catch (err) {
                    console.error("Coach Gen Error:", err);
                    await sendWhatsAppMessage(
                        remoteJid,
                        "⚠️ Ocorreu um erro ao gerar seu plano. Tente novamente digitando 'Coach'."
                    );
                    await supabase
                        .from("whatsapp_conversations")
                        .update({ state: "IDLE", temp_data: {} })
                        .eq("phone_number", senderNumber);
                }
            }, 0);
            return new Response("Coach Workflow Completed", { status: 200 });
        }

        // ── 6. Food Scan Flow (IDLE) ────────────────────────────────
        if (state === "IDLE") {
            console.log(`[WH] Entering Food Scan flow. isImage=${isImage}`);

            setTimeout(async () => {
                // 6a. Verificar plano e quota
                const { data: entitlement } = await supabase
                    .from("user_entitlements")
                    .select("is_active, valid_until, entitlement_code")
                    .eq("user_id", userId)
                    .match({ is_active: true })
                    .maybeSingle();

                const isPaid = entitlement &&
                    ['pro', 'mensal', 'trimestral', 'anual', 'trial', 'paid'].includes(entitlement.entitlement_code) &&
                    (!entitlement.valid_until || new Date(entitlement.valid_until) > new Date());

                if (!isPaid) {
                    const { count: freeUsed } = await supabase
                        .from("food_analyses")
                        .select("*", { count: "exact", head: true })
                        .eq("user_id", userId)
                        .eq("used_free_quota", true);

                    if ((freeUsed || 0) >= FREE_FOOD_LIMIT) {
                        await sendWhatsAppInteractiveMessage(
                            remoteJid,
                            `🚫 *Limite gratuito atingido*\nVocê já usou suas ${FREE_FOOD_LIMIT} análises grátis.\n\nAssine o plano PRO para escaneamento de alimentos e uso ilimitado do bot inteligente.`,
                            "🚀 Assinar Plano PRO",
                            "https://foodsnap.com.br"
                        );
                        return;
                    }
                }

                // 6b. Sem imagem → mensagem de boas-vindas
                if (!isImage) {
                    // Verifica se é texto aleatório ignorável
                    if (!interactiveId && /^\s*$/.test(textMessage)) {
                        return;
                    }
                    await sendWhatsAppMessage(
                        remoteJid,
                        "👋 Olá! Envie uma *foto do seu prato* (bem nítida e de cima 📸) que eu te retorno *calorias e macronutrientes* em segundos.\n\nOu digite *Coach* para iniciar uma consultoria completa."
                    );
                    return;
                }

                // 6c. Processar imagem
                await sendWhatsAppMessage(remoteJid, "📸 Recebi sua foto! Estou analisando o prato agora… ⏳");

                const base64Image = await getWhatsAppMedia(messageId);
                if (!base64Image) {
                    await sendWhatsAppMessage(remoteJid, "⚠️ Não consegui baixar a imagem. Tente enviar novamente.");
                    return;
                }

                // 6d. Chamar Gemini
                const genAI = new GoogleGenerativeAI(GEMINI_API_KEY);
                const model = genAI.getGenerativeModel({ model: "gemini-2.5-flash" });

                const geminiResult = await model.generateContent({
                    contents: [
                        {
                            role: "user",
                            parts: [
                                { text: SYSTEM_PROMPT },
                                { inlineData: { mimeType: "image/jpeg", data: base64Image } },
                            ],
                        },
                    ],
                    generationConfig: { temperature: 0.1, responseMimeType: "application/json" },
                });

                const rawResponseText = geminiResult.response.text();
                // HOTFIX: Add telemetry 1
                console.log("[TELEMETRY] Gemini Raw Response:", rawResponseText.substring(0, 100) + "...");

                // ── Gamificação: Cálculo de Ofensiva (Streak) ──
                let newStreak = user?.current_streak || 0;
                let newLongest = user?.longest_streak || 0;
                let streakIncreased = false;

                try {
                    if (user) {
                        const now = new Date();
                        // BRT timezone (UTC-3)
                        const brtz = new Date(now.getTime() - (3 * 60 * 60 * 1000));
                        const brTodayStr = brtz.toISOString().split("T")[0];

                        if (user.last_scan_date) {
                            const yesterday = new Date(brtz);
                            yesterday.setDate(yesterday.getDate() - 1);
                            const yesterdayStr = yesterday.toISOString().split("T")[0];

                            if (user.last_scan_date === brTodayStr) {
                                // Já escaneou hoje, mantém a ofensiva
                            } else if (user.last_scan_date === yesterdayStr) {
                                // Escaneou ontem, incrementa!
                                newStreak += 1;
                                streakIncreased = true;
                            } else {
                                // Quebrou a ofensiva. Reinicia no 1
                                newStreak = 1;
                                streakIncreased = true;
                            }
                        } else {
                            // Primeiro scan!
                            newStreak = 1;
                            streakIncreased = true;
                        }

                        if (newStreak > newLongest) {
                            newLongest = newStreak;
                        }

                        // Salvar ofensive no DB se mudou a data (mesmo que tenha quebrado a ofensiva e reiniciado) ou submeteu streak
                        if (streakIncreased || user.last_scan_date !== brTodayStr) {
                            await supabase.from("profiles").update({
                                current_streak: newStreak,
                                longest_streak: newLongest,
                                last_scan_date: brTodayStr
                            }).eq("id", userId);
                        }
                    }
                } catch (streakErr) {
                    console.error("[WH] Error updating streak:", streakErr);
                }

                // 6e. Limpar e normalizar resultado
                let analysis: any;
                try {
                    analysis = parseAndCleanGeminiResponse(rawResponseText);
                    // HOTFIX: Add telemetry 2
                    console.log("[TELEMETRY] Parsed Analysis OK. Health Score:", analysis.health_score);
                } catch (parseErr) {
                    console.error("[TELEMETRY] Parse error:", parseErr);
                    await sendWhatsAppMessage(
                        remoteJid,
                        "⚠️ Houve um erro ao interpretar a análise. Tente enviar a foto novamente com boa iluminação."
                    );
                    return;
                }

                // 6f. Formatar e tentar enviar a imagem renderizada
                let replyText = "";
                try {
                    replyText = formatWhatsAppResponse(analysis);
                    console.log("[TELEMETRY] Format WhatsApp Response OK.");
                } catch (formatErr) {
                    console.error("[TELEMETRY] Format text error:", formatErr);
                    replyText = "Opa, ocorreu um problema com a validação gramatical do resuminho! ⚠️";
                }

                let cardImageUrl = null;
                if (IMAGE_RENDERER_URL) {
                    try {
                        console.log("[WH] Generating Image Card via Puppeteer...");
                        const renderRes = await fetch(`${IMAGE_RENDERER_URL}/api/render`, {
                            method: "POST",
                            headers: { "Content-Type": "application/json" },
                            body: JSON.stringify({ data: analysis })
                        });

                        if (renderRes.ok) {
                            const imgBlob = await renderRes.arrayBuffer();
                            const cardPath = `${userId}/card_${Date.now()}.png`;
                            const { error: uploadErr } = await supabase.storage
                                .from("consultas")
                                .upload(cardPath, new Uint8Array(imgBlob), { contentType: "image/png" });

                            if (!uploadErr) {
                                const { data: { publicUrl } } = supabase.storage
                                    .from("consultas")
                                    .getPublicUrl(cardPath);
                                cardImageUrl = publicUrl;
                            }
                        }
                    } catch (e) {
                        console.error("[WH] Failed to generate card image:", e);
                    }
                }

                // Enviar Carta Gráfica (se houver) + Texto, senão envia só Texto puro.
                console.log("[TELEMETRY] Sending Final Output. Has Card Image?", !!cardImageUrl);
                try {
                    if (cardImageUrl) {
                        await sendWhatsAppImage(remoteJid, cardImageUrl); // Apenas a foto do card
                    } else {
                        await sendWhatsAppMessage(remoteJid, replyText); // Envia o dump de texto se falhar
                    }
                } catch (sendErr) {
                    console.error("[TELEMETRY] Error sending final whatsapp output:", sendErr);
                }

                // Enviar Notificação de Ofensiva (Gamificação)
                if (streakIncreased && newStreak >= 1) {
                    let streakMsg = `🔥 *Ofensiva de ${newStreak} dia${newStreak > 1 ? 's' : ''}!* Continue assim para manter seu hábito saudável.`;
                    if (newStreak > (user?.longest_streak || 0)) {
                        streakMsg = `🏆 *Novo Recorde!* Ofensiva de ${newStreak} dias seguidos. Você está imbatível!`;
                    }
                    await sendWhatsAppMessage(remoteJid, streakMsg);
                }

                // 6f-2. Enviar botônicos (Follow-up de recomendações)
                await sendWhatsAppInteractiveButtons(
                    remoteJid,
                    "O que você gostaria de fazer com este prato?",
                    [
                        { id: "action_coach", title: "🤖 Iniciar Treino" },
                        { id: "action_recommend", title: "🥣 O que comer mais?" },
                    ]
                );

                // Atualizar last_analysis no temp_data para a re-pergunta "O que comer mais?"
                await supabase
                    .from("whatsapp_conversations")
                    .update({ state: "IDLE", temp_data: { last_analysis: rawResponseText } })
                    .eq("phone_number", senderNumber);

                // 6g. Mapear confidence para enum do banco
                const confidenceMap: Record<string, string> = {
                    alta: "high",
                    media: "medium",
                    média: "medium",
                    baixa: "low",
                };

                // 6h. Salvar no banco
                const { data: inserted } = await supabase
                    .from("food_analyses")
                    .insert({
                        user_id: userId,
                        source: "whatsapp",
                        image_url: null, // será atualizado após upload
                        ai_raw_response: rawResponseText,
                        ai_structured: analysis,
                        total_calories: analysis.total?.calories || 0,
                        total_protein: analysis.total?.protein || 0,
                        total_carbs: analysis.total?.carbs || 0,
                        total_fat: analysis.total?.fat || 0,
                        total_fiber: analysis.total?.fiber || 0,
                        total_sodium_mg: analysis.total?.sodium_mg || 0,
                        nutrition_score: analysis.health_score || 0,
                        confidence_level: confidenceMap[analysis.confidence] || "medium",
                        used_free_quota: !isPaid,
                    })
                    .select("id")
                    .single();

                // 6i. Upload imagem para Supabase Storage (bucket consultas)
                if (inserted?.id) {
                    try {
                        const imgPath = `${userId}/${inserted.id}.jpg`;
                        const imgBytes = base64ToUint8Array(base64Image);
                        await supabase.storage
                            .from("consultas")
                            .upload(imgPath, imgBytes, { contentType: "image/jpeg", upsert: true });

                        // Atualizar image_url no registro
                        const { data: { publicUrl } } = supabase.storage
                            .from("consultas")
                            .getPublicUrl(imgPath);

                        await supabase
                            .from("food_analyses")
                            .update({ image_url: publicUrl })
                            .eq("id", inserted.id);
                    } catch (uploadErr) {
                        console.error("Image upload error (non-fatal):", uploadErr);
                    }
                }

            }, 0);
            return new Response("Food Scan Started", { status: 200 });
        }

    } catch (criticalErr) { // Capture da função async flutuante
        console.error("[WH] Critical Background Error:", criticalErr);
    }
}
