#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Complementa el workflow n8n [2004] (ddUEORBEtZLzsQF2) para que, al crear un
contacto en sucursal, además de rellenar contact.sucursal / contact.tienda,
escriba "Canal de Origen" = SUCURSAL y deje el tag de origen correcto, PERO solo
cuando el contacto fue creado por un usuario (createdBy.source ∈ {WEB_USER,
MOBILE_USER}). Los creados por integración (INTEGRATION) no se tocan.

Replica en TIEMPO REAL el criterio del batch scripts/fix_branch_user_origin.py.

Cambios (solo AÑADE, preserva el flujo actual):
  1. Extiende el Code node "Buscar contact.sucursal y contact.tienda" para
     resolver también el field "Canal de Origen" (por fieldKey con fallback a
     nombre) y exponer createdBySource / esUsuario (leído del GET del contacto).
  2. Tras el PUT actual de sucursal/tienda añade:
       IF "Creado por usuario" -> [true] PUT Canal de Origen = SUCURSAL
            -> Tag+ sucursal -> Tag- formulario -> Tag- facebook-ads
       [false] (fin: no se toca).

Uso:
    python n8n/_add_canal_origen_branch.py            # dry-run (dumpea payload)
    python n8n/_add_canal_origen_branch.py --apply     # aplica + reactiva
"""

import argparse
import os
import sys

ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if ROOT_DIR not in sys.path:
    sys.path.insert(0, ROOT_DIR)

from scripts.n8n_workflow_lib import (  # noqa: E402
    AlreadyExistsError,
    N8NClient,
    load_credentials,
)

WID = "ddUEORBEtZLzsQF2"

# Nodos existentes (referencias).
CODE_NODE = 'Buscar "contact.sucursal" y "contact.tienda"'
GET_CONTACT_NODE = "Obtener Contacto Cuenta Origen - SUCURSAL"
PUT_SUCURSAL_NODE = "Actualizar Contacto Cuenta Objetivo - SUCURSAL"
VERIFICADOR_NODE = "Buscar Sucursal en Verificador de Sucursales"

# Nodos nuevos.
IF_NODE = "Creado por usuario (Canal de Origen)"
PUT_CANAL_NODE = "PUT Canal de Origen = SUCURSAL"
TAG_ADD_NODE = "Tag+ sucursal"
TAG_RM_FORM_NODE = "Tag- formulario"
TAG_RM_FB_NODE = "Tag- facebook-ads"

# Expresiones reutilizables (referencian nodos upstream por nombre).
CONTACT_ID_EXPR = "{{ $('Datos de Lead').item.json.Cliente['Contact ID'] }}"
TOKEN_EXPR = "{{ $('" + VERIFICADOR_NODE + "').item.json['SC TOKEN BUCEFALO'] }}"
CODE_REF = "$('" + CODE_NODE + "').item.json"

HEADERS = [
    {"name": "Accept", "value": "application/json"},
    {"name": "Version", "value": "2021-07-28"},
    {"name": "Authorization", "value": "=Bearer " + TOKEN_EXPR},
]

# Code node extendido: preserva sucursal/tienda EXACTO y añade canal + esUsuario.
NEW_JSCODE = r"""const customFields = $input.first().json.customFields;

function findField(key, names) {
  let f = customFields.find(x => x.fieldKey === key);
  if (!f) {
    const wanted = names.map(n => n.toLowerCase().trim());
    f = customFields.find(x => wanted.includes((x.name || "").toLowerCase().trim()));
  }
  return f || null;
}

const sucursalField = findField("contact.sucursal", ["Sucursal"]);
const tiendaField = findField("contact.tienda", ["TIENDA", "Tienda"]);
const canalField = findField("contact.fuente_de_posible_cliente", ["CANAL DE ORIGEN", "Canal de Origen"]);

