#!/usr/bin/env hy

(import json)
(import time [sleep])
(import threading [Thread])
(import datetime [datetime])
(import subprocess [run PIPE DEVNULL])

(setv notified-tasks #{})

(defn notify-send [title desc [time 0] [actions []]]
  (let [cmd ["notify-send" title desc "-t" (str time)]]
    (for [action actions]
      (list.append cmd "-A")
      (list.append cmd action))
    (let [result (run cmd :stdout PIPE :text True)]
      (try
        (int result.stdout)
        (except [ValueError]
          None)))))

(defn get-tasks []
  (let [todo-proc (run ["todo" "--porcelain" "list"]
                       :stdout PIPE
                       :text True)]
    (json.loads todo-proc.stdout)))

(defn mark-task-done [task]
  (run ["todo" "done" (str (get task "id"))]
       :stdout DEVNULL))

(defn hash-task [task]
  (hash (+ (str (get task "start"))
           (str (get task "due"))
           (str (get task "description"))
           (str (get task "summary")))))

(defn notify-task [task timestamp message]
  (let [summary (get task "summary")
        time-obj (datetime.fromtimestamp timestamp)
        time-str (datetime.strftime time-obj "%Y/%m/%d %H:%M")
        result (notify-send
                 (if summary summary "No Summary")
                 (+ "Task " message " at " time-str)
                 :actions
                 ["Mark Done"])]
    (when (= result 0)
      (mark-task-done task))))

(defn get-notify-time-and-message [task]
  (if (is-not (get task "start") None)
    #("Starts" (get task "start"))
    #("Due" (get task "due"))))

(defn notify-tasks [tasks]
  (global notified-tasks)
  (let [now (int (datetime.timestamp (datetime.now)))
        notified #{}]
    (for [task tasks]
      (let [[message time] (get-notify-time-and-message task)]
        (when (and (not (get task "completed"))
                   (is-not time None)
                   (<= (- time now) 300))
          (let [task-hash (hash-task task)]
            (when (not-in task-hash notified-tasks)
              (Thread.start (Thread :target notify-task
                                    :args #(task time message))))
            (set.add notified task-hash)))))
    (setv notified-tasks notified)))

(while True
  (notify-tasks (get-tasks))
  (sleep 60))