#!/usr/bin/env python3
"""Create real person contacts in Barbara's M365 Contacts folder from missing contacts list."""

import json
import subprocess
import re
import time

TENANT_ID = "dd4a82e8-85a3-44ac-8800-07945ab4d95f"
CLIENT_ID = "fabb3421-8b34-484b-bc17-e46de9703418"
CLIENT_SECRET = "~QJ8Q~NyQSs4OcGqHZyPrA2CVnq9KBfKiimntbMO"
USER = "barbara@bardach.net"
GRAPH_BASE = "https://graph.microsoft.com/v1.0"
MIN_MESSAGES = 4
BARBARAS_PHONE = "(520) 275-3867"

# Commercial domains to exclude
COMMERCIAL_DOMAINS = {
    "monos.com", "zestypaws.com", "augustinusbader.com", "ella-bella.com",
    "thefarmersdog.com", "nordprotect.zendesk.com", "hilton.com", "orhp.com",
    "havenlifestyles.com", "4unature.com", "skyslope.com", "arcisgolf.com",
    "tucsonrealtors.org"
}

# Commercial keywords in display_name (case-insensitive)
COMMERCIAL_NAME_KEYWORDS = [
    "team", "support", "reception", "frontdesk", "nordprotect", "zesty", "monos"
]

# Commercial email prefixes
COMMERCIAL_EMAIL_PREFIXES = [
    "care@", "hello@", "connect@", "contact@", "bark@", "support+",
    "justchecking", "ticketing@"
]

# Title suffixes to drop when parsing names
TITLE_SUFFIXES = [
    "office manager", "broker", "agent", "realtor", "manager", "director",
    "assistant", "coordinator", "specialist", "advisor", "consultant"
]


def get_token():
    """Get OAuth token via client credentials."""
    url = f"https://login.microsoftonline.com/{TENANT_ID}/oauth2/v2.0/token"
    cmd = [
        "curl", "-s", "-X", "POST", url,
        "-H", "Content-Type: application/x-www-form-urlencoded",
        "-d", f"client_id={CLIENT_ID}&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default&client_secret={CLIENT_SECRET}&grant_type=client_credentials"
    ]
    result = subprocess.run(cmd, capture_output=True, text=True)
    data = json.loads(result.stdout)
    if "access_token" not in data:
        print(f"[ERROR] Token failed: {data}")
        return None
    return data["access_token"]


def is_email_like(name):
    """Check if display_name is just an email address."""
    return "@" in name and "." in name


def is_commercial(contact):
    """Check if a contact is commercial/automated."""
    email = contact["email"].lower()
    name = contact["display_name"].lower()
    domain = email.split("@")[-1] if "@" in email else ""

    # Own email
    if email == "bardach@bardach.net":
        return True

    # No-reply patterns
    if any(x in email for x in ["noreply", "no-reply", "donotreply"]):
        return True

    # Commercial domains
    if domain in COMMERCIAL_DOMAINS:
        return True

    # Commercial name keywords
    for kw in COMMERCIAL_NAME_KEYWORDS:
        if kw in name:
            return True

    # Commercial email prefixes
    for prefix in COMMERCIAL_EMAIL_PREFIXES:
        if email.startswith(prefix) or prefix in email:
            return True

    return False


def parse_name(display_name):
    """Parse display_name into (givenName, surname)."""
    name = display_name.strip()

    # Handle "Last, First" format
    if "," in name:
        parts = [p.strip() for p in name.split(",", 1)]
        if len(parts) == 2 and parts[0] and parts[1]:
            first = parts[1].split()[0]  # Take first word after comma
            return first, parts[0]

    # Split into words
    words = name.split()
    if len(words) == 0:
        return "", ""
    if len(words) == 1:
        return words[0], ""
    if len(words) == 2:
        return words[0], words[1]

    # 3+ words: check for title suffixes
    # Try to find where a title suffix starts
    lower_name = name.lower()
    for suffix in TITLE_SUFFIXES:
        idx = lower_name.find(suffix)
        if idx > 0:
            # Take everything before the suffix
            name_part = name[:idx].strip()
            name_words = name_part.split()
            if len(name_words) >= 2:
                return name_words[0], " ".join(name_words[1:])
            elif len(name_words) == 1:
                return name_words[0], ""

    # Default: first word = given, second word = surname, ignore rest
    return words[0], words[1]


