"""
Merge and delete duplicate contacts in Barbara Bardach's main Contacts folder.
Reads analysis from bardach_main_dupes_analysis.json, merges data from delete
contacts into keepers, then deletes the duplicates.
"""

import json
import subprocess
import sys
import urllib.parse

TENANT_ID = "dd4a82e8-85a3-44ac-8800-07945ab4d95f"
CLIENT_ID = "fabb3421-8b34-484b-bc17-e46de9703418"
CLIENT_SECRET = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"
USER_EMAIL = "barbara@bardach.net"
GRAPH_BASE = f"https://graph.microsoft.com/v1.0/users/{USER_EMAIL}/contacts"

ANALYSIS_FILE = r"D:\ClaudeTools\temp\bardach_main_dupes_analysis.json"


def get_token():
    url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token"
    data = (
        f"grant_type=client_credentials"
        f"&client_id={CLIENT_ID}"
        f"&client_secret={urllib.parse.quote(CLIENT_SECRET, safe='')}"
        f"&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default"
    )
    result = subprocess.run(
        ["curl", "-s", "-X", "POST", url,
         "-H", "Content-Type: application/x-www-form-urlencoded",
         "-d", data],
        capture_output=True, text=True
    )
    resp = json.loads(result.stdout)
    if "access_token" not in resp:
        print(f"[ERROR] Failed to get token: {resp}")
        sys.exit(1)
    return resp["access_token"]


def get_contact(token, contact_id):
    """GET full contact details from Graph API."""
    url = f"{GRAPH_BASE}/{contact_id}"
    select = "$select=displayName,givenName,surname,emailAddresses,homePhones,businessPhones,personalNotes,companyName,jobTitle,homeAddress,businessAddress,birthday"
    result = subprocess.run(
        ["curl", "-s", "-X", "GET", f"{url}?{select}",
         "-H", f"Authorization: Bearer {token}",
         "-H", "Content-Type: application/json"],
        capture_output=True, text=True
    )
    return json.loads(result.stdout)


def patch_contact(token, contact_id, payload):
    """PATCH a contact with the given payload."""
    payload_json = json.dumps(payload)
    url = f"{GRAPH_BASE}/{contact_id}"
    result = subprocess.run(
        ["curl", "-s", "-X", "PATCH", url,
         "-H", f"Authorization: Bearer {token}",
         "-H", "Content-Type: application/json",
         "-d", payload_json,
         "-w", "\n%{http_code}"],
        capture_output=True, text=True
    )
    lines = result.stdout.strip().rsplit("\n", 1)
    status = int(lines[-1]) if len(lines) > 1 else 0
    return status, lines[0] if len(lines) > 1 else result.stdout


def delete_contact(token, contact_id):
    """DELETE a contact. Returns HTTP status code."""
    url = f"{GRAPH_BASE}/{contact_id}"
    result = subprocess.run(
        ["curl", "-s", "-X", "DELETE", url,
         "-H", f"Authorization: Bearer {token}",
         "-o", "/dev/null",
         "-w", "%{http_code}"],
        capture_output=True, text=True
    )
    return int(result.stdout.strip())


def emails_equal(a, b):
    """Case-insensitive email comparison."""
    return (a or "").lower().strip() == (b or "").lower().strip()


def has_address_data(addr):
    """Check if an address dict has any actual data."""
    if not addr or not isinstance(addr, dict):
        return False
    return any(v for v in addr.values() if v)


