#!/usr/bin/env python3
import json
import os
import subprocess
import sys
import urllib.request

NOTION_VERSION = "2025-09-03"
TASKS_DS_ID = os.environ.get("NOTION_TASKS_DS_ID", "98ac8c59-4c40-4672-a628-2c35392eecfa")
REMINDER_LIST = os.environ.get("REMINDER_LIST", "Reminders")
MARKER_PREFIX = "NOTION_TASK_ID:"


def run(cmd):
    return subprocess.check_output(cmd, text=True)


def notion_request(method, path, payload=None):
    key_path = os.path.expanduser("~/.config/notion/api_key")
    if not os.path.exists(key_path):
        raise RuntimeError("Missing Notion API key at ~/.config/notion/api_key")
    key = open(key_path, "r", encoding="utf-8").read().strip()
    data = None
    if payload is not None:
        data = json.dumps(payload).encode("utf-8")
    req = urllib.request.Request(
        f"https://api.notion.com/v1{path}",
        data=data,
        method=method,
        headers={
            "Authorization": f"Bearer {key}",
            "Notion-Version": NOTION_VERSION,
            "Content-Type": "application/json",
        },
    )
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read().decode("utf-8"))


def query_tasks():
    results = []
    cursor = None
    while True:
        body = {"page_size": 100}
        if cursor:
            body["start_cursor"] = cursor
        page = notion_request("POST", f"/data_sources/{TASKS_DS_ID}/query", body)
        results.extend(page.get("results", []))
        if not page.get("has_more"):
            break
        cursor = page.get("next_cursor")
    return results


def get_prop(props, name, ptype):
    p = props.get(name)
    if not p:
        return None
    return p.get(ptype)


def task_name(props):
    t = get_prop(props, "Name", "title") or []
    return "".join(x.get("plain_text", "") for x in t).strip()


def task_due(props):
    d = get_prop(props, "Reminder At", "date")
    if d and d.get("start"):
        return d.get("start")
    d2 = get_prop(props, "Due", "date")
    if d2 and d2.get("start"):
        return d2.get("start")
    return None


def task_should_sync(props):
    needs = get_prop(props, "Needs Reminder", "checkbox")
    if needs is None:
        return False
    status = get_prop(props, "Completed", "status") or {}
    if (status.get("name") or "") == "Done":
        return False
    if not needs:
        return False
    return task_due(props) is not None


def reminders_index():
    raw = run(["remindctl", "show", "all", "--json"])
    data = json.loads(raw)
    by_marker = {}
    for r in data:
        notes = r.get("notes") or ""
        if MARKER_PREFIX in notes:
            marker = notes.split(MARKER_PREFIX, 1)[1].split()[0].strip()
            by_marker[marker] = r
    return by_marker


def set_notion_sync_fields(page_id, status, external_id=None, error_text=None):
    props = {
        "Last Sync Status": {"select": {"name": status}},
        "Last Sync Attempt At": {"date": {"start": __import__("datetime").datetime.utcnow().isoformat() + "Z"}},
    }
    if external_id is not None:
        props["Reminder External ID"] = {"rich_text": [{"type": "text", "text": {"content": external_id}}]}
        props["Reminder Synced At"] = {"date": {"start": __import__("datetime").datetime.utcnow().isoformat() + "Z"}}
    if error_text is not None:
        props["Last Sync Error"] = {"rich_text": [{"type": "text", "text": {"content": error_text[:1800]}}]}
    notion_request("PATCH", f"/pages/{page_id}", {"properties": props})


def add_reminder(title, due, notion_id):
    notes = f"Synced from Notion\n{MARKER_PREFIX}{notion_id}"
    out = run(["remindctl", "add", "--title", title, "--list", REMINDER_LIST, "--due", due, "--notes", notes, "--json"])
    obj = json.loads(out)
    rid = obj.get("id") or obj.get("reminder", {}).get("id")
    return rid


def edit_reminder(reminder_id, title, due):
    run(["remindctl", "edit", reminder_id, "--title", title, "--due", due, "--json"])


def complete_reminder(reminder_id):
    run(["remindctl", "complete", reminder_id, "--json"])


def main():
    dry_run = "--apply" not in sys.argv
    tasks = query_tasks()
    r_index = reminders_index()

    created = updated = cleared = skipped = errors = 0

    for row in tasks:
        page_id = row.get("id")
        props = row.get("properties", {})
        name = task_name(props)
        if not name:
            skipped += 1
            continue

        notion_id = page_id
        due = task_due(props)
        status = (get_prop(props, "Completed", "status") or {}).get("name") or ""
        should_sync = task_should_sync(props)

        existing = r_index.get(notion_id)

        try:
            if should_sync:
                title = f"[Notion] {name}"
                if existing:
                    if not dry_run:
                        edit_reminder(existing["id"], title, due)
                        set_notion_sync_fields(page_id, "updated", external_id=existing["id"], error_text="")
                    updated += 1
                else:
                    if not dry_run:
                        rid = add_reminder(title, due, notion_id)
                        set_notion_sync_fields(page_id, "synced", external_id=rid, error_text="")
                    created += 1
            else:
                if existing and (status == "Done" or not (get_prop(props, "Needs Reminder", "checkbox") or False) or due is None):
                    if not dry_run:
                        complete_reminder(existing["id"])
                        set_notion_sync_fields(page_id, "cleared", external_id="", error_text="")
                    cleared += 1
                else:
                    skipped += 1
        except Exception as e:
            errors += 1
            if not dry_run:
                try:
                    set_notion_sync_fields(page_id, "error", error_text=str(e))
                except Exception:
                    pass

    summary = {
        "dry_run": dry_run,
        "created": created,
        "updated": updated,
        "cleared": cleared,
        "skipped": skipped,
        "errors": errors,
    }
    print(json.dumps(summary, indent=2))


if __name__ == "__main__":
    main()
