#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Quita el tag `sucursal` de contactos de ORIGEN DIGITAL mal etiquetados.

Contexto: audit_origin_for_contact_list.py probó que un conjunto de contactos con
tags `facebook-ads` + `sucursal` son en realidad de origen DIGITAL
(createdBy.source = INTEGRATION en su sucursal, NO creados a mano). En la taxonomía
MP, `sucursal` y `facebook-ads` son tags de ORIGEN mutuamente excluyentes, así que
el tag `sucursal` sobra y genera el conflicto. Este script lo quita conservando
`facebook-ads` / LEAD DIGITAL.

Entrada: el CSV del audit (columnas marca_id, branch_location_id, branch_contact_id,
verdict, tags_marca). Solo procesa filas verdict == REAL_DIGITAL.

Quita el tag en AMBOS lados (Marca y la sucursal de origen), porque la réplica n8n
mantiene el tag en los dos. Para vencer la carrera con n8n (que puede re-escribir
los tags sucursal->Marca en paralelo), opera por RONDAS:
    1. Quita `sucursal` en cada lado pendiente (PUT/DELETE audytado).
    2. Espera --settle-seconds (default 60s) a que n8n termine cualquier réplica.
    3. Re-GET de cada lado y verifica que el tag NO regresó.
    4. Si regresó en algún lado, repite (hasta --max-rounds).
Termina solo cuando 0 lados tienen el tag (certeza de cambio efectivo) o se agotan
las rondas (reporta stragglers).

