#!/usr/bin/env python3
"""
FIX CONAFOR SMART - OPÇÃO 3
Aprovado por Renato em 27/08/2026

1. Preservar 3 opps corretas (Guilherme linha 93, Murilo linha 115, Rafael linha 120) + monique
2. Deletar 117 opps com mismatch
3. Reimportar 117 leads criando contato NOVO (sem dedupe por phone)
4. Validar: 100% dos nomes de opp batem com nome do contato

Executado por: Amanda (CRM Linkia / GHL)
"""

import json
import csv
import time
import requests
from datetime import datetime
from collections import Counter

# ============================================================
# CONFIGURAÇÃO
# ============================================================
# Credenciais PX3 em /opt/mia/config/linkia_px3.env
import os as _os


def _load_env_file(path):
    data = {}
    try:
        with open(path) as f:
            for line in f:
                line = line.strip()
                if not line or line.startswith("#") or "=" not in line:
                    continue
                k, v = line.split("=", 1)
                data[k.strip()] = v.strip().strip('"').strip("'")
    except FileNotFoundError:
        pass
    return data


_PX3_ENV = _load_env_file("/opt/mia/config/linkia_px3.env")
TOKEN = _os.environ.get("LINKIA_PX3_TOKEN") or _PX3_ENV.get("LINKIA_PX3_TOKEN", "")
LOCATION = (
    _os.environ.get("LINKIA_PX3_LOCATION_ID")
    or _PX3_ENV.get("LINKIA_PX3_LOCATION_ID", "")
)
if not TOKEN or not LOCATION:
    raise RuntimeError(
        "conafor_smart_opcao3_fix: credenciais PX3 nao encontradas. "
        "Verifique /opt/mia/config/linkia_px3.env."
    )
PIPELINE    = "3H0v6vq6oa7oOST4mKTV"   # CONAFOR SMART
STAGE       = "e6b37871-95a9-49ee-95db-027c9d301454"  # Novo Lead
BASE        = "https://services.leadconnectorhq.com"
CSV_PATH    = "/opt/mia-bot/docs/1787836331382479.csv"
SOURCE      = "Import CSV Renato 27/08/2026 (fix opção 3)"

LOG_PATH = f"/opt/mia/workspace/clientes/px3lab/imports/conafor_smart_OPCAO3_{datetime.now().strftime('%Y%m%d_%H%M')}.log"

# Opps a preservar (os 3 corretos + monique)
PRESERVE_OPP_IDS = {
    'rSdURlXSXspfQmZ2OENp',  # Rafael - correto
    'XPAok3KjZPJzoFXESUH8',  # Murilo - correto
    'qxfMs7ifkJt4GaDQCytz',  # Guilherme - correto
    'Nm8LhrX9L48bTXjTJFT5',  # Teste Re - CONAFOR (monique) - pré-existente
}

# Linhas do CSV a pular (já têm opp correta preservada)
# idx 0-based: 92 = Guilherme (+5517991312889), 114 = Murilo (+5514981141944), 119 = Rafael (+5514997386556)
SKIP_CSV_INDICES = {92, 114, 119}

# ============================================================
# SESSÃO HTTP
# ============================================================
session = requests.Session()
session.headers.update({
    "Authorization": f"Bearer {TOKEN}",
    "Version": "2021-07-28",
    "Content-Type": "application/json",
})

# ============================================================
# LOG
# ============================================================
log_lines = []

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

def save_log():
    with open(LOG_PATH, 'w', encoding='utf-8') as f:
        f.write('\n'.join(log_lines))
    print(f"\nLog salvo em: {LOG_PATH}")

# ============================================================
# HELPERS API
# ============================================================
def api_delete_opp(opp_id, retry=0):
    """DELETE /opportunities/{id} com backoff exponencial em 429"""
    url = f"{BASE}/opportunities/{opp_id}"
    r = session.delete(url)
    if r.status_code == 429:
        wait = 2 ** retry
        log(f"  429 rate limit, aguardando {wait}s...")
        time.sleep(wait)
        if retry < 4:
            return api_delete_opp(opp_id, retry + 1)
        return False, f"429 após {retry} retries"
    if r.status_code in (200, 204):
        return True, "OK"
    return False, f"{r.status_code}: {r.text[:100]}"

