#!/usr/bin/env python3
"""One-off cleanup para los hallazgos residuales en Cancún y Uruapan.

Cancún `opportunity.vehiculo` vs `opportunity.opportunityvehiculo`:
    4 records con valores complementarios (no contradictorios). Resolución:
    - Josué Alcocer y Rafael Reyes: el contact tiene la fuente canónica con
      marca+version+año completos. PUT opp.vehiculo = string canónico desde
      el contact.
    - ismael flores y Angel Juarez: contact vacío. Concatenar valores del
      orphan + clean en formato Marca Modelo Año.
    Después: DELETE orphan `opportunity.opportunityvehiculo`.

Uruapan `contact.fuente_del_prospecto`:
    1 record con orphan='Sucursal' vs clean='SUCURSAL'. Mismo valor lógico
    con casing distinto. Clean ya está alineado con Marca.
    Acción: DELETE orphan field (zombie en gabriela ramos queda invisible).

Default DRY-RUN. Usar --apply para escritura real.
"""

import argparse
import datetime
import json
import os
import sys
import time
import warnings

warnings.filterwarnings("ignore", message=r"urllib3 .* doesn't match a supported version!")
import requests

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 sync_engine  # noqa: E402

BASE_URL = "https://services.leadconnectorhq.com"
API_VERSION = "2021-07-28"
from paths import MIGRATIONS_DIR  # noqa: E402

CANCUN_LOC = "uJEn2iuUficuml9zxAnt"
URUAPAN_LOC = "FoQWuksh4wQjPbVVZ8ZQ"

# Valores finales decididos por record en Cancún.
CANCUN_OPP_VEHICULO_UPDATES = [
    {"opp_id": "hGghAwTK9vLoI7aRNED4", "label": "Josué Alcocer",
     "new_value": "Honda Brv Mas equipada 2018",
     "source": "regenerated_from_contact"},
    {"opp_id": "xiLM8vGogPWNHt9YljPC", "label": "Rafael Reyes Hernandez",
     "new_value": "Mercedes benz Cla 200 2019",
     "source": "regenerated_from_contact"},
    {"opp_id": "bdFbVzaBr2RJic2rZwDj", "label": "ismael flores",
     "new_value": "BEAT 2018",
     "source": "concat_orphan_clean"},
    {"opp_id": "wy4Y0QvVn48QN3iHcC3m", "label": "Angel Juarez Lopez",
     "new_value": "Nissan VERSSA 2025",
     "source": "concat_orphan_clean_normalized_tab"},
]

CANCUN_ORPHAN_FIELDKEY = "opportunity.opportunityvehiculo"
CANCUN_CLEAN_FIELDKEY = "opportunity.vehiculo"

URUAPAN_ORPHAN_FIELDKEY = "contact.fuente_del_prospecto"


_last_request_by_token: dict = {}


def wait_for_rate_limit(token):
    now = time.time()
    elapsed = now - _last_request_by_token.get(token, 0)
    if elapsed < 0.110:
        time.sleep(0.110 - elapsed)
    _last_request_by_token[token] = time.time()


def ghl_request(method, endpoint, token, *, params=None, json_body=None, version=API_VERSION):
    wait_for_rate_limit(token)
    url = endpoint if endpoint.startswith("http") else f"{BASE_URL}{endpoint}"
    headers = {
        "Accept": "application/json",
        "Authorization": f"Bearer {token}",
        "Version": version,
        "Content-Type": "application/json",
    }
    resp = requests.request(method, url, headers=headers, params=params, json=json_body, timeout=45)
    resp.raise_for_status()
    if resp.status_code == 204:
        return {}
    return resp.json()


def cf_value(record, field_id):
    for f in record.get("customFields", []) or []:
        if f.get("id") != field_id:
            continue
        for key in ("value", "fieldValue", "fieldValueString"):
            v = f.get(key)
            if v is not None:
                return v
        return None
    return None


def find_field_id(location_id, token, fieldkey):
    for model in ("contact", "opportunity"):
        data = ghl_request("GET", f"/locations/{location_id}/customFields", token,
                           params={"model": model})
        for cf in data.get("customFields") or []:
            if cf.get("fieldKey") == fieldkey:
                return cf.get("id")
    return None


def write_snapshot(payload, name):
    os.makedirs(MIGRATIONS_DIR, exist_ok=True)
    ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    safe = "".join(c if c.isalnum() else "_" for c in name)[:60]
    path = os.path.join(MIGRATIONS_DIR, f"residual_{safe}_{ts}.json")
    with open(path, "w", encoding="utf-8") as fh:
        json.dump(payload, fh, ensure_ascii=False, indent=2, default=str)
    return path