NO toca oportunidades (quitar un tag de contacto no crea ni modifica opps) -> riesgo
cero de oportunidades de más. Todo cambio queda en script_audit (reversible por run_id).
Dry-run por defecto; aplica solo con --apply.
"""

import argparse
import csv
import glob
import os
import sys
import time
import uuid

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 common  # noqa: E402
from common import BRAND_LOCATION_ID, REPORTS_DIR  # noqa: E402
from tag_canal_origen_workflow import ghl_request, safe_remove_contact_tag  # noqa: E402

SCRIPT_NAME = "fix_remove_sucursal_tag_digital.py"
TARGET_TAG = "sucursal"
OUT_DIR = os.path.join(REPORTS_DIR, "origin_audit")


def latest_report():
    files = sorted(glob.glob(os.path.join(OUT_DIR, "origin_audit_*.csv")))
    if not files:
        raise SystemExit("No hay reporte de audit en generated/reports/origin_audit/. Corre el audit primero.")
    return files[-1]


def load_targets(report_path, only_ids=None, limit=None):
    """Lee el CSV del audit y arma la lista de lados a corregir (Marca + sucursal)."""
    rows = list(csv.DictReader(open(report_path, encoding="utf-8-sig")))
    targets = []
    for r in rows:
        if r.get("verdict") != "REAL_DIGITAL":
            continue
        if only_ids and r["marca_id"] not in only_ids:
            continue
        marca_id = r["marca_id"]
        branch_loc = r.get("branch_location_id") or ""
        branch_cid = r.get("branch_contact_id") or ""
        sides = [{"location_id": BRAND_LOCATION_ID, "contact_id": marca_id, "label": "Marca"}]
        if branch_loc and branch_cid:
            sides.append({"location_id": branch_loc, "contact_id": branch_cid,
                          "label": r.get("tienda") or branch_loc})
        targets.append({"marca_id": marca_id, "nombre": r.get("nombre", ""), "sides": sides})
    if limit:
        targets = targets[:limit]
    return targets


def get_contact_live(contact_id, token):
    data = ghl_request("GET", f"/contacts/{contact_id}", token)
    c = data.get("contact") if isinstance(data.get("contact"), dict) else data
    return c


def has_tag(contact, tag):
    return tag in {str(t).strip().lower() for t in (contact.get("tags") or [])}


def main():
    if hasattr(sys.stdout, "reconfigure"):
        sys.stdout.reconfigure(encoding="utf-8")

    parser = argparse.ArgumentParser(description="Quita el tag `sucursal` de contactos digitales mal etiquetados. Dry-run por defecto.")
    parser.add_argument("--report", help="CSV del audit (default: el más reciente en generated/reports/origin_audit/)")
    parser.add_argument("--only", help="Lista de marca_id separados por coma (para piloto)")
    parser.add_argument("--limit", type=int, help="Limita el número de contactos (para piloto)")
    parser.add_argument("--apply", action="store_true", help="Aplica los cambios. Sin este flag = dry-run.")
    parser.add_argument("--run-id", help="ID de auditoría (se crea uno si se aplica y no se da)")
    parser.add_argument("--settle-seconds", type=int, default=60, help="Espera entre quitar y verificar (default 60s, por la réplica n8n)")
    parser.add_argument("--max-rounds", type=int, default=4, help="Rondas máximas de quitar->esperar->verificar")
    args = parser.parse_args()

    dry_run = not args.apply
    only_ids = {x.strip() for x in args.only.split(",")} if args.only else None
    report_path = args.report or latest_report()
    targets = load_targets(report_path, only_ids=only_ids, limit=args.limit)

    accounts = {a["location_id"]: a for a in common.load_accounts(include_main=True)}

    # Aplana todos los "lados" (contacto+location) a corregir.
    sides = []
    for t in targets:
        for s in t["sides"]:
            if s["location_id"] not in accounts:
                print(f"  AVISO: sin token para {s['location_id']} ({t['nombre']}); se omite ese lado")
                continue
            sides.append({**s, "nombre": t["nombre"], "token": accounts[s["location_id"]]["token"]})

    print("=" * 74)
    print("CORRECCIÓN: quitar tag `sucursal` de contactos de ORIGEN DIGITAL")
    print("=" * 74)
    print(f"Reporte: {os.path.basename(report_path)}")
    print(f"Modo: {'DRY-RUN (sin cambios)' if dry_run else 'APPLY (escribe en el CRM)'}")
    print(f"Contactos objetivo: {len(targets)} | lados (Marca+sucursal): {len(sides)}")
    print(f"Settle: {args.settle_seconds}s | rondas máx: {args.max_rounds}\n")

    run_id = args.run_id
    if not dry_run:
        run_id = run_id or str(uuid.uuid4())
        script_audit.create_run(run_id, SCRIPT_NAME, arguments=" ".join(sys.argv[1:]),
                                locations=sorted({s["location_id"] for s in sides}))
        print(f"run_id: {run_id}\n")

    # --- Rondas: quitar -> esperar -> verificar (vence la carrera con n8n) ---
    removed_total = 0
    for rnd in range(1, args.max_rounds + 1):
        # 1) Detectar lados que AÚN tienen el tag (GET vivo).
        pending = []
        for s in sides:
            contact = get_contact_live(s["contact_id"], s["token"])
            if has_tag(contact, TARGET_TAG):
                pending.append((s, contact))

        print(f"--- Ronda {rnd}: {len(pending)} lado(s) con tag `{TARGET_TAG}`")
        if not pending:
            print("  Todos los lados limpios. Cambio efectivo confirmado.")
            break

        # 2) Quitar el tag en cada lado pendiente.
        for s, contact in pending:
            changed = safe_remove_contact_tag(run_id, s["location_id"], contact, TARGET_TAG,
                                               s["token"], dry_run=dry_run)
            if changed:
                removed_total += 1
                print(f"  [{'DRY' if dry_run else 'DEL'}] {s['nombre']:<28} | {s['label']:<24} | tag- {TARGET_TAG}")

        if dry_run:
            print("\nDry-run: no se espera ni verifica. Vuelve a correr con --apply --run-id para aplicar.")
            break

        # 3) Esperar a que n8n termine cualquier réplica en vuelo, luego la próxima
        #    ronda re-verifica (paso 1) si el tag regresó.
        if rnd < args.max_rounds:
            print(f"  Esperando {args.settle_seconds}s a que n8n estabilice...")
            time.sleep(args.settle_seconds)
    else:
        # se agotaron las rondas: verificación final
        leftover = [s for s in sides if has_tag(get_contact_live(s["contact_id"], s["token"]), TARGET_TAG)]
        if leftover:
            print(f"\n  ATENCIÓN: {len(leftover)} lado(s) AÚN con el tag tras {args.max_rounds} rondas:")
            for s in leftover:
                print(f"    - {s['nombre']} | {s['label']} | {s['contact_id']}")

    if not dry_run and run_id:
        script_audit.update_run_status(run_id, "success")

    print("\n" + "=" * 74)
    print(f"RESUMEN: lados con tag quitado={removed_total} ({'simulado' if dry_run else 'aplicado'})")
    if not dry_run:
        print(f"run_id para rollback desde el dashboard: {run_id}")


if __name__ == "__main__":
    main()
