#|

src/web/sse.lisp

HTMX Server Sent Events

|#

(in-package #:cl-user)

(defpackage #:cl-deck-builder2.web.sse
  (:use #:cl)
  (:import-from #:websocket-driver
                #:make-client
                #:make-server
                #:on
                #:send
                #:start-connection
                #:close-connection)
  (:import-from #:cl-deck-builder2.i18n
                #:_)
  (:local-nicknames (#:v #:org.shirakumo.verbose))
  (:export #:make-chat-client
           #:make-chat-server
           #:message))

(in-package #:cl-deck-builder2.web.sse)

;; TODO (defclass chat-room () ())
(defvar *connections* (make-hash-table))

(defvar *chat-handler* nil
  "The WebSocket client on the server side.")

(defun handle-new-connection (con)
  (setf (gethash con *connections*)
        ;; (format nil "user-~a" (random 100000))
        (princ-to-string (gensym "USER-"))))

(defun broadcast-to-room (connection message)
  (let ((message (format nil "<p>~a: ~a</p>"
                         (gethash connection *connections*)
                         message)))
    (loop :for con :being :the :hash-key :of *connections* :do
      (send con message))))

(defun handle-close-connection (connection)
  (let ((message (format nil "<p>... ~a disconnected.</p>"
                         (gethash connection *connections*))))
    (loop :for con :being :the :hash-key :of *connections* :do
      (send con message))))

(defun make-chat-server (env)
  (handler-case
      (let ((ws (make-server env)))
        (on :open ws
            (lambda () (handle-new-connection ws)))

        (on :message ws
            (lambda (msg) (broadcast-to-room ws msg)))

        (on :close ws
            (lambda (&key code reason)
              (declare (ignore code reason))
              (handle-close-connection ws)))

        (lambda (responder)
          (declare (ignore responder))
          (start-connection ws)))
    (error (e)
      (declare (ignore e))
      (_ "Something went wrong. Try again?"))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun make-chat-client (&key (address "localhost") (port 5001) (endpoint "chat-server") &allow-other-keys)
  (let ((url (format nil "ws://~a:~d/~a" address port endpoint)))
    (when *chat-handler*
      (close-connection *chat-handler*)
      (setf *chat-handler* nil))
    (setf *chat-handler* 
          (start-connection
           (make-client url)))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun message (message)
  (unless *chat-handler*
    (make-chat-client))
  (send *chat-handler* message))
