"""
Operation 2: Move unique contacts from Temp to Main Contacts folder (278 contacts)
Tries move endpoint first, falls back to copy+delete.
"""
import json
import subprocess
import time
import urllib.parse

TENANT_ID = "dd4a82e8-85a3-44ac-8800-07945ab4d95f"
CLIENT_ID = "fabb3421-8b34-484b-bc17-e46de9703418"
CLIENT_SECRET = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"
SCOPE = "https://graph.microsoft.com/.default"
USER = "barbara@bardach.net"
DATA_FILE = "D:/ClaudeTools/temp/bardach_temp_vs_main.json"
OUTPUT_FILE = "D:/ClaudeTools/temp/bardach_op2_move_unique.json"

# Contact fields to copy in fallback mode
CONTACT_FIELDS = [
    "givenName", "surname", "displayName", "middleName", "nickName",
    "title", "jobTitle", "companyName", "department", "officeLocation",
    "businessHomePage", "personalNotes", "generation", "imAddresses",
    "emailAddresses", "homePhones", "mobilePhone", "businessPhones",
    "homeAddress", "businessAddress", "otherAddress",
    "birthday", "yomiGivenName", "yomiSurname", "yomiCompanyName",
    "fileAs", "initials", "manager", "assistantName", "profession",
    "spouseName", "children"
]

def get_token():
    url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token"
    data = (
        f"client_id={CLIENT_ID}"
        f"&scope={urllib.parse.quote(SCOPE)}"
        f"&client_secret={urllib.parse.quote(CLIENT_SECRET)}"
        f"&grant_type=client_credentials"
    )
    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] Token acquisition failed: {resp}")
        raise Exception("Failed to get token")
    print("[OK] Token acquired")
    return resp["access_token"]

def get_contacts_folder_id(token):
    """Get the default Contacts folder ID."""
    url = f"https://graph.microsoft.com/v1.0/users/{USER}/contactFolders"
    result = subprocess.run(
        ["curl", "-s", "-X", "GET", url,
         "-H", f"Authorization: Bearer {token}"],
        capture_output=True, text=True
    )
    resp = json.loads(result.stdout)
    for folder in resp.get("value", []):
        if folder.get("displayName") == "Contacts":
            return folder["id"]
    return None

def try_move(token, contact_id, dest_id):
    """Try the /move endpoint."""
    url = f"https://graph.microsoft.com/v1.0/users/{USER}/contacts/{contact_id}/move"
    body = json.dumps({"destinationId": dest_id})
    result = subprocess.run(
        ["curl", "-s", "-w", "\n%{http_code}", "-X", "POST", url,
         "-H", f"Authorization: Bearer {token}",
         "-H", "Content-Type: application/json",
         "-d", body],
        capture_output=True, text=True
    )
    lines = result.stdout.rsplit("\n", 1)
    status = lines[-1].strip() if len(lines) > 1 else "000"
    body_text = lines[0] if len(lines) > 1 else result.stdout
    return status, body_text

def get_contact(token, contact_id):
    """Read full contact data."""
    url = f"https://graph.microsoft.com/v1.0/users/{USER}/contacts/{contact_id}"
    result = subprocess.run(
        ["curl", "-s", "-X", "GET", url,
         "-H", f"Authorization: Bearer {token}"],
        capture_output=True, text=True
    )
    return json.loads(result.stdout)

def create_contact(token, contact_data):
    """Create a contact in the default Contacts folder."""
    url = f"https://graph.microsoft.com/v1.0/users/{USER}/contacts"
    # Build clean payload with only writable fields
    payload = {}
    for field in CONTACT_FIELDS:
        val = contact_data.get(field)
        if val is not None and val != "" and val != [] and val != {}:
            payload[field] = val
    body = json.dumps(payload)
    result = subprocess.run(
        ["curl", "-s", "-w", "\n%{http_code}", "-X", "POST", url,
         "-H", f"Authorization: Bearer {token}",
         "-H", "Content-Type: application/json",
         "-d", body],
        capture_output=True, text=True
    )
    lines = result.stdout.rsplit("\n", 1)
    status = lines[-1].strip() if len(lines) > 1 else "000"
    body_text = lines[0] if len(lines) > 1 else result.stdout
    return status, body_text

def delete_contact(token, contact_id):
    url = f"https://graph.microsoft.com/v1.0/users/{USER}/contacts/{contact_id}"
    result = subprocess.run(
        ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",
         "-X", "DELETE", url,
         "-H", f"Authorization: Bearer {token}"],
        capture_output=True, text=True
    )
    return result.stdout.strip()

