# deletelinks_emails_only.py
import os
import re
import requests
import json
import time
import csv
import sys
from pathlib import Path
from datetime import datetime
from typing import Optional, List

# ---------------------------
# Config - عدّل القيم لو لازِم
# ---------------------------
CLIENT_ID = "1000.6XRABP1ZPTQ2M0TOCLMUA3764Y0B1Z"
CLIENT_SECRET = "2da33cfb7e3babc22ff3c25438e12cacd6fde224cd"
AUTH_CODE = ""  # أو اتركه فارغ واستخدم tokens.json
REDIRECT_URI = "https://www.zoho.com"
TOKENS_FILE = Path("tokens.json")

RECRUIT_API_BASE = "https://recruit.zoho.com/recruit/v2"
CV_FOLDER = Path(r"C:\Users\Mostafa Rizk\Desktop\zoho\zohodeletelinks")   # مجلد العمل
MAPPING_CSV = CV_FOLDER / "Emails.csv"  # يجب أن يكون هنا
LOG_FILE = Path("clear_log.csv")

TARGET_FIELD_LABELS = ["File 1", "File 2", "File 3", "File 4", "File 5"]

# Safety: DRY_RUN=True --> يطبع اللي هيعمله فقط. غيّرها False للتنفيذ الحقيقي.
DRY_RUN = True

# ---------------------------
# Token helpers (كما في سكربتك الأصلي)
# ---------------------------
def load_tokens() -> Optional[dict]:
    if not TOKENS_FILE.exists():
        return None
    try:
        return json.loads(TOKENS_FILE.read_text(encoding="utf-8"))
    except Exception as e:
        print(f"[WARN] failed to read tokens.json: {e}")
        return None

def save_tokens(tokens: dict):
    old = load_tokens() or {}
    merged = old.copy()
    merged.update(tokens)
    if "refresh_token" not in tokens and "refresh_token" in old:
        merged["refresh_token"] = old["refresh_token"]
    TOKENS_FILE.write_text(json.dumps(merged, indent=2), encoding="utf-8")

def exchange_auth_code_for_tokens(auth_code: str):
    url = "https://accounts.zoho.com/oauth/v2/token"
    data = {
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET,
        "grant_type": "authorization_code",
        "code": auth_code,
        "redirect_uri": REDIRECT_URI,
    }
    r = requests.post(url, data=data, timeout=30)
    if r.status_code != 200:
        raise Exception(f"Auth code exchange failed ({r.status_code}): {r.text}")
    tokens = r.json()
    save_tokens(tokens)
    print("[INFO] Auth code exchanged and tokens saved to tokens.json")
    return tokens

def refresh_access_token() -> str:
    tokens = load_tokens()
    if not tokens:
        raise Exception("tokens.json not found. Place tokens.json or set AUTH_CODE and run exchange.")
    refresh_token = tokens.get("refresh_token")
    if not refresh_token:
        raise Exception("No refresh_token in tokens.json. Run the AUTH_CODE flow first.")
    url = "https://accounts.zoho.com/oauth/v2/token"
    data = {
        "refresh_token": refresh_token,
        "client_id": CLIENT_ID,
        "client_secret": CLIENT_SECRET,
        "grant_type": "refresh_token"
    }
    r = requests.post(url, data=data, timeout=30)
    if r.status_code != 200:
        raise Exception(f"Refresh failed ({r.status_code}): {r.text}")
    new = r.json()
    new["refresh_token"] = refresh_token
    save_tokens(new)
    print("[INFO] Access token refreshed and saved to tokens.json")
    return new["access_token"]

def get_access_token(allow_exchange_if_missing=True) -> str:
    tokens = load_tokens()
    if not tokens:
        if AUTH_CODE and allow_exchange_if_missing:
            tokens = exchange_auth_code_for_tokens(AUTH_CODE)
        else:
            raise Exception("tokens.json not found and AUTH_CODE not provided.")
    access_token = tokens.get("access_token")
    if not access_token:
        access_token = refresh_access_token()
    return access_token

# ---------------------------
# Helpers
# ---------------------------
def current_timestamp() -> str:
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

def append_log_row(writer: csv.DictWriter, log_file_obj, row: dict):
    writer.writerow(row)
    log_file_obj.flush()