def api_create_contact(first_name, phone, tags=None, retry=0):
    """POST /contacts/ — cria contato NOVO sem dedupe"""
    url = f"{BASE}/contacts/"
    payload = {
        "locationId": LOCATION,
        "firstName": first_name,
        "phone": phone,
        "source": SOURCE,
    }
    if tags:
        payload["tags"] = tags
    r = session.post(url, json=payload)
    if r.status_code == 429:
        wait = 2 ** retry
        log(f"  429 rate limit criando contato, aguardando {wait}s...")
        time.sleep(wait)
        if retry < 4:
            return api_create_contact(first_name, phone, tags, retry + 1)
        return None, f"429 após {retry} retries"
    if r.status_code in (200, 201):
        data = r.json()
        contact = data.get('contact', data)
        return contact.get('id'), "OK"
    # Se der 400 com "Contact already exists" — capturar contactId do erro
    if r.status_code == 400:
        try:
            err_data = r.json()
            # GHL às vezes retorna o contactId existente no body do erro
            existing_id = err_data.get('meta', {}).get('contactId') or err_data.get('contactId')
            if existing_id:
                return existing_id, f"DUP-400-existingId={existing_id}"
        except Exception:
            pass
        return None, f"400: {r.text[:150]}"
    return None, f"{r.status_code}: {r.text[:150]}"

def api_create_opportunity(contact_id, opp_name, retry=0):
    """POST /opportunities/ — cria opp vinculada ao contato"""
    url = f"{BASE}/opportunities/"
    payload = {
        "pipelineId": PIPELINE,
        "locationId": LOCATION,
        "name": opp_name,
        "pipelineStageId": STAGE,
        "status": "open",
        "contactId": contact_id,
        "monetaryValue": 0,
    }
    r = session.post(url, json=payload)
    if r.status_code == 429:
        wait = 2 ** retry
        log(f"  429 rate limit criando opp, aguardando {wait}s...")
        time.sleep(wait)
        if retry < 4:
            return api_create_opportunity(contact_id, opp_name, retry + 1)
        return None, f"429 após {retry} retries"
    if r.status_code in (200, 201):
        data = r.json()
        opp = data.get('opportunity', data)
        return opp.get('id'), "OK"
    return None, f"{r.status_code}: {r.text[:150]}"

def api_get_contact(contact_id, retry=0):
    """GET /contacts/{id}"""
    url = f"{BASE}/contacts/{contact_id}"
    r = session.get(url)
    if r.status_code == 429:
        wait = 2 ** retry
        time.sleep(wait)
        if retry < 4:
            return api_get_contact(contact_id, retry + 1)
        return None
    if r.status_code == 200:
        return r.json().get('contact', {})
    return None

def api_fetch_all_opps():
    """Puxar todas as opps do pipeline CONAFOR SMART / Novo Lead"""
    all_opps = []
    url = f"{BASE}/opportunities/search?location_id={LOCATION}&pipeline_id={PIPELINE}&pipeline_stage_id={STAGE}&limit=100"
    page = 0
    while url:
        page += 1
        r = session.get(url)
        if r.status_code != 200:
            log(f"  ERRO ao buscar opps pag {page}: {r.status_code}")
            break
        data = r.json()
        opps = data.get('opportunities', [])
        all_opps.extend(opps)
        meta = data.get('meta', {})
        next_url = meta.get('nextPageUrl')
        if next_url and len(opps) == 100:
            url = next_url
            time.sleep(0.3)
        else:
            url = None
    return all_opps