def main():
    print("=" * 60)
    print("OPERATION 2: Move unique contacts to Main Contacts (278)")
    print("=" * 60)

    with open(DATA_FILE, "r") as f:
        data = json.load(f)

    unique = data["unique_to_temp"]
    total = len(unique)
    print(f"[INFO] Loaded {total} unique contacts to move")

    token = get_token()

    # Get the contacts folder ID for move endpoint
    folder_id = get_contacts_folder_id(token)
    print(f"[INFO] Main Contacts folder ID: {folder_id}")

    # Test move endpoint with first contact
    move_works = False
    if total > 0:
        test_id = unique[0]["temp_id"]
        # Try with "contacts" string first
        status, body = try_move(token, test_id, "contacts")
        if status in ("200", "201"):
            move_works = True
            print("[OK] Move endpoint works with 'contacts' destination")
        elif folder_id:
            # Try with actual folder ID
            status, body = try_move(token, test_id, folder_id)
            if status in ("200", "201"):
                move_works = True
                print("[OK] Move endpoint works with folder ID")
            else:
                print(f"[WARNING] Move endpoint returned {status}, falling back to copy+delete")
                print(f"  Response: {body[:200]}")
        else:
            print(f"[WARNING] Move returned {status} and no folder ID found, using copy+delete")

    use_move = move_works
    dest_id = "contacts" if not folder_id else folder_id
    # If the first contact was already moved successfully via test, track it
    start_index = 1 if move_works else 0

    successes = []
    failures = []
    method_used = "move" if use_move else "copy+delete"
    print(f"[INFO] Using method: {method_used}")

    if move_works:
        # First one already moved
        successes.append({
            "temp_id": unique[0]["temp_id"],
            "displayName": unique[0].get("displayName", ""),
            "method": "move"
        })

    start_time = time.time()

    for i in range(start_index, total):
        contact = unique[i]
        # Re-acquire token every 250 operations
        if i > 0 and i % 250 == 0:
            print(f"[INFO] Re-acquiring token at operation {i}...")
            token = get_token()

        temp_id = contact["temp_id"]
        display = contact.get("displayName", "")

        if use_move:
            status, body = try_move(token, temp_id, dest_id)
            if status in ("200", "201"):
                successes.append({"temp_id": temp_id, "displayName": display, "method": "move"})
            else:
                failures.append({"temp_id": temp_id, "displayName": display, "status": status, "error": body[:200]})
        else:
            # Fallback: copy + delete
            try:
                cdata = get_contact(token, temp_id)
                if "error" in cdata:
                    failures.append({"temp_id": temp_id, "displayName": display, "status": "read_fail", "error": str(cdata["error"])[:200]})
                    continue

                cstatus, cbody = create_contact(token, cdata)
                if cstatus in ("200", "201"):
                    # Delete original
                    dstatus = delete_contact(token, temp_id)
                    if dstatus in ("204", "200"):
                        successes.append({"temp_id": temp_id, "displayName": display, "method": "copy+delete"})
                    else:
                        successes.append({"temp_id": temp_id, "displayName": display, "method": "copy_only", "delete_status": dstatus})
                else:
                    failures.append({"temp_id": temp_id, "displayName": display, "status": cstatus, "error": cbody[:200]})
            except Exception as e:
                failures.append({"temp_id": temp_id, "displayName": display, "status": "exception", "error": str(e)[:200]})

        # Progress every 25
        if (i + 1) % 25 == 0 or (i + 1) == total:
            elapsed = time.time() - start_time
            rate = (i + 1) / elapsed if elapsed > 0 else 0
            print(f"  [{i+1}/{total}] OK={len(successes)} FAIL={len(failures)} ({rate:.1f}/sec)")

    elapsed = time.time() - start_time
    results = {
        "operation": "move_unique_contacts",
        "method": method_used,
        "total": total,
        "successes": len(successes),
        "failures": len(failures),
        "elapsed_seconds": round(elapsed, 1),
        "successful_contacts": successes,
        "failed_contacts": failures
    }

    with open(OUTPUT_FILE, "w") as f:
        json.dump(results, f, indent=2)

    print(f"\n[SUCCESS] Operation 2 complete")
    print(f"  Moved:   {len(successes)}/{total}")
    print(f"  Failed:  {len(failures)}/{total}")
    print(f"  Method:  {method_used}")
    print(f"  Time:    {elapsed:.1f}s")
    print(f"  Results: {OUTPUT_FILE}")

if __name__ == "__main__":
    main()
