#!/usr/bin/env python3
"""
Auditoria de mismatch nome — Conafor Smart · 27/08/2026
Lê CSV + log, consulta API GHL PX3 via curl, gera relatório MD + log completo.
NAO modifica nada no Linkia.
"""

import csv
import re
import time
import unicodedata
import json
import subprocess
import sys
from datetime import datetime
from collections import Counter

# credenciais
TOKEN       = "pit-25f78b4f-4cc8-48f2-b8dc-73a98f851eaf"
BASE_URL    = "https://services.leadconnectorhq.com"

# paths
CSV_PATH    = "/opt/mia-bot/docs/1787836331382479.csv"
LOG_PATH    = "/opt/mia/workspace/clientes/px3lab/imports/conafor_smart_FIX_20260827_1324.log"
OUT_MD      = "/opt/mia/workspace/clientes/px3lab/imports/conafor_smart_AUDIT_20260827.md"
OUT_LOG     = "/opt/mia/workspace/clientes/px3lab/imports/conafor_smart_AUDIT_20260827.log"

log_lines = []

def log(msg):
    ts = datetime.now().strftime("%H:%M:%S")
    line = f"[{ts}] {msg}"
    print(line, flush=True)
    log_lines.append(line)

def normalize(name: str) -> str:
    """lowercase sem acentos para comparacao."""
    nfkd = unicodedata.normalize("NFKD", name or "")
    return "".join(c for c in nfkd if not unicodedata.combining(c)).lower().strip()

def api_get_contact(contact_id: str) -> dict:
    url = f"{BASE_URL}/contacts/{contact_id}"
    result = subprocess.run(
        [
            "curl", "-s", "--max-time", "20",
            "-H", f"Authorization: Bearer {TOKEN}",
            "-H", "Version: 2021-07-28",
            "-H", "Accept: application/json",
            "-H", "User-Agent: Mozilla/5.0 (compatible; Linkia-CRM/1.0)",
            url,
        ],
        capture_output=True, text=True
    )
    if result.returncode != 0:
        log(f"  curl error para {contact_id}: {result.stderr[:100]}")
        return {}
    try:
        data = json.loads(result.stdout)
        return data
    except Exception as e:
        log(f"  JSON parse error para {contact_id}: {e} | raw: {result.stdout[:100]}")
        return {}

# 1. Ler CSV
log("Lendo CSV...")
csv_rows = []
with open(CSV_PATH, newline="", encoding="utf-8") as f:
    reader = csv.DictReader(f)
    for row in reader:
        csv_rows.append({
            "first_name": row["First Name"].strip(),
            "phone":      row["Phone"].strip(),
        })
log(f"  {len(csv_rows)} linhas de dados lidas do CSV.")

# 2. Parsear log
log("Parseando log de importacao...")
CREATE_RE = re.compile(
    r"\[(\d{3})/120\]\s+(.+?)\s+\|\s+CREATE\s+contact=(\S+)\s+->\s+new_opp=(\S+)\s+OK"
)
log_entries = []
with open(LOG_PATH, encoding="utf-8") as f:
    for line in f:
        m = CREATE_RE.search(line)
        if m:
            log_entries.append({
                "seq":        int(m.group(1)),
                "name_log":   m.group(2).strip(),
                "contact_id": m.group(3),
                "opp_id":     m.group(4),
            })
log(f"  {len(log_entries)} entradas CREATE parseadas do log.")

# 3. Cruzar CSV x log por indice
if len(csv_rows) != 120 or len(log_entries) != 120:
    log(f"ERRO: tamanhos divergem csv={len(csv_rows)} log={len(log_entries)}")
    sys.exit(1)

entries = []
for i, (csv_row, log_entry) in enumerate(zip(csv_rows, log_entries)):
    entries.append({
        "idx":        i + 1,
        "name_csv":   csv_row["first_name"],
        "phone_csv":  csv_row["phone"],
        "contact_id": log_entry["contact_id"],
        "opp_id":     log_entry["opp_id"],
        "name_log":   log_entry["name_log"],
    })

# 4. Identificar phones duplicados no CSV ANTES das chamadas API
phone_counter = Counter(r["phone_csv"] for r in entries)
dup_phones_csv = {ph: cnt for ph, cnt in phone_counter.items() if cnt > 1}
log(f"Phones duplicados no CSV: {dup_phones_csv}")

# 5. Consultar API para cada contato
log("Consultando API GHL para cada contato (120 calls)...")
results = []
DELAY = 0.8

