import json, urllib.request, time

TOKEN = "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"
NOTION_VERSION = "2025-09-03"
BASE = "https://api.notion.com/v1"

TASKS_DS = "164ffa30-cf0b-810a-a744-000b90b56ad8"
PROJECTS_DS = "164ffa30-cf0b-81c1-9843-000b2e2bef01"


def notion(method, path, body=None, retries=4):
    url = BASE + path
    data = json.dumps(body).encode('utf-8') if body is not None else None
    for i in range(retries):
        try:
            req = urllib.request.Request(url, data=data, method=method)
            req.add_header('Authorization', f'Bearer {TOKEN}')
            req.add_header('Notion-Version', NOTION_VERSION)
            req.add_header('Content-Type', 'application/json')
            with urllib.request.urlopen(req, timeout=60) as r:
                return json.loads(r.read().decode('utf-8'))
        except Exception:
            if i == retries - 1:
                raise
            time.sleep(1.5 * (i+1))


def ensure_property(ds_id, name, prop_def):
    ds = notion('GET', f'/data_sources/{ds_id}')
    if name in ds.get('properties', {}):
        return 'exists'
    notion('PATCH', f'/data_sources/{ds_id}', {'properties': {name: prop_def}})
    return 'created'


def fetch_all_rows(ds_id):
    out, cursor = [], None
    while True:
        body = {'page_size': 100}
        if cursor:
            body['start_cursor'] = cursor
        resp = notion('POST', f'/data_sources/{ds_id}/query', body)
        out.extend(resp.get('results', []))
        if not resp.get('has_more'):
            break
        cursor = resp.get('next_cursor')
    return out


def g(prop_obj, typ):
    if typ == 'status':
        s = prop_obj.get('status')
        return s.get('name') if s else None
    if typ == 'select':
        s = prop_obj.get('select')
        return s.get('name') if s else None
    return None

ops = {}
ops['tasks_status'] = ensure_property(TASKS_DS, 'Status (Simple)', {'status': {'options': [
    {'name':'Backlog','color':'default'},{'name':'Active','color':'green'},{'name':'Waiting','color':'yellow'},
    {'name':'Done','color':'blue'},{'name':'Archived','color':'gray'},{'name':'Reference','color':'purple'}]}})
ops['tasks_effort'] = ensure_property(TASKS_DS, 'Effort (Simple)', {'select': {'options': [
    {'name':'Quick (<=15m)','color':'green'},{'name':'Small (15-30m)','color':'blue'},{'name':'Medium (30-60m)','color':'yellow'},
    {'name':'Deep (60m+)','color':'red'},{'name':'Unknown','color':'default'}]}})
ops['tasks_impact'] = ensure_property(TASKS_DS, 'Impact (Simple)', {'select': {'options': [
    {'name':'Low','color':'default'},{'name':'Medium','color':'blue'},{'name':'High','color':'orange'},{'name':'Very High','color':'red'},{'name':'Unknown','color':'gray'}]}})
ops['tasks_value'] = ensure_property(TASKS_DS, 'Value (Simple)', {'select': {'options': [
    {'name':'Low','color':'default'},{'name':'Medium','color':'blue'},{'name':'High','color':'orange'},{'name':'Critical','color':'red'},{'name':'Unknown','color':'gray'}]}})
ops['tasks_urgency'] = ensure_property(TASKS_DS, 'Urgency (Simple)', {'select': {'options': [
    {'name':'Today','color':'red'},{'name':'Urgent','color':'orange'},{'name':'Normal','color':'blue'},
    {'name':'Flexible','color':'green'},{'name':'Waiting','color':'yellow'},{'name':'Later','color':'gray'}]}})
ops['projects_status'] = ensure_property(PROJECTS_DS, 'Status (Simple)', {'status': {'options': [
    {'name':'Backlog','color':'default'},{'name':'Active','color':'green'},{'name':'Waiting','color':'yellow'},
    {'name':'Done','color':'blue'},{'name':'Archived','color':'gray'},{'name':'Idea','color':'purple'}]}})
ops['projects_value'] = ensure_property(PROJECTS_DS, 'Value (Simple)', {'select': {'options': [
    {'name':'Low','color':'default'},{'name':'Medium','color':'blue'},{'name':'High','color':'orange'},{'name':'Critical','color':'red'},{'name':'Unknown','color':'gray'}]}})
ops['projects_urgency'] = ensure_property(PROJECTS_DS, 'Urgency (Simple)', {'select': {'options': [
    {'name':'Top','color':'red'},{'name':'Pressing','color':'orange'},{'name':'Normal','color':'blue'},{'name':'Flexible','color':'green'},{'name':'Holding/Later','color':'gray'}]}})

