# -*- coding: utf-8 -*-
"""Cablea el UPSERT en tiempo real del mapeo Baserow (tabla 754) tras crear/
actualizar la opp en Marca, en el workflow Cfgwp0bOtDW8zuKW.

Contexto: el lookup Baserow + 'Decidir Match' ya evitan duplicados cuando el
mapeo id_opp_sucursal->id_opp_marca existe. PERO las salidas de
'Crear Oportunidad - MARCA' y 'Actualizar Oportunidad - MARCA (v2)' estaban
VACIAS -> el mapeo solo se refrescaba con el backfill agendado (cada 30 min).
En esa ventana, una re-ejecucion del mismo opp podia volver a no encontrar el
mapeo y, si el contacto era ambiguo, crear un duplicado.

Fix (aditivo, 2 nodos, create-only condicional -> minimo riesgo):
  - 'Preparar Upsert Mapeo' [code]: decide si hay que escribir el mapeo. Solo
    escribe cuando NO existe aun (action CREATE, o UPDATE resuelto por contacto;
    NUNCA cuando 'Decidir Match' ya lo resolvio via Baserow -> la fila ya existe,
    no duplicar). Calcula id_opp_marca segun el camino. Si no hace falta -> [].
  - 'Crear Mapeo - Baserow' [baserow create]: inserta la fila {id_opp_sucursal,
    id_opp_marca, location_id_sucursal, updated_at}.
  - Ambos con onError=continueRegularOutput: si Baserow falla, NO rompe la
    replicacion (la opp ya quedo creada/actualizada aguas arriba; el backfill
    completara el mapeo despues).

Idempotente: el backfill verifica por id_opp_sucursal antes de crear, asi que
una fila creada por este upsert no se duplica luego.

Dry-run por defecto (dump a n8n/dryrun_*.json); --apply para PUT real.
"""
import argparse
import os
import sys
import uuid

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from scripts.n8n_workflow_lib import load_credentials, N8NClient

WID = "Cfgwp0bOtDW8zuKW"
BASEROW_DB = 63
MAPPING_TABLE = 754
F_ID_OPP_SUCURSAL = 7280
F_ID_OPP_MARCA = 7283
F_LOCATION_SUCURSAL = 7284
F_UPDATED_AT = 7285
BASEROW_CRED = {"baserowApi": {"id": "LZztQ3WMpzXjSTIH", "name": "Baserow account"}}

NAME_PREP = "Preparar Upsert Mapeo"
NAME_CREATE = "Crear Mapeo - Baserow"
CREATE_SRC = "Crear Oportunidad - MARCA"
UPDATE_SRC = "Actualizar Oportunidad - MARCA (v2)"
DECIDE = "Decidir Match (Create vs Update)"

PREP_CODE = """// Prepara el UPSERT del mapeo Baserow (tabla 754) tras CREATE/UPDATE de la opp en Marca.
// Solo escribe cuando el mapeo NO existe aun:
//   - action CREATE            -> la fila no existe (el lookup Baserow fallo) -> crear.
//   - action UPDATE por contacto -> Baserow no la tenia -> crear (idempotencia futura).
//   - action UPDATE por Baserow  -> la fila YA existe -> NO escribir (no duplicar).
// Asi una re-ejecucion del mismo opp encuentra el mapeo y hace UPDATE, no CREATE duplicado.
const decision = $('Decidir Match (Create vs Update)').first().json;
const action = decision.action;
const matchReason = decision.matchReason || '';
const sucursalOppId = decision.sucursalOppId;

const matchedByBaserow = matchReason.indexOf('Baserow') !== -1;
const needWrite = (action === 'CREATE') || (action === 'UPDATE' && !matchedByBaserow);
if (!needWrite) return [];

// id de la opp en Marca segun el camino
let marcaOppId = decision.opportunityId;
if (action === 'CREATE') {
  try {
    const co = $('Crear Oportunidad - MARCA').first().json;
    marcaOppId = (co.opportunity && co.opportunity.id) || co.id || marcaOppId;
  } catch (e) {}
}
if (!marcaOppId || !sucursalOppId) return [];

let locationSucursal = '';
try { locationSucursal = $('DATOS API - SUCURSAL').first().json['Location ID'] || ''; } catch (e) {}

return [{ json: { sucursalOppId, marcaOppId, locationSucursal } }];
"""