for e in entries:
    cid  = e["contact_id"]
    data = api_get_contact(cid)
    contact = data.get("contact", {})

    first_name_api = (contact.get("firstName", "") or "").strip()
    last_name_api  = (contact.get("lastName",  "") or "").strip()
    phone_api      = (contact.get("phone",     "") or "").strip()
    email_api      = (contact.get("email",     "") or "").strip()
    full_name_api  = f"{first_name_api} {last_name_api}".strip()

    # Comparacao: primeiro token do nome do CSV vs firstName do contato (case-insensitive, sem acento)
    name_csv_norm  = normalize(e["name_csv"].split()[0])
    first_api_norm = normalize(first_name_api.split()[0]) if first_name_api else ""

    match = bool(first_name_api) and (name_csv_norm == first_api_norm)

    row = {
        **e,
        "first_name_api": first_name_api,
        "last_name_api":  last_name_api,
        "full_name_api":  full_name_api,
        "phone_api":      phone_api,
        "email_api":      email_api,
        "match":          match,
    }
    results.append(row)

    status = "OK" if match else "MISMATCH"
    log(f"  [{e['idx']:03d}] csv={e['name_csv']!r} phone_csv={e['phone_csv']} | api_name={first_name_api!r} phone_api={phone_api} | cid={cid} | {status}")
    time.sleep(DELAY)

# 6. Separar match / mismatch
matches    = [r for r in results if r["match"]]
mismatches = [r for r in results if not r["match"]]

total   = len(results)
n_match = len(matches)
n_miss  = len(mismatches)
pct_err = (n_miss / total * 100) if total else 0

log(f"\nSUMARIO: total={total} | match={n_match} | mismatch={n_miss} | err={pct_err:.1f}%")

# 7. Analise de padroes
cid_counter   = Counter(r["contact_id"] for r in results)
dup_contacts  = {cid: cnt for cid, cnt in cid_counter.items() if cnt > 1}
log(f"Contact IDs duplicados no log: {dup_contacts}")

mismatch_phones_dup   = [r for r in mismatches if r["phone_csv"] in dup_phones_csv]
mismatch_contacts_dup = [r for r in mismatches if r["contact_id"] in dup_contacts]

# Analisar se phone_csv == phone_api (mesmo numero, nome diferente — contato existente com numero portado)
mismatch_same_phone = [r for r in mismatches if r["phone_csv"] and r["phone_api"] and
                       normalize(r["phone_csv"]) == normalize(r["phone_api"])]
mismatch_diff_phone = [r for r in mismatches if r not in mismatch_same_phone]

log(f"Mismatches com mesmo phone (contato antigo com nome diferente): {len(mismatch_same_phone)}")
log(f"Mismatches com phone diferente (possivelmente contact reutilizado por dedup de opp anterior): {len(mismatch_diff_phone)}")

# 8. Montar tabela de mismatches
mismatch_table_rows = []
for i, r in enumerate(mismatches, 1):
    note = ""
    if r["phone_csv"] in dup_phones_csv:
        note = " [phone dup no CSV]"
    elif r["contact_id"] in dup_contacts:
        note = " [contact reutilizado]"
    elif r["phone_csv"] == r["phone_api"]:
        note = " [phone portado/reciclado]"

    mismatch_table_rows.append(
        f"| {i} | {r['name_csv']}{note} | {r['phone_csv']} | "
        f"{r['full_name_api'] or '(vazio)'} | "
        f"{r['phone_api'] or '(vazio)'} | "
        f"{r['email_api'] or '(vazio)'} | "
        f"{r['opp_id']} |"
    )

mismatch_table = "\n".join(mismatch_table_rows) if mismatch_table_rows else "_Nenhum mismatch_"

# 9. Padrao observado
pattern_parts = []

if dup_phones_csv:
    dup_list = "; ".join(f"{ph} (x{cnt})" for ph, cnt in dup_phones_csv.items())
    pattern_parts.append(
        f"**Phones duplicados no CSV ({len(dup_phones_csv)} ocorrencias):** o mesmo numero aparece em duas linhas com nomes diferentes ({dup_list}). "
        "Quando o script deduplicou por phone, a segunda linha recebeu o contato ja existente (criado pela primeira linha), causando mismatch direto."
    )

if len(mismatch_same_phone) > 0 and not dup_phones_csv:
    pattern_parts.append(
        f"**Phones reciclados/portados ({len(mismatch_same_phone)} casos):** o phone do CSV bate com o phone do contato vinculado, mas o nome e diferente. "
        "Isso indica que o numero foi reutilizado por outra pessoa no Linkia — o contato existia antes, com outro dono."
    )
elif len(mismatch_same_phone) > 0:
    pattern_parts.append(
        f"Alem dos phones duplicados no CSV, ha {len(mismatch_same_phone)} caso(s) onde o phone do CSV coincide com o phone do contato vinculado mas o nome diverge — numero portado ou reciclado."
    )

