#!/usr/bin/env python3
"""Sync Notion databases to brain/ folder. Runs on VPS via cron.
Outputs compact MD files optimized for token efficiency."""
import json, os, sys, time
from urllib.request import Request, urlopen
from urllib.error import HTTPError

NOTION_KEY = os.environ.get("NOTION_API_KEY", "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")
NOTION_VER = "2022-06-28"
BRAIN_DIR = os.environ.get("BRAIN_DIR", "/home/clawd/workspace/brain")

DATABASES = {
    "projects":        "2f0330c2-8646-8111-9d86-dc9ad729ce37",
    "tasks":           "2f0330c2-8646-815a-878b-dbdd46606837",
    "contacts":        "2f0330c2-8646-8195-a9a8-c37a1ea718a6",
    "meetings":        "2f0330c2-8646-814f-8be4-c23065765eaa",
    "todos":           "2ef330c2-8646-81d5-844f-f52c4ec02f18",
    "shopping-list":   "2ef330c2-8646-81d1-813f-e552c1564b82",
    "chores":          "2ef330c2-8646-81f6-b93e-f3ff0527ffa8",
}

FINANCE_DBS = {
    "expenses-spoken": "2f0330c2-8646-8195-baf1-e804f1579dd4",
    "expenses":        "2ef330c2-8646-81c4-9496-fa57b179a785",
    "budget-limits":   "2ef330c2-8646-81c4-9c82-e4cce232a218",
}

def notion_api(endpoint, body=None):
    url = f"https://api.notion.com/v1/{endpoint}"
    headers = {"Authorization": f"Bearer {NOTION_KEY}", "Notion-Version": NOTION_VER, "Content-Type": "application/json"}
    data = json.dumps(body).encode() if body else None
    req = Request(url, data=data, headers=headers, method="POST" if body else "GET")
    try:
        return json.loads(urlopen(req, timeout=30).read())
    except HTTPError as e:
        print(f"  ⚠️ {e.code}: {e.read().decode()[:100]}", file=sys.stderr)
        return None

def query_all(db_id):
    pages, cursor = [], None
    while True:
        body = {"page_size": 100}
        if cursor: body["start_cursor"] = cursor
        result = notion_api(f"databases/{db_id}/query", body)
        if not result: break
        pages.extend(result.get("results", []))
        if not result.get("has_more"): break
        cursor = result.get("next_cursor")
        time.sleep(0.35)
    return pages

def extract(prop):
    t = prop.get("type", "")
    if t == "title": return "".join(x.get("plain_text","") for x in prop.get("title",[]))
    if t == "rich_text": return "".join(x.get("plain_text","") for x in prop.get("rich_text",[]))
    if t == "select": s = prop.get("select"); return s["name"] if s else None
    if t == "multi_select": return ", ".join(s["name"] for s in prop.get("multi_select",[]))
    if t == "status": s = prop.get("status"); return s["name"] if s else None
    if t == "date": d = prop.get("date"); return d["start"] if d else None
    if t in ("checkbox",): return prop.get("checkbox")
    if t == "number": return prop.get("number")
    if t == "email": return prop.get("email")
    if t == "phone_number": return prop.get("phone_number")
    if t == "url": return prop.get("url")
    if t == "people": return ", ".join(p.get("name","?") for p in prop.get("people",[]))
    if t in ("created_time","last_edited_time"): return prop.get(t,"")[:10]
    if t == "formula": f=prop.get("formula",{}); return f.get(f.get("type"))
    return None

def to_row(page):
    row = {}
    for name, prop in page.get("properties",{}).items():
        v = extract(prop)
        if v is not None and v != "" and v != []: row[name] = v
    return row

def write_compact_md(rows, path, title):
    """Write token-efficient markdown — table format for structured data."""
    with open(path, "w") as f:
        f.write(f"# {title}\n")
        f.write(f"_Synced: {time.strftime('%Y-%m-%d %H:%M UTC')}_\n\n")
        if not rows:
            f.write("_Empty_\n")
            return
        # Get all keys
        keys = []
        for r in rows:
            for k in r:
                if k not in keys: keys.append(k)
        # Prioritize Name/Title first
        for priority in ["Name","Title","Task","Project"]:
            if priority in keys:
                keys.remove(priority)
                keys.insert(0, priority)
        # Table header
        f.write("| " + " | ".join(keys) + " |\n")
        f.write("| " + " | ".join(["---"]*len(keys)) + " |\n")
        for r in rows:
            vals = []
            for k in keys:
                v = r.get(k, "")
                if isinstance(v, bool): v = "✅" if v else "❌"
                v = str(v).replace("|", "/").replace("\n", " ")[:80]
                vals.append(v)
            f.write("| " + " | ".join(vals) + " |\n")

def main():
    os.makedirs(BRAIN_DIR, exist_ok=True)
    os.makedirs(os.path.join(BRAIN_DIR, "finance"), exist_ok=True)
    total = 0

    for name, db_id in DATABASES.items():
        print(f"📥 {name}...", end=" ", flush=True)
        pages = query_all(db_id)
        rows = [to_row(p) for p in pages]
        json_path = os.path.join(BRAIN_DIR, f"{name}.json")
        md_path = os.path.join(BRAIN_DIR, f"{name}.md")
        with open(json_path, "w") as f: json.dump(rows, f, indent=2, default=str)
        write_compact_md(rows, md_path, name.replace("-"," ").title())
        print(f"{len(rows)}")
        total += len(rows)
        time.sleep(0.5)

    for name, db_id in FINANCE_DBS.items():
        print(f"📥 finance/{name}...", end=" ", flush=True)
        pages = query_all(db_id)
        rows = [to_row(p) for p in pages]
        json_path = os.path.join(BRAIN_DIR, "finance", f"{name}.json")
        md_path = os.path.join(BRAIN_DIR, "finance", f"{name}.md")
        with open(json_path, "w") as f: json.dump(rows, f, indent=2, default=str)
        write_compact_md(rows, md_path, name.replace("-"," ").title())
        print(f"{len(rows)}")
        total += len(rows)
        time.sleep(0.5)

    # Write sync timestamp
    with open(os.path.join(BRAIN_DIR, "_last_sync.txt"), "w") as f:
        f.write(time.strftime("%Y-%m-%d %H:%M:%S UTC"))

    print(f"\n✅ Synced {total} records to {BRAIN_DIR}")

if __name__ == "__main__":
    main()