// createdBy.source SOLO viene del GET individual del contacto.
const contactResp = $('Obtener Contacto Cuenta Origen - SUCURSAL').item.json;
const createdBySource =
  (contactResp && contactResp.contact && contactResp.contact.createdBy && contactResp.contact.createdBy.source) ||
  (contactResp && contactResp.createdBy && contactResp.createdBy.source) ||
  null;
const esUsuario = createdBySource === "WEB_USER" || createdBySource === "MOBILE_USER";

return [
  {
    json: {
      sucursal: {
        id: sucursalField?.id ?? null,
        name: sucursalField?.name ?? null,
        fieldKey: sucursalField?.fieldKey ?? null,
        picklistOptions: sucursalField?.picklistOptions ?? [],
      },
      tienda: {
        id: tiendaField?.id ?? null,
        name: tiendaField?.name ?? null,
        fieldKey: tiendaField?.fieldKey ?? null,
        picklistOptions: tiendaField?.picklistOptions ?? [],
      },
      canal: {
        id: canalField?.id ?? null,
        name: canalField?.name ?? null,
        fieldKey: canalField?.fieldKey ?? null,
        picklistOptions: canalField?.picklistOptions ?? [],
      },
      createdBySource: createdBySource,
      esUsuario: esUsuario,
    },
  },
];"""


def http_node(name, position, *, method, body_obj_expr, on_error=False):
    """Construye un nodo httpRequest (typeVersion 4.2) con auth Bearer en header."""
    params = {
        "method": method,
        "url": "=https://services.leadconnectorhq.com/contacts/" + CONTACT_ID_EXPR + ("/tags" if method in ("POST", "DELETE") else ""),
        "sendHeaders": True,
        "headerParameters": {"parameters": [dict(h) for h in HEADERS]},
        "sendBody": True,
        "specifyBody": "json",
        "jsonBody": body_obj_expr,
        "options": {"redirect": {"redirect": {}}},
    }
    node = {
        "parameters": params,
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": position,
        "name": name,
    }
    if on_error:
        # Un DELETE de un tag inexistente puede no aplicar; no debe romper el flujo.
        node["onError"] = "continueRegularOutput"
    return node


def build_nodes():
    # PUT Canal de Origen = SUCURSAL (solo este CF; NO toca Fuente de Prospecto).
    canal_body = (
        "={\n"
        '  "customFields": [\n'
        "    {\n"
        '      "id": "{{ ' + CODE_REF + '.canal.id }}",\n'
        '      "key": "{{ ' + CODE_REF + '.canal.fieldKey }}",\n'
        '      "field_value": "SUCURSAL"\n'
        "    }\n"
        "  ]\n"
        "}"
    )
    put_canal = {
        "parameters": {
            "method": "PUT",
            "url": "=https://services.leadconnectorhq.com/contacts/" + CONTACT_ID_EXPR,
            "sendHeaders": True,
            "headerParameters": {"parameters": [dict(h) for h in HEADERS]},
            "sendBody": True,
            "specifyBody": "json",
            "jsonBody": canal_body,
            "options": {"redirect": {"redirect": {}}},
        },
        "type": "n8n-nodes-base.httpRequest",
        "typeVersion": 4.2,
        "position": [940, -120],
        "name": PUT_CANAL_NODE,
    }

    if_node = {
        "parameters": {
            "conditions": {
                "options": {"caseSensitive": True, "leftValue": "", "typeValidation": "loose", "version": 3},
                "conditions": [
                    {
                        "id": "canal-origen-esusuario",
                        "leftValue": "={{ " + CODE_REF + ".esUsuario }}",
                        "rightValue": "",
                        "operator": {"type": "boolean", "operation": "true", "singleValue": True},
                    }
                ],
                "combinator": "and",
            },
            "options": {},
        },
        "type": "n8n-nodes-base.if",
        "typeVersion": 2.3,
        "position": [720, -120],
        "name": IF_NODE,
    }

    tag_add = http_node(TAG_ADD_NODE, [1160, -120], method="POST",
                        body_obj_expr='={\n  "tags": ["sucursal"]\n}')
    tag_rm_form = http_node(TAG_RM_FORM_NODE, [1380, -120], method="DELETE",
                            body_obj_expr='={\n  "tags": ["formulario"]\n}', on_error=True)
    tag_rm_fb = http_node(TAG_RM_FB_NODE, [1600, -120], method="DELETE",
                          body_obj_expr='={\n  "tags": ["facebook-ads"]\n}', on_error=True)
    return if_node, put_canal, tag_add, tag_rm_form, tag_rm_fb


def main():
    parser = argparse.ArgumentParser(description="Añade la rama Canal de Origen al workflow n8n [2004].")
    parser.add_argument("--apply", action="store_true", help="Aplica el PUT real (sin esto: dry-run).")
    args = parser.parse_args()

    client = N8NClient(*load_credentials())
    wf, backup_path = client.backup_workflow(WID, label="canal_origen")
    prev_version = wf.get("versionId")
    print(f"Workflow: {wf.get('name')}")
    print(f"  active={wf.get('active')} nodes={len(wf.get('nodes') or [])} versionId={prev_version}")
    print(f"  backup -> {backup_path}")

    # Idempotencia: si ya están los nodos nuevos, no re-aplicar.
    for nm in (IF_NODE, PUT_CANAL_NODE, TAG_ADD_NODE):
        if client.find_node(wf, nm) is not None:
            raise SystemExit(f"El nodo {nm!r} ya existe; el complemento ya fue aplicado. Nada que hacer.")

    # 1. Extender el Code node.
    code_node = client.find_node(wf, CODE_NODE)
    if code_node is None:
        raise SystemExit(f"No se encontró el Code node {CODE_NODE!r}.")
    code_node["parameters"]["jsCode"] = NEW_JSCODE
    print(f"  Code node {CODE_NODE!r}: jsCode extendido (canal + esUsuario).")

    # 2. Añadir nodos nuevos.
    if_node, put_canal, tag_add, tag_rm_form, tag_rm_fb = build_nodes()
    for n in (if_node, put_canal, tag_add, tag_rm_form, tag_rm_fb):
        client.assert_idempotent(wf, n["name"])
        client.add_node(wf, n)
    print("  Nodos añadidos: IF + PUT canal + 3 de tags.")

    # 3. Conexiones: PUT sucursal -> IF -> [true] PUT canal -> tag+ -> tag- form -> tag- fb.
    client.set_connection(wf, PUT_SUCURSAL_NODE, IF_NODE)         # main:0
    client.set_connection(wf, IF_NODE, PUT_CANAL_NODE, output_index=0)  # rama true
    # rama false (index 1): sin destino (fin).
    client.set_connection(wf, PUT_CANAL_NODE, TAG_ADD_NODE)
    client.set_connection(wf, TAG_ADD_NODE, TAG_RM_FORM_NODE)
    client.set_connection(wf, TAG_RM_FORM_NODE, TAG_RM_FB_NODE)
    print("  Conexiones cableadas.")

    expected = [IF_NODE, PUT_CANAL_NODE, TAG_ADD_NODE, TAG_RM_FORM_NODE, TAG_RM_FB_NODE]

    if not args.apply:
        res = client.put_workflow(WID, wf, dry_run=True)
        print(f"\nDRY-RUN. Payload -> {res['path']} ({res['node_count']} nodos).")
        print("Revisa el JSON y vuelve a correr con --apply para aplicar.")
        return

    was_active = bool(wf.get("active"))
    if was_active:
        try:
            client.deactivate(WID)
            print("  Workflow desactivado para PUT estructural.")
        except Exception as exc:
            print(f"  WARN al desactivar: {exc}")
    client.put_workflow(WID, wf, dry_run=False)
    print("  PUT aplicado.")
    if was_active:
        client.activate(WID)
        print("  Workflow reactivado.")
    client.verify_post(WID, expected_node_names=expected, prev_version_id=prev_version)
    print("\nOK: complemento aplicado y verificado. Backup en:", backup_path)


if __name__ == "__main__":
    main()
