#!/usr/bin/env python3
"""
ETAPA 2 — Completa os lotes gelado restantes COM filtro de waitlist.
- gelado-lote1: retoma do checkpoint (index 250-999, já tem 750 feitos)
- gelado-lote2: index 1000-1999 (1000 emails)
- gelado-lote3: index 2000-2330 (331 emails)

Pula contatos com waitlist-photorf2-v2.
Timeout reduzido. Busca de waitlist em uma única request (sabemos que são <100).
"""

import csv
import json
import time
import sys
from pathlib import Path
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
import requests

BASE = "https://services.leadconnectorhq.com"
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/126.0 Safari/537.36"
WORK_DIR = Path("/opt/mia/workspace/clientes/px3lab/mapeamento_leads_lancamento_1007")
CHECKPOINT_FILE = Path("/tmp/correcao_px3_checkpoint.json")
CHECKPOINT_INTERVAL = 250
DELAY = 0.20

# Credenciais
_env = {}
with open("/opt/mia/config/linkia_px3.env") as f:
    for line in f:
        line = line.strip()
        if "=" in line and not line.startswith("#"):
            k, v = line.split("=", 1)
            _env[k.strip()] = v.strip().strip('"').strip("'")

TOKEN = _env.get("LINKIA_PX3_TOKEN", "")
LOCATION_ID = _env.get("LINKIA_PX3_LOCATION_ID", "")

if not TOKEN or not LOCATION_ID:
    print("ERRO: TOKEN ou LOCATION_ID nao encontrado")
    sys.exit(1)

print(f"TOKEN: ...{TOKEN[-8:]}")
print(f"LOCATION_ID: {LOCATION_ID}")

# Session com retry conservador
s = requests.Session()
s.headers.update({
    "Authorization": f"Bearer {TOKEN}",
    "Version": "2021-07-28",
    "Content-Type": "application/json",
    "User-Agent": UA,
})
retry = Retry(
    total=3,
    backoff_factor=1,
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=["GET", "POST"],
)
s.mount("https://", HTTPAdapter(max_retries=retry))


def load_checkpoint() -> dict:
    if CHECKPOINT_FILE.exists():
        with open(CHECKPOINT_FILE) as f:
            return json.load(f)
    return {}


def save_checkpoint(cp: dict):
    with open(CHECKPOINT_FILE, "w") as f:
        json.dump(cp, f, indent=2)


def get_waitlist_ids_and_emails() -> tuple[set, set]:
    """Busca todos os contatos com waitlist-photorf2-v2 em uma única request (pageLimit=100)."""
    WAITLIST_TAG = "waitlist-photorf2-v2"
    ids = set()
    emails = set()
    try:
        r = s.post(
            f"{BASE}/contacts/search",
            json={
                "locationId": LOCATION_ID,
                "filters": [{"field": "tags", "operator": "contains", "value": WAITLIST_TAG}],
                "page": 1,
                "pageLimit": 100,
            },
            timeout=20,
        )
        if r.status_code == 200:
            data = r.json()
            contacts = data.get("contacts", [])
            total = data.get("total", 0)
            for c in contacts:
                if c.get("id"):
                    ids.add(c["id"])
                if c.get("email"):
                    emails.add(c["email"].strip().lower())
            print(f"  Waitlist: {len(contacts)} retornados de {total} total")
            # Se houver mais de 100, paginar
            if total > 100:
                page = 2
                while len(ids) < total:
                    time.sleep(DELAY)
                    r2 = s.post(
                        f"{BASE}/contacts/search",
                        json={
                            "locationId": LOCATION_ID,
                            "filters": [{"field": "tags", "operator": "contains", "value": WAITLIST_TAG}],
                            "page": page,
                            "pageLimit": 100,
                        },
                        timeout=20,
                    )
                    if r2.status_code != 200:
                        print(f"  [waitlist pag {page}] status={r2.status_code}")
                        break
                    d2 = r2.json()
                    batch = d2.get("contacts", [])
                    if not batch:
                        break
                    for c in batch:
                        if c.get("id"):
                            ids.add(c["id"])
                        if c.get("email"):
                            emails.add(c["email"].strip().lower())
                    page += 1
        else:
            print(f"  [waitlist] status={r.status_code} body={r.text[:120]}")
    except Exception as e:
        print(f"  [waitlist EXCEPTION]: {e}")
    return ids, emails