def build_merge_payload(keeper, delete_contact_data):
    """Compare keeper and delete contact, return PATCH payload for fields to merge."""
    payload = {}
    merge_notes = []

    # --- emailAddresses ---
    keeper_emails = keeper.get("emailAddresses") or []
    delete_emails = delete_contact_data.get("emailAddresses") or []
    keeper_addrs = {e.get("address", "").lower().strip() for e in keeper_emails if e.get("address", "").strip()}
    new_emails = []
    for e in delete_emails:
        addr = (e.get("address") or "").strip()
        if addr and addr.lower() not in keeper_addrs:
            new_emails.append(e)
    if new_emails:
        # Filter out empty entries from keeper too
        clean_keeper = [e for e in keeper_emails if (e.get("address") or "").strip()]
        payload["emailAddresses"] = clean_keeper + new_emails
        merge_notes.append(f"emails: +{[e['address'] for e in new_emails]}")

    # --- homePhones ---
    keeper_hp = keeper.get("homePhones") or []
    delete_hp = delete_contact_data.get("homePhones") or []
    keeper_hp_set = {p.strip() for p in keeper_hp if p.strip()}
    new_hp = [p for p in delete_hp if p.strip() and p.strip() not in keeper_hp_set]
    if new_hp:
        payload["homePhones"] = list(keeper_hp) + new_hp
        merge_notes.append(f"homePhones: +{new_hp}")

    # --- businessPhones ---
    keeper_bp = keeper.get("businessPhones") or []
    delete_bp = delete_contact_data.get("businessPhones") or []
    keeper_bp_set = {p.strip() for p in keeper_bp if p.strip()}
    new_bp = [p for p in delete_bp if p.strip() and p.strip() not in keeper_bp_set]
    if new_bp:
        payload["businessPhones"] = list(keeper_bp) + new_bp
        merge_notes.append(f"businessPhones: +{new_bp}")

    # --- personalNotes ---
    keeper_notes = (keeper.get("personalNotes") or "").strip()
    delete_notes = (delete_contact_data.get("personalNotes") or "").strip()
    if delete_notes and not keeper_notes:
        payload["personalNotes"] = delete_notes
        merge_notes.append(f"personalNotes: set")
    elif delete_notes and keeper_notes and delete_notes.lower() != keeper_notes.lower():
        payload["personalNotes"] = keeper_notes + "\n" + delete_notes
        merge_notes.append(f"personalNotes: appended")

    # --- companyName ---
    keeper_co = (keeper.get("companyName") or "").strip()
    delete_co = (delete_contact_data.get("companyName") or "").strip()
    if delete_co and not keeper_co:
        payload["companyName"] = delete_co
        merge_notes.append(f"companyName: '{delete_co}'")

    # --- jobTitle ---
    keeper_jt = (keeper.get("jobTitle") or "").strip()
    delete_jt = (delete_contact_data.get("jobTitle") or "").strip()
    if delete_jt and not keeper_jt:
        payload["jobTitle"] = delete_jt
        merge_notes.append(f"jobTitle: '{delete_jt}'")

    # --- homeAddress ---
    if has_address_data(delete_contact_data.get("homeAddress")) and not has_address_data(keeper.get("homeAddress")):
        payload["homeAddress"] = delete_contact_data["homeAddress"]
        merge_notes.append("homeAddress: set")

    # --- businessAddress ---
    if has_address_data(delete_contact_data.get("businessAddress")) and not has_address_data(keeper.get("businessAddress")):
        payload["businessAddress"] = delete_contact_data["businessAddress"]
        merge_notes.append("businessAddress: set")

    # --- birthday ---
    keeper_bday = keeper.get("birthday")
    delete_bday = delete_contact_data.get("birthday")
    if delete_bday and not keeper_bday:
        payload["birthday"] = delete_bday
        merge_notes.append(f"birthday: '{delete_bday}'")

    return payload, merge_notes


def main():
    with open(ANALYSIS_FILE, "r", encoding="utf-8") as f:
        analysis = json.load(f)

    groups = analysis["groups"]
    print(f"Loaded {len(groups)} duplicate groups to process.\n")

    token = get_token()
    print("[OK] Got access token.\n")

    success_count = 0
    error_count = 0

    for i, group in enumerate(groups, 1):
        name = group["name"]
        keeper_id = group["keeper_id"]
        delete_ids = group["delete_ids"]

        print(f"--- Group {i}/{len(groups)}: {name} ---")

        # GET keeper details
        keeper = get_contact(token, keeper_id)
        if "error" in keeper:
            print(f"  [ERROR] Failed to GET keeper: {keeper['error']}")
            error_count += 1
            continue

        for did in delete_ids:
            # GET delete contact details
            del_data = get_contact(token, did)
            if "error" in del_data:
                print(f"  [ERROR] Failed to GET delete contact: {del_data['error']}")
                error_count += 1
                continue

            # Build merge payload
            payload, merge_notes = build_merge_payload(keeper, del_data)

            if payload:
                status, resp_body = patch_contact(token, keeper_id, payload)
                if 200 <= status < 300:
                    print(f"  [OK] PATCH keeper - merged: {', '.join(merge_notes)}")
                    # Update our local keeper data with the patched fields
                    keeper.update(payload)
                else:
                    print(f"  [ERROR] PATCH keeper failed (HTTP {status}): {resp_body[:200]}")
                    error_count += 1
                    continue
            else:
                print(f"  [INFO] No data to merge from duplicate.")

            # DELETE the duplicate
            del_status = delete_contact(token, did)
            if del_status == 204:
                print(f"  [OK] DELETE duplicate (HTTP 204)")
                success_count += 1
            else:
                print(f"  [ERROR] DELETE failed (HTTP {del_status})")
                error_count += 1

        print()

    print(f"=== DONE: {success_count} deleted successfully, {error_count} errors ===")


if __name__ == "__main__":
    main()