# ---------------------------
# Load emails from Emails.csv (Email column only)
# ---------------------------
def load_emails_from_csv(csv_path: Path) -> List[str]:
    emails = []
    if not csv_path.exists():
        return emails
    try:
        with open(csv_path, newline="", encoding="utf-8-sig") as f:
            reader = csv.DictReader(f)
            # إذا كان الملف بدون header، نجرب قراءة كل سطر كقيمة
            if not reader.fieldnames:
                f.seek(0)
                for row in f:
                    val = row.strip()
                    if val:
                        emails.append(val.lower())
                return emails
            headers = [h.strip().lower() for h in (reader.fieldnames or [])]
            # Find the email column
            email_col = None
            for candidate in ("email", "e-mail", "mail"):
                if candidate in headers:
                    email_col = [h for h in reader.fieldnames if h.strip().lower() == candidate][0]
                    break
            # fallback to first column if nothing matched
            if not email_col and reader.fieldnames:
                email_col = reader.fieldnames[0]
            for row in reader:
                val = (row.get(email_col) or "").strip()
                if val:
                    emails.append(val.lower())
    except Exception as e:
        print(f"[WARN] Failed to read Emails.csv: {e}")
    return emails

# ---------------------------
# Zoho API helpers: search + resolve fields + clear
# (reuse نفس الدوال في سكربتك) 
# ---------------------------
def search_ats_record_by_email(email: str) -> Optional[str]:
    if not email:
        return None
    for attempt in range(2):
        token = get_access_token(allow_exchange_if_missing=(attempt == 0))
        headers = {"Authorization": f"Zoho-oauthtoken {token}"}
        criteria = f"(Email:equals:{email})"
        url = f"{RECRUIT_API_BASE}/ATS/search?criteria={requests.utils.quote(criteria)}"
        try:
            r = requests.get(url, headers=headers, timeout=30)
        except Exception as e:
            print(f"[ERROR] search request failed for {email}: {e}")
            return None
        if r.status_code == 200:
            data = r.json().get("data")
            if data:
                return data[0].get("id")
            return None
        if r.status_code == 204:
            return None
        if r.status_code == 401:
            try:
                refresh_access_token()
                print(f"[INFO] Token refreshed after 401 for search ({email}). Retrying...")
                continue
            except Exception as e:
                print(f"[ERROR] refresh failed after 401: {e}")
                return None
        print(f"[WARN] search API returned {r.status_code} for {email}: {r.text}")
        return None
    return None

def get_candidate_fields_map() -> dict:
    tries = 0
    while tries < 2:
        token = get_access_token()
        headers = {"Authorization": f"Zoho-oauthtoken {token}"}
        url = f"{RECRUIT_API_BASE}/settings/fields?module=Candidates"
        r = requests.get(url, headers=headers, timeout=30)
        if r.status_code == 200:
            j = r.json()
            fields = j.get("fields") or j.get("data") or j.get("Fields") or []
            mapping = {}
            for f in fields:
                label = f.get("label") or f.get("field_label") or f.get("field_label")
                api = f.get("api_name") or f.get("field_name") or f.get("field_api_name")
                if label and api:
                    mapping[label.strip().lower()] = api
            return mapping
        if r.status_code == 401:
            try:
                refresh_access_token()
            except Exception as e:
                print(f"[ERROR] refresh failed while fetching fields: {e}")
                return {}
            tries += 1
            continue
        print(f"[WARN] Could not fetch fields: {r.status_code} {r.text}")
        return {}
    return {}

def resolve_field_api_names(labels: List[str], fields_map: dict) -> dict:
    out = {}
    for lab in labels:
        key = lab.strip().lower()
        if key in fields_map:
            out[lab] = fields_map[key]
            continue
        alt1 = key.replace(" ", "_")
        alt2 = key.replace(" ", "")
        alt3 = key.replace(" ", "").replace("-", "_")
        found = None
        for lbl_k, api in fields_map.items():
            if alt1 == api.lower() or alt2 == api.lower() or alt3 == api.lower() or key == api.lower():
                found = api
                break
            if key in lbl_k:
                found = api
                break
        if found:
            out[lab] = found
        else:
            generated = lab.strip().replace(" ", "_")
            out[lab] = generated
    return out