def build_prep_node():
    return {
        "parameters": {"jsCode": PREP_CODE},
        "type": "n8n-nodes-base.code",
        "typeVersion": 2,
        "position": [6040, 960],
        "id": str(uuid.uuid4()),
        "name": NAME_PREP,
        "onError": "continueRegularOutput",
        "notes": ("Decide si escribir el mapeo Baserow tras CREATE/UPDATE. Solo cuando la "
                  "fila no existe (CREATE o UPDATE-por-contacto). onError continue para no "
                  "romper la replicacion."),
    }


def build_create_node():
    return {
        "parameters": {
            "operation": "create",
            "databaseId": BASEROW_DB,
            "tableId": MAPPING_TABLE,
            "fieldsUi": {
                "fieldValues": [
                    {"fieldId": F_ID_OPP_SUCURSAL, "fieldValue": "={{ $json.sucursalOppId }}"},
                    {"fieldId": F_ID_OPP_MARCA, "fieldValue": "={{ $json.marcaOppId }}"},
                    {"fieldId": F_LOCATION_SUCURSAL, "fieldValue": "={{ $json.locationSucursal }}"},
                    {"fieldId": F_UPDATED_AT, "fieldValue": "={{ $now.toISO() }}"},
                ]
            },
        },
        "type": "n8n-nodes-base.baserow",
        "typeVersion": 1,
        "position": [6280, 960],
        "id": str(uuid.uuid4()),
        "name": NAME_CREATE,
        "credentials": BASEROW_CRED,
        "onError": "continueRegularOutput",
        "notes": ("Inserta el mapeo id_opp_sucursal->id_opp_marca en la tabla 754 en tiempo "
                  "real. Idempotente con el backfill (verifica por id_opp_sucursal)."),
    }


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--apply", action="store_true", help="PUT real (default: dry-run).")
    args = ap.parse_args()
    client = N8NClient(*load_credentials())

    print(f"[1/6] GET workflow {WID}")
    wf = client.get_workflow(WID)
    print(f"      versionId previo: {wf.get('versionId')} | active: {wf.get('active')} | nodos: {len(wf.get('nodes') or [])}")

    existing = {n["name"] for n in wf.get("nodes") or []}
    for needed in [CREATE_SRC, UPDATE_SRC, DECIDE]:
        if needed not in existing:
            print(f"\nERROR: falta nodo requerido {needed!r}. Abortando.")
            sys.exit(2)
    for new in [NAME_PREP, NAME_CREATE]:
        if new in existing:
            print(f"\nERROR: nodo {new!r} ya existe. Abortando para no duplicar.")
            sys.exit(2)

    print("\n[2/6] Backup fresco")
    _, bpath = client.backup_workflow(WID, label="pre_baserow_opp_upsert")
    print(f"      backup -> {bpath}")

    print("\n[3/6] Inyectar nodos")
    client.add_node(wf, build_prep_node())
    client.add_node(wf, build_create_node())
    print(f"      + {NAME_PREP}\n      + {NAME_CREATE}")

    print("\n[4/6] Reconectar grafo (aditivo; las salidas estaban vacias)")
    conns = wf["connections"]
    conns[CREATE_SRC] = {"main": [[{"node": NAME_PREP, "type": "main", "index": 0}]]}
    conns[UPDATE_SRC] = {"main": [[{"node": NAME_PREP, "type": "main", "index": 0}]]}
    conns[NAME_PREP] = {"main": [[{"node": NAME_CREATE, "type": "main", "index": 0}]]}
    print(f"      {CREATE_SRC} -> {NAME_PREP}")
    print(f"      {UPDATE_SRC} -> {NAME_PREP}")
    print(f"      {NAME_PREP} -> {NAME_CREATE}")

    print(f"\n[5/6] {'APPLY' if args.apply else 'DRY-RUN'} put_workflow")
    result = client.put_workflow(WID, wf, dry_run=not args.apply)
    if not args.apply:
        print(f"      dry-run dump -> {result}")
        print("\nLISTO. Revisa el JSON. Si todo bien, corre con --apply.")
        return

    print("\n[6/6] Verificar")
    new_wf = client.verify_post(WID, expected_node_names=[NAME_PREP, NAME_CREATE],
                                prev_version_id=wf.get("versionId"))
    print(f"      versionId nuevo: {new_wf.get('versionId')}")
    if not new_wf.get("active"):
        print("      reactivando workflow...")
        client.activate(WID)
        new_wf = client.get_workflow(WID)
    print(f"      active final: {new_wf.get('active')} | nodos: {len(new_wf.get('nodes') or [])}")
    print("\nOK. Upsert Baserow en tiempo real cableado (degradacion elegante via onError).")


if __name__ == "__main__":
    main()