if dup_contacts:
    cid_list = "; ".join(f"{cid} (x{cnt})" for cid, cnt in dup_contacts.items())
    pattern_parts.append(
        f"**Contact IDs reusados ({len(dup_contacts)} IDs):** um mesmo contato esta apontado para mais de uma opportunity ({cid_list}). "
        "Isso e direto reflexo dos phones duplicados no CSV."
    )

if not pattern_parts:
    pattern_parts.append(
        "Nao foi identificado padrao unico. Os mismatches parecem ser de phones "
        "portados entre pessoas ou cadastros antigos do Linkia com numero reciclado."
    )

padrao_text = "\n\n".join(pattern_parts)

# 10. Escrever MD
md_content = f"""# Auditoria mismatch nome - Conafor Smart - 27/08/2026

## Sumario
- Total de opportunities criadas: {total}
- Match (nome do CSV = nome do contato): {n_match}
- Mismatch (nome do CSV != nome do contato): {n_miss}
- % de erro: {pct_err:.1f}%

## Lista de mismatches
| # | Nome CSV | Phone CSV | Contato vinculado (nome) | Phone contato | Email contato | Opportunity ID |
|---|----------|-----------|--------------------------|---------------|---------------|----------------|
{mismatch_table}

## Padrao observado
{padrao_text}

## Detalhes tecnicos adicionais
- Phones duplicados no CSV: `{json.dumps(dup_phones_csv, ensure_ascii=False)}`
- Contact IDs reusados (uma opp por contato): `{json.dumps(dup_contacts, ensure_ascii=False)}`
- Mismatches com phone dup no CSV: {len(mismatch_phones_dup)} de {n_miss}
- Mismatches com contact reutilizado: {len(mismatch_contacts_dup)} de {n_miss}
- Mismatches onde phone_csv == phone_api (numero portado): {len(mismatch_same_phone)} de {n_miss}
- Mismatches onde phones sao diferentes: {len(mismatch_diff_phone)} de {n_miss}

## Opcoes de fix propostas (Renato decide)
1. **Criar contatos NOVOS** para cada mismatch com o nome do CSV - apontar a opp pro contato novo - o contato antigo permanece (pode ter outros dados). Risco: duplicidade de contato se o phone realmente pertence as duas pessoas.
2. **Renomear a opportunity** para bater com o contato existente - "Debora - CONAFOR SMART" vira "Lucas - CONAFOR SMART". Risco: perde a informacao de que quem queria ir na palestra era Debora.
3. **Deletar as opportunities com mismatch e reimportar** so essas linhas criando contato novo (sem dedupe por phone). Risco: gera duplicidade se o phone realmente pertence as duas pessoas hoje.
4. **Descartar a importacao inteira** e refazer com dedupe mais rigoroso (so reusar se nome + phone baterem). Risco: perde o que ja esta classificado nas opps corretas.

## Recomendacao da Amanda
A causa raiz e definitiva: o dedupe usou **somente o phone como chave**, e o CSV tinha {len(dup_phones_csv)} phone(s) repetido(s) em linhas com nomes diferentes - para esses casos o segundo nome foi vinculado ao contato da primeira linha. Para os demais mismatches, o phone ja existia no Linkia com outra pessoa (numero portado ou reciclado), e o dedupe trouxe aquele contato antigo.

A opcao mais segura e a **3 (reimportar so os {n_miss} mismatches sem dedupe por phone)**: cria contato novo com os dados exatos do CSV, aponta a opp para o contato correto, e preserva o contato antigo sem tocar. Antes de executar, vale olhar manualmente os casos marcados como "[phone dup no CSV]" na tabela acima - se as duas pessoas (ex: casal) realmente usam o mesmo numero, a opcao 2 pode ser mais adequada para esses especificos.

Na proxima importacao: dedupe deve validar nome + phone juntos (nao so phone), ou criar contato novo sempre que o firstName nao bater.
"""

with open(OUT_MD, "w", encoding="utf-8") as f:
    f.write(md_content)

log(f"Relatorio MD salvo em {OUT_MD}")

# 11. Salvar log completo
with open(OUT_LOG, "w", encoding="utf-8") as f:
    f.write("\n".join(log_lines))

log(f"Log completo salvo em {OUT_LOG}")
log("Auditoria concluida.")

# Output resumo para stdout
print(f"\n{'='*60}")
print(f"RESUMO FINAL")
print(f"Total: {total} | Match: {n_match} | Mismatch: {n_miss} | Erro: {pct_err:.1f}%")
if mismatches:
    print(f"\nLista de mismatches:")
    for r in mismatches:
        print(f"  #{r['idx']:03d} CSV={r['name_csv']!r} | API={r['full_name_api']!r} | opp={r['opp_id']}")
print(f"{'='*60}")