def clear_file_fields_for_record(record_id: str, api_field_names: List[str]) -> (bool, str):
    tries = 0
    while tries < 3:
        token = get_access_token()
        headers = {
            "Authorization": f"Zoho-oauthtoken {token}",
            "Content-Type": "application/json"
        }
        url = f"{RECRUIT_API_BASE}/Candidates/{record_id}"
        data_obj = {}
        for api_name in api_field_names:
            data_obj[api_name] = ""
        payload = {"data": [data_obj]}
        if DRY_RUN:
            return True, f"[DRY_RUN] would PATCH {url} with payload: {json.dumps(payload)}"
        try:
            r = requests.patch(url, headers=headers, data=json.dumps(payload), timeout=30)
        except Exception as e:
            return False, f"exception: {e}"
        if r.status_code in (200, 201):
            return True, "Cleared successfully"
        if r.status_code == 401:
            try:
                refresh_access_token()
            except Exception as e:
                return False, f"refresh failed: {e}"
            tries += 1
            continue
        return False, f"{r.status_code} - {r.text}"
    return False, "max retries exceeded"

# ---------------------------
# Main
# ---------------------------
def main():
    try:
        _ = get_access_token()
    except Exception as e:
        print(f"[ERROR] token setup error: {e}")
        sys.exit(1)

    # Load emails from Emails.csv (email-only mode)
    emails_from_csv = load_emails_from_csv(MAPPING_CSV)
    if emails_from_csv:
        # Print sample to confirm
        print(f"[INFO] Emails.csv found with {len(emails_from_csv)} emails. First 5: {emails_from_csv[:5]}")
        use_emails_only = True
    else:
        use_emails_only = False
        print(f"[INFO] No Emails.csv found — fallback not desired. Exiting (no input).")
        # هنا قررت نخرج بدلاً من الاعتماد على أسماء الملفات تلقائياً
        # لو عايز fallback ممكن ترجّع السلوك القديم
        return

    # resolve fields map once
    fields_map = get_candidate_fields_map()
    if not fields_map:
        print("[WARN] Could not fetch candidate fields map or map is empty. Will attempt guessed api names.")
    resolved = resolve_field_api_names(TARGET_FIELD_LABELS, fields_map)
    api_names = [resolved[label] for label in TARGET_FIELD_LABELS]
    print("[INFO] Resolved field api names mapping:")
    for k, v in resolved.items():
        print(f"  '{k}' -> '{v}'")

    # logging
    file_exists = LOG_FILE.exists()
    with open(LOG_FILE, "a", newline="", encoding="utf-8") as log_file:
        fieldnames = ["Timestamp", "Email", "RecordID", "Status", "Message", "Source"]
        writer = csv.DictWriter(log_file, fieldnames=fieldnames)
        if not file_exists or LOG_FILE.stat().st_size == 0:
            writer.writeheader()
            log_file.flush()

        for email in emails_from_csv:
            email = email.strip().lower()
            if not email:
                continue
            record_id = search_ats_record_by_email(email)
            if not record_id:
                msg = "NOT_FOUND"
                print(f"[NOT FOUND] {email} -> {msg}")
                append_log_row(writer, log_file, {
                    "Timestamp": current_timestamp(),
                    "Email": email,
                    "RecordID": "",
                    "Status": "NOT_FOUND",
                    "Message": msg,
                    "Source": "emails_csv"
                })
                continue
            ok, message = clear_file_fields_for_record(record_id, api_names)
            status = "OK" if ok else "ERROR"
            print(f"[{status}] {email} (record {record_id}) -> {message}")
            append_log_row(writer, log_file, {
                "Timestamp": current_timestamp(),
                "Email": email,
                "RecordID": record_id,
                "Status": status,
                "Message": message,
                "Source": "emails_csv"
            })
            time.sleep(0.25)

    print("[DONE] Processing completed. Check clear_log.csv for details.")
    if DRY_RUN:
        print("[NOTE] DRY_RUN was True — no changes were made. Set DRY_RUN=False to actually clear the fields.")

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n[ABORT] Interrupted by user.")
        sys.exit(1)
