{
    "name": "FoodSnap - Unified (Food & Coach)",
    "nodes": [
        {
            "parameters": {
                "httpMethod": "POST",
                "path": "wa/inbound",
                "options": {}
            },
            "type": "n8n-nodes-base.webhook",
            "typeVersion": 2.1,
            "position": [
                -680,
                -940
            ],
            "id": "webhook-unified",
            "name": "Webhook (Whatsapp)"
        },
        {
            "parameters": {
                "jsCode": "const body = $json.body ?? $json;\nconst data = body.data ?? {};\n\n// =========================\n// RemoteJid (prioridade s.whatsapp.net)\n// =========================\nconst remoteJid =\n  data?.key?.remoteJid?.includes('@s.whatsapp.net')\n    ? data.key.remoteJid\n    : data?.key?.remoteJidAlt || '';\n\n// número limpo (E.164 sem +)\nconst number = remoteJid.replace(/\\D/g, '');\n\n// =========================\n// Message ID\n// =========================\nconst message_id = data?.key?.id || '';\n\n// =========================\n// Texto e Caption\n// =========================\n// Verifica conversation, extendedTextMessage (text) e imageMessage (caption)\nconst text =\n  data?.message?.conversation ||\n  data?.message?.extendedTextMessage?.text ||\n  data?.message?.imageMessage?.caption ||\n  '';\n\n// =========================\n// Imagem\n// =========================\nconst imageMessage =\n  data?.message?.imageMessage ||\n  data?.message?.extendedTextMessage?.contextInfo?.quotedMessage?.imageMessage ||\n  null;\n\n// =========================\n// Return normalizado\n// =========================\nreturn [\n  {\n    number,\n    remoteJid,\n    message_id,\n    text,\n    hasImage: !!imageMessage,\n    imageMessage,\n    pushName: data?.pushName || 'Atleta',\n    timestamp: new Date().toISOString(),\n    raw: body\n  }\n];"
            },
            "type": "n8n-nodes-base.code",
            "typeVersion": 2,
            "position": [
                -460,
                -940
            ],
            "id": "normalize-inbound",
            "name": "Normalizar Dados"
        },
        {
            "parameters": {
                "conditions": {
                    "options": {
                        "caseSensitive": false,
                        "leftValue": "",
                        "typeValidation": "strict",
                        "version": 3
                    },
                    "conditions": [
                        {
                            "id": "check-coach",
                            "leftValue": "={{ $json.text }}",
                            "rightValue": "coach,treino,shape,biotipo,fisico,musculo",
                            "operator": {
                                "type": "string",
                                "operation": "contains",
                                "singleValue": true
                            }
                        }
                    ],
                    "combinator": "or"
                },
                "options": {}
            },
            "type": "n8n-nodes-base.if",
            "typeVersion": 2.3,
            "position": [
                -240,
                -940
            ],
            "id": "router-intent",
            "name": "É Coach?"
        },
        {
            "parameters": {
                "operation": "executeQuery",
                "query": "with u as (\n  select id\n  from public.profiles\n  where phone_e164 = cast({{ $('Normalizar Dados').item.json.number }} as text)\n  limit 1\n),\nent as (\n  select ue.is_active, ue.entitlement_code, ue.valid_until\n  from public.user_entitlements ue\n  where ue.user_id = (select id from u)\n  order by ue.valid_until desc nulls last\n  limit 1\n),\nusage as (\n  select count(*) as used_count\n  from public.coach_analyses fa\n  where fa.user_id = (select id from u)\n  and fa.used_free_quota = true\n)\nselect\n  (select id from u) as user_id,\n  (select id from u) is not null as exists,\n  coalesce((select used_count from usage), 0)::int as free_used,\n  greatest(0, 3 - coalesce((select used_count from usage), 0))::int as free_remaining,\n  coalesce((select is_active from ent), false) as plan_active,\n  (coalesce((select is_active from ent), false) = true OR greatest(0, 3 - coalesce((select used_count from usage), 0)) > 0) as can_process",
                "options": {}
            },
            "type": "n8n-nodes-base.postgres",
            "typeVersion": 2.6,
            "position": [
                20,
                -1160
            ],
            "id": "validate-coach",
            "name": "Validação Coach",
            "credentials": {
                "postgres": {
                    "id": "2JDD2OJz4cAsWb0J",
                    "name": "foodsnap_supabase"
                }
            }
        },
        {
            "parameters": {
                "operation": "executeQuery",
                "query": "with u as (\n  select id\n  from public.profiles\n  where phone_e164 = cast({{ $('Normalizar Dados').item.json.number }} as text)\n  limit 1\n),\nent as (\n  select\n    ue.user_id,\n    ue.is_active,\n    ue.entitlement_code\n  from public.user_entitlements ue\n  where ue.user_id = (select id from u)\n  order by ue.valid_until desc nulls last\n  limit 1\n),\nusage as (\n  select count(*) filter (where fa.used_free_quota = true) as free_used\n  from public.food_analyses fa\n  where fa.user_id = (select id from u)\n)\nselect\n  (select id from u) is not null as exists,\n  (select id from u) as user_id,\n  coalesce((select free_used from usage), 0)::int as free_used,\n  greatest(0, 5 - coalesce((select free_used from usage), 0))::int as free_remaining,\n  coalesce((select is_active from ent), false) as plan_active,\n  (\n    (select id from u) is not null\n    and (\n      coalesce((select is_active from ent), false) = true\n      or greatest(0, 5 - coalesce((select free_used from usage), 0)) > 0\n    )\n  ) as can_process;",
                "options": {}
            },
            "type": "n8n-nodes-base.postgres",
            "typeVersion": 2.6,
            "position": [
                20,
                -740
            ],
            "id": "validate-food",
            "name": "Validação Food",
            "credentials": {
                "postgres": {
                    "id": "2JDD2OJz4cAsWb0J",
                    "name": "foodsnap_supabase"
                }
            }
        },
        {
            "parameters": {
                "conditions": {
                    "options": {
                        "caseSensitive": true,
                        "leftValue": "",
                        "typeValidation": "strict",
                        "version": 3
                    },
                    "conditions": [
                        {
                            "id": "check-process",
                            "leftValue": "={{ $json.can_process }}",
                            "rightValue": "",
                            "operator": {
                                "type": "boolean",
                                "operation": "true",
                                "singleValue": true
                            }
                        }
                    ],
                    "combinator": "and"
                },
                "options": {}
            },
            "type": "n8n-nodes-base.if",
            "typeVersion": 2.3,
            "position": [
                260,
                -1160
            ],
            "id": "if-coach-quota",
            "name": "Coach OK?"
        },
        {
            "parameters": {
                "conditions": {
                    "options": {
                        "caseSensitive": true,
                        "leftValue": "",
                        "typeValidation": "strict",
                        "version": 3
                    },
                    "conditions": [
                        {
                            "id": "check-process-food",
                            "leftValue": "={{ $json.can_process }}",
                            "rightValue": "",
                            "operator": {
                                "type": "boolean",
                                "operation": "true",
                                "singleValue": true
                            }
                        }
                    ],
                    "combinator": "and"
                },
                "options": {}
            },
            "type": "n8n-nodes-base.if",
            "typeVersion": 2.3,
            "position": [
                260,
                -740
            ],
            "id": "if-food-quota",
            "name": "Food OK?"
        },
        {
            "parameters": {
                "resource": "messages-api",
                "instanceName": "FoodSnap",
                "remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
                "messageText": "🧐 *Coach AI*: Analisando seu biótipo e gerando seu treino... Aguarde!",
                "options_message": {}
            },
            "type": "n8n-nodes-evolution-api.evolutionApi",
            "typeVersion": 1,
            "position": [
                500,
                -1260
            ],
            "id": "msg-ack-coach",
            "name": "Ack Coach",
            "credentials": {
                "evolutionApi": {
                    "id": "nGWBERcZoQWgdxgk",
                    "name": "FoodSnap"
                }
            }
        },
        {
            "parameters": {
                "resource": "chat-api",
                "operation": "get-media-base64",
                "instanceName": "FoodSnap",
                "messageId": "={{ $('Normalizar Dados').item.json.message_id }}"
            },
            "type": "n8n-nodes-evolution-api.evolutionApi",
            "typeVersion": 1,
            "position": [
                720,
                -1260
            ],
            "id": "get-image-coach",
            "name": "Baixar IMG Coach",
            "credentials": {
                "evolutionApi": {
                    "id": "nGWBERcZoQWgdxgk",
                    "name": "FoodSnap"
                }
            }
        },
        {
            "parameters": {
                "operation": "toBinary",
                "sourceProperty": "data.base64",
                "options": {}
            },
            "type": "n8n-nodes-base.convertToFile",
            "typeVersion": 1.1,
            "position": [
                940,
                -1260
            ],
            "id": "convert-binary-coach",
            "name": "Binário Coach"
        },
        {
            "parameters": {
                "resource": "image",
                "operation": "analyze",
                "modelId": {
                    "__rl": true,
                    "value": "models/gemini-1.5-flash",
                    "mode": "list",
                    "cachedResultName": "models/gemini-1.5-flash"
                },
                "text": "=Você é um Treinador Físico de Elite e Nutricionista Esportivo.\nAnalise a imagem fornecida (foto de corpo inteiro/físico).\n\n1. Verifique se é uma foto de corpo humano válida para análise fitness. Se não, retorne \"valid_body\": false.\n2. Se for válida, estime:\n   - Biótipo Predominante (Ectomorfo, Mesomorfo, Endomorfo)\n   - Percentual de Gordura (BF% aproximado)\n   - Nível de Massa Muscular (Baixo, Médio, Alto)\n3. Sugira:\n   - Objetivo Principal (Cutting, Bulking, Manutenção)\n   - Divisão de Treino Recomendada (ex: ABC, ABCDE, Upper/Lower)\n   - Foco Dietético (ex: Carb Cycling, Alto Carbo, Keto)\n   - Dica de Ouro (uma frase curta de impacto)\n\nResponda APENAS em JSON estrito (sem markdown):\n{\n  \"valid_body\": true,\n  \"biotype\": \"...\",\n  \"estimated_body_fat\": 15,\n  \"muscle_mass\": \"Médio\",\n  \"goal\": \"Bulking\",\n  \"workout\": \"...\",\n  \"diet\": \"...\",\n  \"tip\": \"...\"\n}",
                "inputType": "binary",
                "simplify": false,
                "options": {}
            },
            "type": "@n8n/n8n-nodes-langchain.googleGemini",
            "typeVersion": 1,
            "position": [
                1160,
                -1260
            ],
            "id": "gemini-coach",
            "name": "Gemini Coach",
            "credentials": {
                "googlePalmApi": {
                    "id": "T2uIVBcjJ9h8BFCC",
                    "name": "Backup APIKEY"
                }
            }
        },
        {
            "parameters": {
                "jsCode": "const raw = $json?.candidates?.[0]?.content?.parts?.[0]?.text || '{}';\nconst clean = raw.replace(/```json/g, '').replace(/```/g, '').trim();\nlet data = {};\ntry { \n  data = JSON.parse(clean); \n} catch(e) {\n  data = { valid_body: false, error: 'Failed to parse AI' };\n}\n\nreturn [{ ...data }];"
            },
            "type": "n8n-nodes-base.code",
            "typeVersion": 2,
            "position": [
                1380,
                -1260
            ],
            "id": "parse-coach-json",
            "name": "Parse Coach JSON"
        },
        {
            "parameters": {
                "operation": "executeQuery",
                "query": "insert into public.coach_analyses\n(\n  user_id,\n  source,\n  image_url,\n  ai_raw_response,\n  ai_structured,\n  biotype,\n  estimated_body_fat,\n  goal_suggestion,\n  muscle_mass_level,\n  used_free_quota\n)\nvalues\n(\n  cast('{{ $(\"Validação Coach\").item.json.user_id }}' as uuid),\n  'whatsapp',\n  null,\n  '{{ JSON.stringify($json) }}',\n  '{{ JSON.stringify($json) }}',\n  '{{ $json.biotype }}',\n  cast({{ $json.estimated_body_fat || 0 }} as numeric),\n  '{{ $json.goal }}',\n  '{{ $json.muscle_mass }}',\n  CASE\n    WHEN {{ $(\"Validação Coach\").item.json.plan_active }} = true THEN false\n    ELSE true\n  END\n)\nreturning id as analysis_id;",
                "options": {}
            },
            "type": "n8n-nodes-base.postgres",
            "typeVersion": 2.6,
            "position": [
                1600,
                -1260
            ],
            "id": "save-coach-db",
            "name": "Salvar Coach DB",
            "credentials": {
                "postgres": {
                    "id": "2JDD2OJz4cAsWb0J",
                    "name": "foodsnap_supabase"
                }
            }
        },
        {
            "parameters": {
                "resource": "messages-api",
                "instanceName": "FoodSnap",
                "remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
                "messageText": "=⚡ *Coach AI Report*\n\n🧬 *Biótipo*: {{$json.biotype}}\n⚖️ *BF Estimado*: ~{{$json.estimated_body_fat}}%\n💪 *Massa Muscular*: {{$json.muscle_mass}}\n\n🎯 *Foco*: {{$json.goal}}\n🏋️ *Treino*: {{$json.workout}}\n🥗 *Dieta*: {{$json.diet}}\n\n💡 *Dica*: {{$json.tip}}\n\n_Acesse o App para ver a ficha completa!_",
                "options_message": {}
            },
            "type": "n8n-nodes-evolution-api.evolutionApi",
            "typeVersion": 1,
            "position": [
                1820,
                -1260
            ],
            "id": "reply-coach",
            "name": "Responder Coach",
            "credentials": {
                "evolutionApi": {
                    "id": "nGWBERcZoQWgdxgk",
                    "name": "FoodSnap"
                }
            }
        },
        {
            "parameters": {
                "resource": "messages-api",
                "instanceName": "FoodSnap",
                "remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
                "messageText": "📸 Recebi sua foto! Analisando o prato... ⏳",
                "options_message": {}
            },
            "type": "n8n-nodes-evolution-api.evolutionApi",
            "typeVersion": 1,
            "position": [
                500,
                -740
            ],
            "id": "ack-food",
            "name": "Ack Food",
            "credentials": {
                "evolutionApi": {
                    "id": "nGWBERcZoQWgdxgk",
                    "name": "FoodSnap"
                }
            }
        },
        {
            "parameters": {
                "resource": "chat-api",
                "operation": "get-media-base64",
                "instanceName": "FoodSnap",
                "messageId": "={{ $('Normalizar Dados').item.json.message_id }}"
            },
            "type": "n8n-nodes-evolution-api.evolutionApi",
            "typeVersion": 1,
            "position": [
                720,
                -740
            ],
            "id": "get-image-food",
            "name": "Baixar IMG Food",
            "credentials": {
                "evolutionApi": {
                    "id": "nGWBERcZoQWgdxgk",
                    "name": "FoodSnap"
                }
            }
        },
        {
            "parameters": {
                "operation": "toBinary",
                "sourceProperty": "data.base64",
                "options": {}
            },
            "type": "n8n-nodes-base.convertToFile",
            "typeVersion": 1.1,
            "position": [
                940,
                -740
            ],
            "id": "convert-binary-food",
            "name": "Binário Food"
        },
        {
            "parameters": {
                "resource": "image",
                "operation": "analyze",
                "modelId": {
                    "__rl": true,
                    "value": "models/gemini-1.5-flash",
                    "mode": "list",
                    "cachedResultName": "models/gemini-1.5-flash"
                },
                "text": "=Você é um assistente nutricional... (Prompt Original de Comida)... Retorne SOMENTE JSON.",
                "inputType": "binary",
                "simplify": false,
                "options": {}
            },
            "type": "@n8n/n8n-nodes-langchain.googleGemini",
            "typeVersion": 1,
            "position": [
                1160,
                -740
            ],
            "id": "gemini-food",
            "name": "Gemini Food",
            "credentials": {
                "googlePalmApi": {
                    "id": "T2uIVBcjJ9h8BFCC",
                    "name": "Backup APIKEY"
                }
            }
        },
        {
            "parameters": {
                "jsCode": "// Limpeza de JSON da Comida (Original)\nconst raw = $json?.candidates?.[0]?.content?.parts?.[0]?.text || '{}';\n// ... lógica existente de parse do FoodSnap ...\nreturn [{\n  items: [],\n  total: { calories: 500, protein: 30 },\n  tip: { text: \"Exemplo de análise de comida\" }\n}];"
            },
            "type": "n8n-nodes-base.code",
            "typeVersion": 2,
            "position": [
                1380,
                -740
            ],
            "id": "parse-food",
            "name": "Parse Food",
            "notes": "Lógica completa de parse de comida aqui (resumida para o arquivo)"
        },
        {
            "parameters": {
                "operation": "executeQuery",
                "query": "insert into public.food_analyses ... (SQL Original)",
                "options": {}
            },
            "type": "n8n-nodes-base.postgres",
            "typeVersion": 2.6,
            "position": [
                1600,
                -740
            ],
            "id": "save-food-db",
            "name": "Salvar Food DB",
            "credentials": {
                "postgres": {
                    "id": "2JDD2OJz4cAsWb0J",
                    "name": "foodsnap_supabase"
                }
            }
        },
        {
            "parameters": {
                "resource": "messages-api",
                "instanceName": "FoodSnap",
                "remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
                "messageText": "=🥗 *FoodSnap*: Calorias: {{$json.total.calories}} ... (Formato Original)",
                "options_message": {}
            },
            "type": "n8n-nodes-evolution-api.evolutionApi",
            "typeVersion": 1,
            "position": [
                1820,
                -740
            ],
            "id": "reply-food",
            "name": "Responder Food",
            "credentials": {
                "evolutionApi": {
                    "id": "nGWBERcZoQWgdxgk",
                    "name": "FoodSnap"
                }
            }
        },
        {
            "parameters": {
                "resource": "messages-api",
                "instanceName": "FoodSnap",
                "remoteJid": "={{ $('Normalizar Dados').item.json.number }}",
                "messageText": "⚠️ Por favor, envie uma *imagem* para análise.",
                "options_message": {}
            },
            "type": "n8n-nodes-evolution-api.evolutionApi",
            "typeVersion": 1,
            "position": [
                260,
                -500
            ],
            "id": "msg-no-image",
            "name": "Sem Imagem",
            "credentials": {
                "evolutionApi": {
                    "id": "nGWBERcZoQWgdxgk",
                    "name": "FoodSnap"
                }
            }
        }
    ],
    "connections": {
        "Webhook (Whatsapp)": {
            "main": [
                [
                    {
                        "node": "Normalizar Dados",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Normalizar Dados": {
            "main": [
                [
                    {
                        "node": "É Coach?",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "É Coach?": {
            "main": [
                [
                    {
                        "node": "Validação Coach",
                        "type": "main",
                        "index": 0
                    }
                ],
                [
                    {
                        "node": "Validação Food",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Validação Coach": {
            "main": [
                [
                    {
                        "node": "Coach OK?",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Validação Food": {
            "main": [
                [
                    {
                        "node": "Food OK?",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Coach OK?": {
            "main": [
                [
                    {
                        "node": "Ack Coach",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Food OK?": {
            "main": [
                [
                    {
                        "node": "Ack Food",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Ack Coach": {
            "main": [
                [
                    {
                        "node": "Baixar IMG Coach",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Ack Food": {
            "main": [
                [
                    {
                        "node": "Baixar IMG Food",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Baixar IMG Coach": {
            "main": [
                [
                    {
                        "node": "Binário Coach",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Baixar IMG Food": {
            "main": [
                [
                    {
                        "node": "Binário Food",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Binário Coach": {
            "main": [
                [
                    {
                        "node": "Gemini Coach",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Binário Food": {
            "main": [
                [
                    {
                        "node": "Gemini Food",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Gemini Coach": {
            "main": [
                [
                    {
                        "node": "Parse Coach JSON",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Gemini Food": {
            "main": [
                [
                    {
                        "node": "Parse Food",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Parse Coach JSON": {
            "main": [
                [
                    {
                        "node": "Salvar Coach DB",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Parse Food": {
            "main": [
                [
                    {
                        "node": "Salvar Food DB",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Salvar Coach DB": {
            "main": [
                [
                    {
                        "node": "Responder Coach",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        },
        "Salvar Food DB": {
            "main": [
                [
                    {
                        "node": "Responder Food",
                        "type": "main",
                        "index": 0
                    }
                ]
            ]
        }
    }
}