(in-package #:logbot)


(defun get-and-purge-outbox-messages (db)
  (postmodern:with-connection db
    (postmodern:query
     "with deleted as (
          delete from outbox
          returning target, message, queued_at
      )
      select target,
             message
      from deleted
      order by queued_at"
     :rows)))

(defun make-log-entry (db target message host source user)
  (postmodern:with-connection db
    (postmodern:execute
     "insert into log (target, message, host, source, \"user\")
      values ($1, $2, $3, $4, $5)"
     target
     message
     (if (string= "" host) :null host)
     source
     (if (null user) :null user))))


(defclass logbot (ircbot)
  ((pg-thread :accessor logbot-pg-thread :initform nil)
   (db :reader logbot-db :initarg :db)))

(defmethod ircbot-connect :after ((bot logbot))
  (let ((conn (ircbot-connection bot)))    
    (add-hook conn 'irc-mode-message (lambda (message)
                                       (logbot-check-mode bot message)))
    (add-hook conn 'irc-privmsg-message (lambda (message)
                                          (destructuring-bind (target message-text) (arguments message)
                                            (make-log-entry (logbot-db bot)
                                                            target
                                                            message-text
                                                            (host message)
                                                            (source message)
                                                            (user message)))))))

(defmethod ircbot-send-message :after ((bot logbot) target message-text)
  (let* ((b-connection (ircbot-connection bot))
         (b-user (user b-connection)))
    (make-log-entry (logbot-db bot)
                    target
                    message-text
                    (hostname b-user)
                    (nickname b-user)
                    (username b-user))))

(defmethod logbot-check-mode ((bot logbot) message)
  (if (= 3 (length (arguments message)))
      (destructuring-bind (channel mode nick) (arguments message)
        (when (and (string= (host message) "services.")
                   (string= channel (ircbot-channel bot))
                   (or (string= mode "+o") (string= mode "+v"))
                   (string= nick (ircbot-nick bot)))

          (when (null (logbot-pg-thread bot))
            (logbot-start-pg-thread bot)
            (logbot-send-outbox bot))))))

(defmethod logbot-send-outbox ((bot logbot))
  (loop
     for (target message)
     in (get-and-purge-outbox-messages (logbot-db bot))
     do (ircbot-send-message bot target message)))

(defmethod logbot-start-pg-thread ((bot logbot))
  (setf (logbot-pg-thread bot)
        (sb-thread:make-thread
         (lambda ()
           (postmodern:with-connection (logbot-db bot)
             (postmodern:execute "listen outbox_new_message")
             (loop
                (if (string= (cl-postgres:wait-for-notification postmodern:*database*)
                             "outbox_new_message")
                    (logbot-send-outbox bot)))))
         :name "logbot-pg")))

(defun make-logbot (server port nick password channel db)
  (make-instance 'logbot
                 :server server
                 :port port
                 :nick nick
                 :password password
                 :channel channel
                 :db db))