def fix_cancun(branch, apply_flag):
    location_id = branch["location_id"]
    token = branch["token"]
    print(f"\n=== CANCÚN ({location_id}) ===")
    clean_id = find_field_id(location_id, token, CANCUN_CLEAN_FIELDKEY)
    orphan_id = find_field_id(location_id, token, CANCUN_ORPHAN_FIELDKEY)
    print(f"  clean_field {CANCUN_CLEAN_FIELDKEY} → id={clean_id}")
    print(f"  orphan_field {CANCUN_ORPHAN_FIELDKEY} → id={orphan_id}")
    if not clean_id:
        print("  ✗ clean field no existe en Cancún. ABORTAR.")
        return

    # Snapshot del estado actual de cada opp + valor nuevo a aplicar
    snapshot_records = []
    for upd in CANCUN_OPP_VEHICULO_UPDATES:
        ro = ghl_request("GET", f"/opportunities/{upd['opp_id']}", token,
                         params={"locationId": location_id})
        opp = ro.get("opportunity", ro)
        snapshot_records.append({
            "opp_id": upd["opp_id"],
            "label": upd["label"],
            "contact_id": opp.get("contactId"),
            "current_clean": cf_value(opp, clean_id),
            "current_orphan": cf_value(opp, orphan_id) if orphan_id else None,
            "new_value": upd["new_value"],
            "source": upd["source"],
        })
    snap_path = write_snapshot({
        "branch": branch["nombre"],
        "location_id": location_id,
        "operation": "cancun_vehiculo_merge",
        "clean_field_id": clean_id,
        "orphan_field_id": orphan_id,
        "records": snapshot_records,
        "apply": apply_flag,
        "timestamp_utc": datetime.datetime.now(datetime.timezone.utc).isoformat(),
    }, "cancun_vehiculo")
    print(f"  Snapshot → {snap_path}")

    print()
    print("  Plan:")
    for rec in snapshot_records:
        print(f"    {rec['label']:30s} | actual='{rec['current_clean']}' → nuevo='{rec['new_value']}' ({rec['source']})")

    if not apply_flag:
        print(f"\n  [DRY-RUN] No se aplican cambios.")
        return

    # PUT cada opp con el nuevo valor en clean field
    errors = []
    updated = 0
    for rec in snapshot_records:
        try:
            ghl_request("PUT", f"/opportunities/{rec['opp_id']}", token,
                        json_body={"customFields": [{"id": clean_id, "value": rec["new_value"]}]})
            updated += 1
            print(f"    ✓ PUT opp {rec['opp_id']} ({rec['label']}) ← '{rec['new_value']}'")
        except Exception as exc:
            errors.append({"opp_id": rec["opp_id"], "error": str(exc)})
            print(f"    ✗ PUT falló opp {rec['opp_id']}: {exc}")

    if errors:
        print(f"\n  ✗ {len(errors)} PUTs fallaron. NO se elimina el orphan field. Snapshot preservado.")
        return

    if orphan_id:
        try:
            ghl_request("DELETE", f"/locations/{location_id}/customFields/{orphan_id}", token)
            print(f"\n  ✓ DELETE orphan field {orphan_id} ({CANCUN_ORPHAN_FIELDKEY})")
        except Exception as exc:
            print(f"\n  ⚠ DELETE orphan falló: {exc}")


def fix_uruapan(branch, apply_flag):
    location_id = branch["location_id"]
    token = branch["token"]
    print(f"\n=== URUAPAN ({location_id}) ===")
    orphan_id = find_field_id(location_id, token, URUAPAN_ORPHAN_FIELDKEY)
    print(f"  orphan_field {URUAPAN_ORPHAN_FIELDKEY} → id={orphan_id}")
    if not orphan_id:
        print(f"  ✓ orphan ya no existe. SKIP.")
        return

    snap_path = write_snapshot({
        "branch": branch["nombre"],
        "location_id": location_id,
        "operation": "uruapan_orphan_delete",
        "orphan_field_id": orphan_id,
        "orphan_fieldkey": URUAPAN_ORPHAN_FIELDKEY,
        "reason": "Valor 'Sucursal' duplicado funcionalmente como 'SUCURSAL' en contact.fuente_de_prospecto. Clean ya alineado con Marca.",
        "apply": apply_flag,
        "timestamp_utc": datetime.datetime.now(datetime.timezone.utc).isoformat(),
    }, "uruapan_fuente_del_prospecto")
    print(f"  Snapshot → {snap_path}")
    print(f"  Plan: DELETE orphan {orphan_id}. Zombie value en gabriela ramos queda invisible.")

    if not apply_flag:
        print(f"\n  [DRY-RUN] No se aplica.")
        return

    try:
        ghl_request("DELETE", f"/locations/{location_id}/customFields/{orphan_id}", token)
        print(f"  ✓ DELETE orphan field {orphan_id}")
    except Exception as exc:
        print(f"  ⚠ DELETE orphan falló: {exc}")


def main():
    parser = argparse.ArgumentParser(description="One-off cleanup Cancún + Uruapan.")
    parser.add_argument("--apply", action="store_true",
                        help="Aplica cambios reales. Sin esto corre en DRY-RUN.")
    parser.add_argument("--cancun-only", action="store_true",
                        help="Solo procesa Cancún.")
    parser.add_argument("--uruapan-only", action="store_true",
                        help="Solo procesa Uruapan.")
    args = parser.parse_args()

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

    accounts = sync_engine.parse_accounts_csv()
    accounts_by_id = {a["location_id"]: a for a in accounts}

    print("=" * 70)
    print(f"Modo: {'APPLY (escritura real)' if args.apply else 'DRY-RUN'}")
    print("=" * 70)

    if not args.uruapan_only:
        cancun = accounts_by_id.get(CANCUN_LOC)
        if cancun:
            fix_cancun(cancun, args.apply)
        else:
            print(f"\n⚠ Cancún ({CANCUN_LOC}) no en CSV.")

    if not args.cancun_only:
        uruapan = accounts_by_id.get(URUAPAN_LOC)
        if uruapan:
            fix_uruapan(uruapan, args.apply)
        else:
            print(f"\n⚠ Uruapan ({URUAPAN_LOC}) no en CSV.")


if __name__ == "__main__":
    main()