# ============================================================
# MAIN
# ============================================================
def main():
    log("=" * 60)
    log("FIX CONAFOR SMART — OPÇÃO 3")
    log(f"Pipeline: {PIPELINE} | Stage: {STAGE}")
    log(f"Preservar: {PRESERVE_OPP_IDS}")
    log("=" * 60)

    # --------------------------------------------------------
    # FASE 0: Carregar lista de opps a deletar (já calculada)
    # --------------------------------------------------------
    log("\n--- FASE 0: Carregar lista de deletar ---")
    with open('/tmp/conafor_to_delete.json') as f:
        to_delete_ids = json.load(f)
    log(f"Opps a deletar: {len(to_delete_ids)}")
    if len(to_delete_ids) != 117:
        log(f"PARADO: esperado 117, mas temos {len(to_delete_ids)}. Verificar antes de continuar.")
        save_log()
        return

    # --------------------------------------------------------
    # FASE 0.5: Anotar opps preservadas
    # --------------------------------------------------------
    log("\n--- OPPS PRESERVADAS ---")
    log(f"  Rafael: rSdURlXSXspfQmZ2OENp")
    log(f"  Murilo: XPAok3KjZPJzoFXESUH8")
    log(f"  Guilherme: qxfMs7ifkJt4GaDQCytz")
    log(f"  Monique: Nm8LhrX9L48bTXjTJFT5")

    # --------------------------------------------------------
    # FASE 1: Deletar 117 opps
    # --------------------------------------------------------
    log("\n--- FASE 1: Deletando 117 opps ---")
    deleted_ok = 0
    deleted_fail = []

    for i, opp_id in enumerate(to_delete_ids, 1):
        success, msg = api_delete_opp(opp_id)
        if success:
            deleted_ok += 1
            log(f"  [{i:03d}/{len(to_delete_ids)}] DELETE {opp_id} OK")
        else:
            deleted_fail.append({'opp_id': opp_id, 'error': msg})
            log(f"  [{i:03d}/{len(to_delete_ids)}] DELETE {opp_id} FAIL: {msg}")
        time.sleep(0.3)

    log(f"\nDeletados OK: {deleted_ok}/{len(to_delete_ids)}")
    log(f"Deletados FAIL: {len(deleted_fail)}")

    if deleted_ok != 117:
        log(f"\nPARADO: Esperado 117 deletados, mas temos {deleted_ok}.")
        log(f"Falhas: {deleted_fail}")
        save_log()
        return

    # --------------------------------------------------------
    # FASE 2: Reimportar 117 leads (criar contato NOVO + opp)
    # --------------------------------------------------------
    log("\n--- FASE 2: Aguardando 5s antes de reimportar ---")
    time.sleep(5)

    log("--- FASE 2: Carregando CSV ---")
    csv_rows = []
    with open(CSV_PATH, 'r', encoding='utf-8') as f:
        reader = csv.DictReader(f)
        for row in reader:
            csv_rows.append({'firstName': row['First Name'].strip(), 'phone': row['Phone'].strip()})

    to_import = [(i, csv_rows[i]) for i in range(len(csv_rows)) if i not in SKIP_CSV_INDICES]
    log(f"Linhas a importar: {len(to_import)} (esperado: 117)")

    # Identificar phones duplicados (para taggear)
    phone_counts = Counter(r['phone'] for _, r in to_import)
    dup_phones = {p for p, c in phone_counts.items() if c > 1}
    log(f"Phones duplicados no lote de importação: {dup_phones}")

    log("\n--- FASE 2: Criando contatos e opportunities ---")
    created_contacts_ok = 0
    created_contacts_dup = 0
    created_opps_ok = 0
    created_opps_fail = []
    import_results = []

    for seq, (csv_idx, row) in enumerate(to_import, 1):
        first_name = row['firstName']
        phone = row['phone']
        opp_name = f"{first_name} - CONAFOR SMART"
        csv_line = csv_idx + 2  # +1 para 1-based, +1 para header

        # Tags: dup_phone se aplicável
        tags = ['dup_phone'] if phone in dup_phones else []

        # Criar contato NOVO
        contact_id, contact_msg = api_create_contact(first_name, phone, tags if tags else None)

        if contact_id is None:
            log(f"  [{seq:03d}/117] ERRO contato {first_name} (linha {csv_line}): {contact_msg}")
            import_results.append({
                'seq': seq, 'csv_idx': csv_idx, 'csv_line': csv_line,
                'firstName': first_name, 'phone': phone,
                'contact_id': None, 'contact_status': f'FAIL: {contact_msg}',
                'opp_id': None, 'opp_status': 'SKIP'
            })
            time.sleep(0.5)
            continue

        is_dup = 'DUP' in contact_msg
        if is_dup:
            created_contacts_dup += 1
        else:
            created_contacts_ok += 1

        time.sleep(0.3)

        # Criar opportunity vinculando ao contato
        opp_id, opp_msg = api_create_opportunity(contact_id, opp_name)
        if opp_id:
            created_opps_ok += 1
            log(f"  [{seq:03d}/117] {first_name} (linha {csv_line}) | contact={contact_id} ({contact_msg}) -> opp={opp_id} OK")
        else:
            created_opps_fail.append({'seq': seq, 'firstName': first_name, 'error': opp_msg})
            log(f"  [{seq:03d}/117] {first_name} (linha {csv_line}) | contact={contact_id} OK, OPP FAIL: {opp_msg}")

        import_results.append({
            'seq': seq, 'csv_idx': csv_idx, 'csv_line': csv_line,
            'firstName': first_name, 'phone': phone,
            'contact_id': contact_id, 'contact_status': contact_msg,
            'opp_id': opp_id, 'opp_status': opp_msg or 'OK'
        })
        time.sleep(0.3)

    log(f"\nContatos criados (novos): {created_contacts_ok}")
    log(f"Contatos duplicados (phone já existe): {created_contacts_dup}")
    log(f"Opps criadas: {created_opps_ok}")
    log(f"Opps com falha: {len(created_opps_fail)}")

    if created_opps_ok != 117:
        log(f"\nATENCAO: Esperado 117 opps, criadas {created_opps_ok}.")
        if created_opps_fail:
            log(f"Falhas: {created_opps_fail}")

    # --------------------------------------------------------
    # FASE 3: Validação
    # --------------------------------------------------------
    log("\n--- FASE 3: Aguardando 60s para indexação ---")
    time.sleep(60)

    log("--- FASE 3: Validando pipeline ---")
    all_opps = api_fetch_all_opps()
    log(f"Total de opps no pipeline CONAFOR SMART / Novo Lead: {len(all_opps)}")

    if len(all_opps) != 121:
        log(f"ATENCAO: Esperado 121 opps (120 + monique), mas há {len(all_opps)}")

    # Buscar dados dos contatos e comparar nomes
    contact_cache = {}
    match_count = 0
    mismatch_list = []

    for opp in all_opps:
        opp_id = opp.get('id', '')
        opp_name = opp.get('name', '')
        contact_id = opp.get('contactId', '')

        # extrair parte do nome antes de " - CONAFOR"
        opp_first = opp_name.split(' - CONAFOR')[0].strip() if ' - CONAFOR' in opp_name else opp_name

        if contact_id not in contact_cache:
            c = api_get_contact(contact_id)
            contact_cache[contact_id] = c or {}
            time.sleep(0.15)

        contact_first = contact_cache[contact_id].get('firstName', '').strip()
        contact_phone = contact_cache[contact_id].get('phone', '')

        if opp_id in PRESERVE_OPP_IDS and opp_id == 'Nm8LhrX9L48bTXjTJFT5':
            # monique — não validar nome
            log(f"  MONIQUE (preservada): opp={opp_id} name={opp_name} [skip validação]")
            match_count += 1
            continue

        is_match = opp_first.lower() == contact_first.lower()
        if is_match:
            match_count += 1
        else:
            mismatch_list.append({
                'opp_id': opp_id,
                'opp_name': opp_name,
                'opp_first': opp_first,
                'contact_first': contact_first,
                'contact_phone': contact_phone
            })

    log(f"\n=== RESULTADO DA VALIDAÇÃO ===")
    log(f"Total no pipeline: {len(all_opps)}")
    log(f"Match (nome opp = nome contato): {match_count}")
    log(f"Mismatch: {len(mismatch_list)}")

    if mismatch_list:
        log("\nMISMATCHES RESTANTES:")
        for m in mismatch_list:
            log(f"  opp={m['opp_id']} name={m['opp_name']} | contact firstName={m['contact_first']} phone={m['contact_phone']}")

    # --------------------------------------------------------
    # RESUMO FINAL
    # --------------------------------------------------------
    log("\n" + "=" * 60)
    log("RESUMO FINAL - OPÇÃO 3")
    log("=" * 60)
    log(f"Opps deletadas:          {deleted_ok}/117")
    log(f"Opps preservadas:        4 (Rafael, Murilo, Guilherme, Monique)")
    log(f"Novos contatos criados:  {created_contacts_ok} (+ {created_contacts_dup} com phone dup = reutilizou existente)")
    log(f"Opps recriadas:          {created_opps_ok}/117")
    log(f"Total final pipeline:    {len(all_opps)} (esperado: 121)")
    log(f"Validação 100% match:    {'SIM' if not mismatch_list else f'NÃO — {len(mismatch_list)} mismatches'}")

    # Contar pares dup no lote importado
    log(f"\nDuplicatas de phone no lote (intencional - opção 3):")
    for p in dup_phones:
        leads = [(r['firstName'], f"linha {i+2}") for i, r in to_import if r['phone'] == p]
        log(f"  {p}: {leads}")

    # Salvar resultados de importação
    results_path = LOG_PATH.replace('.log', '_import_results.json')
    with open(results_path, 'w', encoding='utf-8') as f:
        json.dump({
            'import_results': import_results,
            'mismatch_list': mismatch_list,
            'summary': {
                'deleted': deleted_ok,
                'contacts_new': created_contacts_ok,
                'contacts_dup': created_contacts_dup,
                'opps_created': created_opps_ok,
                'total_final': len(all_opps),
                'match_count': match_count,
                'mismatch_count': len(mismatch_list)
            }
        }, f, ensure_ascii=False, indent=2)
    log(f"\nDetalhes salvos em: {results_path}")

    save_log()
    log("FIM.")

if __name__ == '__main__':
    main()
