{
  "name": "Alerta inteligente con Gemini + Notificaciones",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "hours",
              "hoursInterval": 6
            }
          ]
        }
      },
      "id": "schedule",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1,
      "position": [
        200,
        300
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "http://host.docker.internal:3001/trpc/lotes.list",
        "options": {
          "response": {
            "response": {
              "fullResponse": false
            }
          }
        },
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer {{ $env.BACKEND_JWT_TOKEN }}"
            }
          ]
        }
      },
      "id": "fetch-lotes",
      "name": "Fetch Lotes",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        420,
        300
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "http://host.docker.internal:3001/trpc/suelo.list",
        "options": {
          "response": {
            "response": {
              "fullResponse": false
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer {{ $env.BACKEND_JWT_TOKEN }}"
            }
          ]
        }
      },
      "id": "fetch-suelo",
      "name": "Fetch Suelo",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        640,
        200
      ]
    },
    {
      "parameters": {
        "method": "GET",
        "url": "http://host.docker.internal:3001/trpc/clima.list",
        "options": {
          "response": {
            "response": {
              "fullResponse": false
            }
          }
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer {{ $env.BACKEND_JWT_TOKEN }}"
            }
          ]
        }
      },
      "id": "fetch-clima",
      "name": "Fetch Clima",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        640,
        400
      ]
    },
    {
      "parameters": {
        "mode": "combine",
        "mergeByFields": {},
        "combinationMode": "mergeByPosition",
        "options": {}
      },
      "id": "merge-data",
      "name": "Merge Data",
      "type": "n8n-nodes-base.merge",
      "typeVersion": 2.1,
      "position": [
        860,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={{ $env.GEMINI_API_KEY }}",
        "sendBody": true,
        "contentType": "json",
        "body": "={{ JSON.stringify({ contents: [{ parts: [{ text: `Eres un agr\u00f3nomo experto en agricultura de precisi\u00f3n en Argentina. Analiza los siguientes datos de campo y genera alertas agron\u00f3micas accionables. DATOS DE LOTES: ${JSON.stringify($node['Fetch Lotes'].json.result?.data ?? [])} DATOS DE SUELO (\u00faltimos registros): ${JSON.stringify(($node['Fetch Suelo'].json.result?.data ?? []).slice(-10))} DATOS DE CLIMA (\u00faltimos 7 d\u00edas): ${JSON.stringify(($node['Fetch Clima'].json.result?.data ?? []).slice(-7))} INSTRUCCIONES: - Eval\u00faa condiciones de estr\u00e9s h\u00eddrico, anomal\u00edas de suelo, riesgos clim\u00e1ticos y tendencias de rendimiento. - Genera entre 0 y 3 alertas relevantes. Si todo est\u00e1 dentro de par\u00e1metros normales, devuelve un array vac\u00edo. - Cada alerta debe ser un objeto JSON con EXACTAMENTE estos campos: tipo (string: humedad-baja|ph-acido|ph-alcalino|ola-calor|lluvia-intensa|rendimiento-bajo-pred|nutriente-deficiente|estres-hidrico), severidad (string: critica|alta|media|baja), categoria (string: hidrico|quimico|climatico|predictivo), mensaje (string: descripci\u00f3n concisa del problema detectado, max 120 chars), racional (string: explicaci\u00f3n t\u00e9cnica de por qu\u00e9 se genera la alerta con datos espec\u00edficos), recomendacion (string: acci\u00f3n concreta que el agr\u00f3nomo debe tomar), confianza (number: 0.0 a 1.0), loteId (number: ID del lote afectado, usar los IDs de los datos proporcionados) - RESPONDE SOLO con un JSON array v\u00e1lido, sin markdown, sin explicaciones. Ejemplo: [{ \"tipo\": \"humedad-baja\", \"severidad\": \"alta\", \"categoria\": \"hidrico\", \"mensaje\": \"Humedad de suelo cr\u00edtica en Lote Norte: 22%\", \"racional\": \"Lectura del 2026-05-03: humedad=22%, muy por debajo del umbral de 35% para soja.\", \"recomendacion\": \"Aplicar riego de 20mm en las pr\u00f3ximas 24hs.\", \"confianza\": 0.91, \"loteId\": 1 }]` }] }] }) }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": false
            }
          }
        }
      },
      "id": "gemini",
      "name": "Gemini Analizar",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        1080,
        300
      ]
    },
    {
      "parameters": {
        "jsCode": "// Parse Gemini response and extract alerts array\nconst geminiResponse = $input.first().json;\nconst text = geminiResponse.candidates?.[0]?.content?.parts?.[0]?.text ?? '[]';\n\n// Clean potential markdown fences\nconst cleaned = text.replace(/```json\\n?/g, '').replace(/```\\n?/g, '').trim();\n\ntry {\n  const alertas = JSON.parse(cleaned);\n  if (!Array.isArray(alertas) || alertas.length === 0) {\n    return [{ json: { _skip: true, reason: 'No alerts generated' } }];\n  }\n  return alertas.map(a => ({\n    json: {\n      tipo: a.tipo || 'general',\n      severidad: a.severidad || 'media',\n      categoria: a.categoria || 'climatico',\n      mensaje: (a.mensaje || '').substring(0, 200),\n      racional: a.racional || '',\n      recomendacion: a.recomendacion || '',\n      confianza: typeof a.confianza === 'number' ? a.confianza : 0.8,\n      loteId: a.loteId || null,\n      fecha: new Date().toISOString().split('T')[0]\n    }\n  }));\n} catch (e) {\n  return [{ json: { _skip: true, reason: 'Parse error: ' + e.message } }];\n}"
      },
      "id": "parse-alertas",
      "name": "Parse Alertas",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1300,
        300
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [],
          "string": [
            {
              "value1": "={{ $json._skip }}",
              "operation": "isEmpty"
            }
          ]
        }
      },
      "id": "filter-valid",
      "name": "Filter Valid",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        1520,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://lab03.robertoqr.dev/webhooks/n8n/alertas",
        "sendBody": true,
        "contentType": "json",
        "body": "={{ $json }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": false,
              "neverError": true
            }
          }
        }
      },
      "id": "post-alerta",
      "name": "POST Alerta",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        1740,
        300
      ],
      "notes": "Respuesta: { received, alertaId, email, telefono, data } -- data contiene el payload original de la alerta."
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "email-not-empty",
              "leftValue": "={{ $json.email }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-email",
      "name": "IF Email Configured",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1960,
        220
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "conditions": [
            {
              "id": "telefono-not-empty",
              "leftValue": "={{ $json.telefono }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEmpty",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-telefono",
      "name": "IF Tel\u00e9fono Configurado",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1960,
        400
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.emailjs.com/api/v1.0/email/send",
        "sendBody": true,
        "contentType": "json",
        "body": "={{ JSON.stringify({ service_id: $env.EMAILJS_SERVICE_ID, template_id: $env.EMAILJS_TEMPLATE_ID, user_id: $env.EMAILJS_PUBLIC_KEY, accessToken: $env.EMAILJS_PRIVATE_KEY, template_params: { to_email: $json.email, alerta_tipo: $json.data.tipo, alerta_severidad: $json.data.severidad, alerta_categoria: $json.data.categoria, alerta_mensaje: $json.data.mensaje, alerta_racional: $json.data.racional, alerta_recomendacion: $json.data.recomendacion, alerta_confianza: $json.data.confianza, alerta_lote_id: $json.data.loteId, alerta_fecha: $json.data.fecha } }) }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": false,
              "neverError": true
            }
          }
        }
      },
      "id": "send-email",
      "name": "Send Email (EmailJS)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2200,
        200
      ],
      "notes": "EmailJS requiere service_id, template_id, user_id (public key), accessToken (private key) como env vars"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.EVOLUTION_API_URL }}/message/sendText/{{ $env.EVOLUTION_INSTANCE }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "apikey",
              "value": "={{ $env.EVOLUTION_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "contentType": "json",
        "body": "={{ JSON.stringify({ number: $json.telefono, text: `\ud83d\udea8 *Alerta AgroTech*\\n\\n*Tipo:* ${$('Filter Valid').item.json.tipo}\\n*Severidad:* ${String($('Filter Valid').item.json.severidad).toUpperCase()}\\n*Categor\u00eda:* ${$('Filter Valid').item.json.categoria}\\n*Lote:* ${$('Filter Valid').item.json.loteId ?? 'N/A'}\\n*Fecha:* ${$('Filter Valid').item.json.fecha}\\n\\n*Mensaje:* ${$('Filter Valid').item.json.mensaje}\\n\\n*Recomendaci\u00f3n:* ${$('Filter Valid').item.json.recomendacion}\\n\\n_Confianza: ${($('Filter Valid').item.json.confianza * 100).toFixed(0)}%_` }) }}",
        "options": {
          "response": {
            "response": {
              "fullResponse": false,
              "neverError": true
            }
          }
        }
      },
      "id": "send-whatsapp",
      "name": "Send WhatsApp (Evolution API)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        2200,
        400
      ],
      "notes": "Evolution API env vars: EVOLUTION_API_URL, EVOLUTION_INSTANCE, EVOLUTION_API_KEY"
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Fetch Lotes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Lotes": {
      "main": [
        [
          {
            "node": "Fetch Suelo",
            "type": "main",
            "index": 0
          },
          {
            "node": "Fetch Clima",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Suelo": {
      "main": [
        [
          {
            "node": "Merge Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Clima": {
      "main": [
        [
          {
            "node": "Merge Data",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Data": {
      "main": [
        [
          {
            "node": "Gemini Analizar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Analizar": {
      "main": [
        [
          {
            "node": "Parse Alertas",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Alertas": {
      "main": [
        [
          {
            "node": "Filter Valid",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter Valid": {
      "main": [
        [
          {
            "node": "POST Alerta",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "POST Alerta": {
      "main": [
        [
          {
            "node": "IF Email Configured",
            "type": "main",
            "index": 0
          },
          {
            "node": "IF Tel\u00e9fono Configurado",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IF Email Configured": {
      "main": [
        [
          {
            "node": "Send Email (EmailJS)",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    },
    "IF Tel\u00e9fono Configurado": {
      "main": [
        [
          {
            "node": "Send WhatsApp (Evolution API)",
            "type": "main",
            "index": 0
          }
        ],
        []
      ]
    }
  }
}