def lookup_contact(email: str) -> dict | None:
    try:
        r = s.get(
            f"{BASE}/contacts/",
            params={"locationId": LOCATION_ID, "query": email, "limit": 5},
            timeout=15,
        )
        if r.status_code == 200:
            contacts = r.json().get("contacts", [])
            for c in contacts:
                if (c.get("email") or "").strip().lower() == email.lower():
                    return c
            if len(contacts) == 1:
                return contacts[0]
        return None
    except Exception as e:
        print(f"  [lookup EXCEPTION] {email}: {e}")
        return None


def apply_tag(contact_id: str, tag: str) -> tuple[bool, int]:
    try:
        r = s.post(
            f"{BASE}/contacts/{contact_id}/tags",
            json={"tags": [tag]},
            timeout=15,
        )
        return r.status_code in (200, 201), r.status_code
    except Exception as e:
        print(f"  [apply_tag EXCEPTION] id={contact_id} tag={tag}: {e}")
        return False, 0


def load_csv_emails_slice(path: Path, start: int, end: int) -> list[str]:
    emails = []
    with open(path, newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for i, row in enumerate(reader):
            if i < start:
                continue
            if i >= end:
                break
            email = (row.get("email") or "").strip().lower()
            if email:
                emails.append(email)
    return emails


def process_lote(batch_name: str, tag: str, emails: list[str], cp: dict, waitlist_ids: set, waitlist_emails: set) -> dict:
    if "etapa2" not in cp:
        cp["etapa2"] = {}
    if batch_name not in cp["etapa2"]:
        cp["etapa2"][batch_name] = {
            "sucesso": [],
            "pulados_waitlist": [],
            "nao_encontrado": [],
            "falha_tag": [],
        }

    batch_cp = cp["etapa2"][batch_name]
    ja_feitos = set(
        batch_cp["sucesso"] +
        batch_cp["pulados_waitlist"] +
        batch_cp["nao_encontrado"] +
        [x["email"] for x in batch_cp["falha_tag"]]
    )

    sucesso = len(batch_cp["sucesso"])
    pulados = len(batch_cp["pulados_waitlist"])
    nao_enc = len(batch_cp["nao_encontrado"])
    falhas = len(batch_cp["falha_tag"])
    ops = 0

    print(f"\n--- {batch_name} ({len(emails)} emails, já feitos: {len(ja_feitos)}) ---")

    for i, email in enumerate(emails, 1):
        if email in ja_feitos:
            continue

        # Verificar waitlist por email
        if email in waitlist_emails:
            batch_cp["pulados_waitlist"].append(email)
            pulados += 1
            ja_feitos.add(email)
            print(f"  [PULADO waitlist] {email}")
            continue

        # Lookup contato
        contact = lookup_contact(email)
        time.sleep(DELAY)

        if not contact:
            batch_cp["nao_encontrado"].append(email)
            nao_enc += 1
            ja_feitos.add(email)
            continue

        cid = contact.get("id")

        # Verificar waitlist por ID
        if cid in waitlist_ids:
            batch_cp["pulados_waitlist"].append(email)
            pulados += 1
            ja_feitos.add(email)
            print(f"  [PULADO waitlist por id] {email} id={cid}")
            continue

        # Aplicar tag
        ok, status = apply_tag(cid, tag)
        time.sleep(DELAY)
        ops += 1

        if ok:
            sucesso += 1
            batch_cp["sucesso"].append(email)
        else:
            falhas += 1
            batch_cp["falha_tag"].append({"email": email, "id": cid, "status": status})
            print(f"  [FALHA TAG] {email} id={cid} status={status}")

        ja_feitos.add(email)

        if ops % 50 == 0:
            pct = (i / len(emails)) * 100
            print(f"  [{i}/{len(emails)} {pct:.0f}%] sucesso={sucesso} pulados={pulados} nao_enc={nao_enc} falhas={falhas}")

        if ops % CHECKPOINT_INTERVAL == 0:
            save_checkpoint(cp)
            print(f"  [CHECKPOINT {ops}]")

    save_checkpoint(cp)
    print(f"\n  {batch_name} CONCLUIDO: aplicados={sucesso} pulados={pulados} nao_enc={nao_enc} falhas={falhas}")
    return {"sucesso": sucesso, "pulados_waitlist": pulados, "nao_encontrado": nao_enc, "falhas": falhas}


def contar_tags_finais() -> dict:
    print("\n" + "=" * 70)
    print("CONTAGEM FINAL DAS TAGS NO CRM")
    print("=" * 70)

    tags = [
        "lancamento-cloudphotorf2-quente",
        "lancamento-cloudphotorf2-morno",
        "lancamento-cloudphotorf2-frio",
        "lancamento-cloudphotorf2-gelado-lote1",
        "lancamento-cloudphotorf2-gelado-lote2",
        "lancamento-cloudphotorf2-gelado-lote3",
    ]

    contagens = {}
    for tag in tags:
        try:
            r = s.post(
                f"{BASE}/contacts/search",
                json={
                    "locationId": LOCATION_ID,
                    "filters": [{"field": "tags", "operator": "contains", "value": tag}],
                    "page": 1,
                    "pageLimit": 1,
                },
                timeout=15,
            )
            if r.status_code == 200:
                total = r.json().get("total", 0)
                contagens[tag] = total
                print(f"  {tag}: {total}")
            else:
                print(f"  {tag}: ERRO {r.status_code}")
                contagens[tag] = -1
        except Exception as e:
            print(f"  {tag}: EXCEPTION {e}")
            contagens[tag] = -1
        time.sleep(DELAY)

    return contagens


def main():
    t0 = time.time()
    print("=" * 70)
    print("ETAPA 2 — COMPLETAR GELADOS COM FILTRO DE WAITLIST")
    print("=" * 70)

    cp = load_checkpoint()

    # Puxar lista de waitlist (única request rápida)
    print("\nCarregando lista de waitlist-photorf2-v2...")
    waitlist_ids, waitlist_emails = get_waitlist_ids_and_emails()
    print(f"  IDs na waitlist: {len(waitlist_ids)}")
    print(f"  Emails na waitlist: {len(waitlist_emails)}")

    gelado_csv = WORK_DIR / "leads_gelado.csv"

    # gelado-lote1 residual (index 250-999): já tem 750 no checkpoint, pular
    # gelado-lote2: index 1000-1999
    # gelado-lote3: index 2000-9999
    lotes = [
        ("gelado-lote1", "lancamento-cloudphotorf2-gelado-lote1", 250, 1000),
        ("gelado-lote2", "lancamento-cloudphotorf2-gelado-lote2", 1000, 2000),
        ("gelado-lote3", "lancamento-cloudphotorf2-gelado-lote3", 2000, 9999),
    ]

    resultados = {}
    for batch_name, tag, csv_start, csv_end in lotes:
        emails = load_csv_emails_slice(gelado_csv, csv_start, csv_end)
        res = process_lote(batch_name, tag, emails, cp, waitlist_ids, waitlist_emails)
        resultados[batch_name] = res

    # Contagem final
    contagens = contar_tags_finais()

    t1 = time.time()
    elapsed = t1 - t0
    print(f"\nTempo total: {int(elapsed//60)}m {int(elapsed%60)}s")

    print("\n" + "=" * 70)
    print("RELATÓRIO FINAL — ETAPA 2")
    print("=" * 70)
    for lote, stats in resultados.items():
        print(f"  {lote}: aplicados={stats['sucesso']} pulados_waitlist={stats['pulados_waitlist']} nao_enc={stats['nao_encontrado']} falhas={stats['falhas']}")

    print("\nCONTAGEM FINAL NO CRM:")
    for tag, n in contagens.items():
        print(f"  {tag}: {n}")

    return resultados, contagens


if __name__ == "__main__":
    main()
