import csv
import requests
import time
from datetime import datetime

# ====================================================
# Optional tqdm progress bar
# ====================================================
try:
    from tqdm import tqdm
    HAS_TQDM = True
except:
    HAS_TQDM = False

# ====================================================
# CONFIG
# ====================================================
API_URL = "https://inp.api-us1.com/api/3"
API_KEY = "0493c4d2d33662a670c7b347077c74dce411fe0261fb00affa7e8cdd34041ec16861355a"   # <<< replace me!

CSV_FILE = "Contacts_Matched_test.csv"
FAILED_LOG_FILE = "failed_updates.csv"

RETRY_STATUS = {429, 500, 502, 503, 504}
MAX_RETRIES = 5

EMAIL_COL = "Email"

CUSTOM_FIELD_IDS = {
    "id": 17, "cancel date": 79, "newmbr date": 86, "paid thru": 264,
    "co id": 16, "cert status": 183, "pcl status": 287,
    "pcl completion date": 289, "pf1 status": 291,
    "pf1 completion date": 292, "pf2 status": 293,
    "pf2 completion date": 294, "intro to accounting status": 295,
    "intro to accounting completion date": 296, "state province": 4,
    "c p a   certification": 182, "last  act": 278, "french": 7,
    "last  infoline": 276, "last conf": 277,
    "Processed member type": 18, "new lead status": 2,
    "New DO ID": 20, "Number of Passed Courses": 297
}

HEADERS = {
    "Api-Token": API_KEY,
    "Content-Type": "application/json",
}

# ====================================================
# SAFE REQUEST WRAPPER
# ====================================================
def safe_request(method, url, **kwargs):
    for attempt in range(MAX_RETRIES):
        try:
            r = requests.request(method, url, headers=HEADERS, timeout=20, **kwargs)
        except:
            time.sleep(1)
            continue

        if r.status_code in RETRY_STATUS:
            time.sleep(1.3 * (attempt + 1))
            continue

        return r
    return r

# ====================================================
# HELPERS
# ====================================================
def _safe_date(v):
    if not v or not v.strip():
        return ""
    s = v.strip()
    for fmt in ("%Y-%m-%d", "%Y/%m/%d", "%m/%d/%Y", "%d/%m/%Y"):
        try:
            return datetime.strptime(s, fmt).strftime("%Y-%m-%d")
        except:
            pass
    return s

# ====================================================
# CONTACT LOOKUP + UPDATE FUNCTIONS
# ====================================================
contact_id_cache = {}

def fetch_contact_id(email):
    r = safe_request("GET", f"{API_URL}/contacts", params={"email": email})
    if r.status_code != 200:
        return None
    contacts = r.json().get("contacts", [])
    return contacts[0]["id"] if contacts else None

def update_contact(cid, core, custom):
    payload = {"contact": core}
    if custom:
        payload["contact"]["fieldValues"] = custom
    return safe_request("PUT", f"{API_URL}/contacts/{cid}", json=payload)

# ====================================================
# MAIN
# ====================================================
if __name__ == "__main__":

    print("📥 Loading CSV...")
    with open(CSV_FILE, newline='', encoding="utf-8-sig") as f:
        rows = list(csv.DictReader(f))

    total = len(rows)
    print(f"Found {total:,} contacts.\n")

    # Build unique email set
    all_emails = {row.get(EMAIL_COL, "").strip().lower() for row in rows if row.get(EMAIL_COL)}

    print(f"Unique emails: {len(all_emails):,}\n")

    # ------------------------------------------------
    # Phase 1: Prefetch contact IDs
    # ------------------------------------------------
    print("📡 Fetching contact IDs from ActiveCampaign...")
    iterator = tqdm(all_emails) if HAS_TQDM else all_emails

    for email in iterator:
        contact_id_cache[email] = fetch_contact_id(email)

    # ------------------------------------------------
    # Phase 2: Process updates (FAST)
    # ------------------------------------------------
    print("\n🛠 Updating contacts...")
    failures = []
    ok = skip = fail = 0

    iterator = tqdm(range(total)) if HAS_TQDM else range(total)

    for idx in iterator:
        row = rows[idx]

        # Throttle every 100 contacts
        if idx > 0 and idx % 100 == 0:
            time.sleep(1)

        email = (row.get(EMAIL_COL, "") or "").strip().lower()
        if not email:
            skip += 1
            continue

        cid = contact_id_cache.get(email)
        if not cid:
            skip += 1
            continue

        # Prepare update
        core = {"email": email}
        custom = []

        for col, fid in CUSTOM_FIELD_IDS.items():
            val = (row.get(col, "") or "").strip()
            if val:
                if col.lower() == "paid thru":
                    val = _safe_date(val)
                custom.append({"field": str(fid), "value": val})

        # Send update
        r = update_contact(cid, core, custom)
        if r.status_code not in (200, 201):
            failures.append({"email": email, "error": r.status_code, **row})
            fail += 1
        else:
            ok += 1

    # ------------------------------------------------
    # Phase 3: Save failures
    # ------------------------------------------------
    if failures:
        with open(FAILED_LOG_FILE, "w", newline='', encoding="utf-8-sig") as f:
            writer = csv.DictWriter(f, fieldnames=failures[0].keys())
            writer.writeheader()
            writer.writerows(failures)
        print(f"\n❌ Logged {len(failures)} failures → {FAILED_LOG_FILE}")

    print("\n==============================")
    print(f"✅ Success: {ok}")
    print(f"⚠️ Skipped: {skip}")
    print(f"❌ Failed: {fail}")
    print("==============================")
    print("🎉 Completed FAST without updating accounts!")