TASK_STATUS_MAP = {'to Process':'Backlog','Research':'Backlog','Order Status':'Waiting','Today':'Active','Priority':'Active','Active':'Active','Review':'Waiting','Queued':'Backlog','Waiting...':'Waiting','Ongoing':'Active','Backlog':'Backlog','Task done':'Done','Archived':'Archived','Reference':'Reference','Published':'Done'}
TASK_EFFORT_MAP = {'≤ 15 min_Quick':'Quick (<=15m)','15-30min_Small':'Small (15-30m)','45min_Medium':'Medium (30-60m)','~1 hr_Full':'Deep (60m+)','2+ hrs_Deep':'Deep (60m+)','Unknown':'Unknown','not-applicable':'Unknown','task complete':'Unknown'}
TASK_IMPACT_MAP = {'Very High':'Very High','High':'High','Fairly High':'High','Moderate':'Medium','Average':'Medium','Some':'Low','Little':'Low','Low':'Low','Potential?':'Unknown','Unknown':'Unknown','none':'Low','task complete':'Unknown'}
TASK_VALUE_MAP = {'11':'Critical','10':'Critical','09':'High','08':'High','06':'Medium','05':'Medium','03':'Low','02':'Low','01':'Low','Potential - ?':'Unknown','Unknown':'Unknown','Assign':'Unknown','task done':'Unknown'}
TASK_URGENCY_MAP = {'Today':'Today','ASAP':'Urgent','Soon':'Normal','Normal':'Normal','Flexible':'Flexible','Waiting...':'Waiting','Later':'Later','—None':'Flexible','task finished':'Later'}

PROJ_STATUS_MAP = {'Reference':'Backlog','Template':'Backlog','Backlog':'Backlog','Priority':'Active','Active':'Active','Waiting':'Waiting','Review':'Waiting','Scheduled':'Backlog','Ongoing':'Active','Idea':'Idea','Tool':'Active','Dormant':'Waiting','Done':'Done','Archived':'Archived'}
PROJ_VALUE_MAP = {'11':'Critical','High':'High','Medium':'Medium','Normal':'Medium','Fair':'Low','Low':'Low','Possible':'Low','Unknown':'Unknown'}
PROJ_URGENCY_MAP = {'Top':'Top','Pressing':'Pressing','Normal':'Normal','Flexible':'Flexible','Holding':'Holding/Later','Later':'Holding/Later','Zero':'Holding/Later','Moving':'Normal','Active':'Normal'}


def update_rows(ds_id, mapper_fn):
    rows = fetch_all_rows(ds_id)
    updated = 0
    for row in rows:
        payload = mapper_fn(row)
        if payload:
            notion('PATCH', f"/pages/{row['id']}", {'properties': payload})
            updated += 1
            time.sleep(0.25)
    return len(rows), updated


def map_task(row):
    p = row.get('properties', {})
    st = TASK_STATUS_MAP.get(g(p.get('Status', {}), 'status'))
    ef = TASK_EFFORT_MAP.get(g(p.get('Effort', {}), 'select'))
    im = TASK_IMPACT_MAP.get(g(p.get('Impact', {}), 'status'))
    va = TASK_VALUE_MAP.get(g(p.get('Value', {}), 'status'))
    ur = TASK_URGENCY_MAP.get(g(p.get('Urgency', {}), 'select'))
    out = {}
    if st: out['Status (Simple)'] = {'status': {'name': st}}
    if ef: out['Effort (Simple)'] = {'select': {'name': ef}}
    if im: out['Impact (Simple)'] = {'select': {'name': im}}
    if va: out['Value (Simple)'] = {'select': {'name': va}}
    if ur: out['Urgency (Simple)'] = {'select': {'name': ur}}
    return out


def map_proj(row):
    p = row.get('properties', {})
    st = PROJ_STATUS_MAP.get(g(p.get('Status', {}), 'status'))
    va = PROJ_VALUE_MAP.get(g(p.get('Value', {}), 'select'))
    ur = PROJ_URGENCY_MAP.get(g(p.get('Urgent', {}), 'select'))
    out = {}
    if st: out['Status (Simple)'] = {'status': {'name': st}}
    if va: out['Value (Simple)'] = {'select': {'name': va}}
    if ur: out['Urgency (Simple)'] = {'select': {'name': ur}}
    return out

nt, ut = update_rows(TASKS_DS, map_task)
np, up = update_rows(PROJECTS_DS, map_proj)

print(json.dumps({'field_ops': ops, 'tasks_total': nt, 'tasks_updated': ut, 'projects_total': np, 'projects_updated': up}, indent=2))