#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Backfill de custom fields descriptivos en una opp de MARCA que quedó "solo
enlace" (solo `opportunity.id_oportunidad_sucursal`), tomando los valores de la
opp de sucursal enlazada y, como respaldo, del CONTACTO de sucursal.

Caso de origen: la opp de Marca `8HITkGkOn3gN23Tl8LBr` (Miguel Temixco) quedó sin
Sucursal / TIENDA / Canal de Origen / Vehículo. El nodo de réplica las copia por
`fieldKey`, pero esta opp se creó fuera del flujo normal. Mismo `fieldKey`
canónico (sufijo) en contact y opportunity.

Prioridad por campo: (a) valor en la opp de sucursal → (b) valor en el contacto
de sucursal. Solo rellena campos que estén VACÍOS en la opp de Marca (no
sobreescribe). dry-run por defecto; snapshot + script_audit para rollback.

Uso:
    python scripts/backfill_brand_opp_cf_from_source.py \\
        --brand-opp-id 8HITkGkOn3gN23Tl8LBr --branch-location-id yjqKxoO02rsdwdJZSPmD
    # añade --apply para escribir en Bucéfalo
"""

import argparse
import datetime
import json
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)

import script_audit  # noqa: E402
import sync_engine  # noqa: E402
from scripts import common  # noqa: E402
from paths import MIGRATIONS_DIR  # noqa: E402

gc = sync_engine.ghl_client
BRAND_LOCATION_ID = common.BRAND_LOCATION_ID
LINK_KEY = "opportunity.id_oportunidad_sucursal"

# (opp fieldKey canónico, contact fieldKey de respaldo o None).
TARGETS = [
    ("opportunity.sucursal", "contact.sucursal"),
    ("opportunity.tienda", "contact.tienda"),
    ("opportunity.fuente_de_posible_cliente", "contact.fuente_de_posible_cliente"),  # CANAL DE ORIGEN
    ("opportunity.fuente_de_prospecto", "contact.fuente_de_prospecto"),
    ("opportunity.vehiculo", None),  # el contacto guarda marca/versión/año por separado
]


def clean(v):
    return "" if v is None else str(v).strip()


def opp_cf_value(cf):
    for k in ("fieldValue", "fieldValueString", "value"):
        if cf.get(k) is not None:
            return cf[k]
    return None


def schema_key_by_id(token, location_id, object_key):
    """id -> fieldKey usando el schema dinámico de la location."""
    fields = gc.get_object_schema_fields(token, location_id, object_key)
    return {f["id"]: f.get("fieldKey") for f in fields if f.get("id")}, \
           {f.get("fieldKey"): f for f in fields if f.get("fieldKey")}


def main():
    ap = argparse.ArgumentParser(description="Backfill CF descriptivos en opp de Marca desde la opp/contacto de sucursal.")
    ap.add_argument("--brand-opp-id", required=True)
    ap.add_argument("--branch-location-id", required=True)
    ap.add_argument("--brand-location-id", default=BRAND_LOCATION_ID)
    ap.add_argument("--apply", action="store_true")
    ap.add_argument("--run-id", default=None)
    args = ap.parse_args()

    accounts = {a["location_id"]: a for a in common.load_accounts()}
    brand = accounts.get(args.brand_location_id)
    branch = accounts.get(args.branch_location_id)
    if not brand or not branch:
        raise SystemExit("No se encontró el token de la location de Marca o sucursal en el CSV.")
    brand_token, branch_token = brand["token"], branch["token"]

    # 1. Opp de Marca (estado actual).
    brand_opp = (gc.get_opportunity(brand_token, args.brand_opp_id) or {}).get("opportunity") or {}
    if not brand_opp:
        raise SystemExit(f"No se pudo leer la opp de Marca {args.brand_opp_id}.")
    brand_id2key, brand_key2def = schema_key_by_id(brand_token, args.brand_location_id, "opportunity")
    brand_val_by_key = {}
    for cf in brand_opp.get("customFields") or []:
        k = brand_id2key.get(cf.get("id"))
        if k:
            brand_val_by_key[k] = opp_cf_value(cf)

    # 2. Resolver la opp de sucursal enlazada (del CF link de Marca).
    branch_opp_id = clean(brand_val_by_key.get(LINK_KEY))
    if not branch_opp_id:
        raise SystemExit(f"La opp de Marca no tiene {LINK_KEY}; no se puede resolver el origen.")
    branch_opp = (gc.get_opportunity(branch_token, branch_opp_id) or {}).get("opportunity") or {}
    if not branch_opp:
        raise SystemExit(f"No se pudo leer la opp de sucursal {branch_opp_id}.")
    branch_id2key, _ = schema_key_by_id(branch_token, args.branch_location_id, "opportunity")
    branch_opp_val_by_key = {}
    for cf in branch_opp.get("customFields") or []:
        k = branch_id2key.get(cf.get("id"))
        if k:
            branch_opp_val_by_key[k] = opp_cf_value(cf)

    # 3. Contacto de sucursal (respaldo).
    branch_contact_val_by_key = {}
    cid = branch_opp.get("contactId") or branch_opp.get("contact", {}).get("id")
    if cid:
        contact = (gc._request("GET", f"/contacts/{cid}", branch_token) or {}).get("contact") or {}
        c_id2key, _ = schema_key_by_id(branch_token, args.branch_location_id, "contact")
        for cf in contact.get("customFields") or []:
            k = c_id2key.get(cf.get("id"))
            if k:
                branch_contact_val_by_key[k] = cf.get("value")

    # 4. Calcular el backfill (solo campos vacíos en Marca).
    run_id = args.run_id or f"backfill-opp-cf-{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}"
    plan = []
    for opp_key, contact_key in TARGETS:
        if clean(brand_val_by_key.get(opp_key)) != "":
            continue  # ya tiene valor, no sobreescribir
        value = clean(branch_opp_val_by_key.get(opp_key))
        source = "opp_sucursal"
        if value == "" and contact_key:
            value = clean(branch_contact_val_by_key.get(contact_key))
            source = "contacto_sucursal"
        if value == "":
            continue
        bdef = brand_key2def.get(opp_key)
        if not bdef:
            print(f"  WARN: Marca no tiene el campo {opp_key}; se omite.")
            continue
        plan.append({"opp_key": opp_key, "field_id": bdef["id"], "name": bdef.get("name"),
                     "value": value, "source": source})

    print(f"Opp Marca: {args.brand_opp_id}  <- opp sucursal: {branch_opp_id}  (contacto {cid})")
    if not plan:
        print("Nada que rellenar (todos los campos objetivo ya tienen valor o no hay fuente).")
        return
    print(f"Campos a rellenar ({len(plan)}):")
    for p in plan:
        print(f"  {p['name']:20} [{p['opp_key']}] = {p['value']!r}   (fuente: {p['source']})")

    if not args.apply:
        print("\nDRY-RUN. Vuelve a correr con --apply para escribir en Bucéfalo.")
        return

    # 5. Snapshot + audit + PUT.
    os.makedirs(MIGRATIONS_DIR, exist_ok=True)
    snap_path = os.path.join(MIGRATIONS_DIR, f"backfill_opp_cf_{args.brand_opp_id}_{run_id}.json")
    with open(snap_path, "w", encoding="utf-8") as fh:
        json.dump({"run_id": run_id, "brand_opp_id": args.brand_opp_id, "before": brand_opp, "plan": plan},
                  fh, ensure_ascii=False, indent=2)
    print(f"  snapshot -> {snap_path}")

    script_audit.create_run(run_id, "backfill_brand_opp_cf_from_source", arguments=" ".join(sys.argv[1:]),
                            locations=[args.brand_location_id])
    change_ids = []
    for p in plan:
        cidc = script_audit.record_change(run_id, args.brand_location_id, "opportunity", args.brand_opp_id,
                                          p["field_id"], p["name"], None, p["value"])
        change_ids.append(cidc)

    payload = {"customFields": [{"id": p["field_id"], "key": p["opp_key"], "field_value": p["value"]} for p in plan]}
    gc.update_opportunity(brand_token, args.brand_opp_id, payload)
    for cidc in change_ids:
        script_audit.mark_change(cidc, "applied")
    script_audit.update_run_status(run_id, "success")
    print(f"  PUT aplicado. run_id={run_id} (reversible desde el dashboard).")


if __name__ == "__main__":
    main()