def build_contact_payload(contact):
    """Build the JSON payload for creating a contact."""
    given, surname = parse_name(contact["display_name"])
    payload = {
        "givenName": given,
        "surname": surname,
        "displayName": contact["display_name"],
        "emailAddresses": [
            {"address": contact["email"], "name": contact["display_name"]}
        ]
    }

    phone = contact.get("phone")
    label = (contact.get("phone_label") or "").strip()

    if phone and phone != BARBARAS_PHONE:
        label_lower = label.lower()
        if label_lower == "fax":
            pass  # Skip fax
        elif label_lower in ("cell", "mobile"):
            payload["mobilePhone"] = phone
        elif label_lower in ("home",):
            payload["homePhones"] = [phone]
        else:
            # Office, Direct, Phone, empty -> businessPhones
            payload["businessPhones"] = [phone]

    return payload


def create_contact(token, payload):
    """Create a contact via Graph API."""
    url = f"{GRAPH_BASE}/users/{USER}/contacts"
    json_str = json.dumps(payload)
    cmd = [
        "curl", "-s", "-X", "POST", url,
        "-H", f"Authorization: Bearer {token}",
        "-H", "Content-Type: application/json",
        "-d", json_str
    ]
    result = subprocess.run(cmd, capture_output=True, text=True)
    try:
        data = json.loads(result.stdout)
    except json.JSONDecodeError:
        return None, result.stdout
    return data, None


def main():
    # Load data
    with open(r"D:\ClaudeTools\temp\bardach_missing_real_contacts.json", encoding="utf-8") as f:
        data = json.load(f)

    contacts = data["contacts"]
    print(f"[INFO] Loaded {len(contacts)} total missing contacts")

    # Filter: minimum messages
    contacts = [c for c in contacts if c["total"] >= MIN_MESSAGES]
    print(f"[INFO] After >= {MIN_MESSAGES} messages filter: {len(contacts)}")

    # Filter: remove empty/email-only display names
    filtered = []
    removed_reasons = []
    for c in contacts:
        name = (c["display_name"] or "").strip()
        email = c["email"].lower()

        if not name:
            removed_reasons.append(f"  REMOVED (empty name): {email}")
            continue
        if is_email_like(name):
            removed_reasons.append(f"  REMOVED (email-as-name): {name} <{email}>")
            continue
        if is_commercial(c):
            removed_reasons.append(f"  REMOVED (commercial): {name} <{email}>")
            continue
        filtered.append(c)

    print(f"[INFO] Removed {len(contacts) - len(filtered)} non-person entries:")
    for r in removed_reasons:
        print(r)

    print(f"\n[INFO] Final filtered list: {len(filtered)} real person contacts\n")

    # Print the filtered list for review
    print(f"{'#':<4} {'Name':<35} {'Email':<45} {'Phone':<18} {'Msgs':>5}")
    print("-" * 110)
    has_phone_count = 0
    for i, c in enumerate(filtered, 1):
        phone = c.get("phone") or ""
        if phone == BARBARAS_PHONE:
            phone = "(skipped-own)"
        if phone and phone != "(skipped-own)":
            has_phone_count += 1
        label = c.get("phone_label") or ""
        phone_display = f"{phone} [{label}]" if label else phone
        print(f"{i:<4} {c['display_name']:<35} {c['email']:<45} {phone_display:<18} {c['total']:>5}")

    print(f"\n[INFO] {has_phone_count} contacts have phone numbers")
    print(f"[INFO] Starting contact creation...\n")

    # Get token
    token = get_token()
    if not token:
        print("[ERROR] Could not get token. Aborting.")
        return

    created = 0
    errors = 0
    with_phone = 0

    for i, c in enumerate(filtered):
        # Refresh token every 30 creates
        if i > 0 and i % 30 == 0:
            print(f"[INFO] Refreshing token after {i} contacts...")
            token = get_token()
            if not token:
                print("[ERROR] Token refresh failed. Aborting.")
                return

        payload = build_contact_payload(c)
        has_phone = "businessPhones" in payload or "mobilePhone" in payload or "homePhones" in payload

        resp, err = create_contact(token, payload)
        if err:
            print(f"[ERROR] {c['display_name']} <{c['email']}>: curl error: {err}")
            errors += 1
            continue

        if "id" in resp:
            phone_note = " (with phone)" if has_phone else ""
            print(f"[CREATED] {c['display_name']} <{c['email']}>{phone_note}")
            created += 1
            if has_phone:
                with_phone += 1
        else:
            err_code = resp.get("error", {}).get("code", "unknown")
            err_msg = resp.get("error", {}).get("message", str(resp))
            print(f"[ERROR] {c['display_name']} <{c['email']}>: {err_code} - {err_msg}")
            errors += 1

    print(f"\n{'=' * 60}")
    print(f"[SUMMARY]")
    print(f"  Total filtered contacts: {len(filtered)}")
    print(f"  Created:     {created}")
    print(f"  With phone:  {with_phone}")
    print(f"  Errors:      {errors}")
    print(f"{'=' * 60}")


if __name__ == "__main__":
    main()
