;; handlers/SessionAPIHandler.fnl
;; Provides a RequestHandler that handles service logic for the /session API endpoint

;; -------------------------------------------------------------------------- ;;
;; Dependencies

(local basexx (require :basexx))
(local turbo (require :turbo))
(local otp (require :otp))

;; -------------------------------------------------------------------------- ;;
;; Local modules

(local auth_utils (require :modules.auth_utils))
(local config (require :modules.config))
(local sessions_db (require :modules.sessions_db))

;; -------------------------------------------------------------------------- ;;
;; Helper functions

(fn generate_cookie_header [name value expire_hours]
  "Return a header string containing a cookie definition.
Cookie will have name `name` and value `value`, and will be set to expire in `expire_hours` hours."
  (let [max_age_seconds (* expire_hours 60 60)]
    (string.format "%s=%s; Path=/; Max-Age=%d; HttpOnly; %s"
                   (turbo.escape.escape name)
                   (turbo.escape.escape value)
                   max_age_seconds
                   (if config.use_secure_cookies
                       "SameSite=Strict; Secure;"
                       "SameSite=Lax;"))))

;; -------------------------------------------------------------------------- ;;
;; Handler definition

(local SessionAPIHandler (class "SessionAPIHandler" turbo.web.RequestHandler))

;; ---------------------------------- ;;
;; GET

(fn SessionAPIHandler.get [self]
  "Inform the client of the status of their session.
Responds with 204 No Content if there is an active session.
Responds with 401 Unauthorized if there is no active session."
  (let [session_id (self:get_cookie "session_id")]
    (if (and session_id
             (sessions_db.is_session_active? session_id))
        (self:set_status 204)
        (self:set_status 401))))

;; ---------------------------------- ;;
;; POST

(fn SessionAPIHandler.post [self]
  "Start a new session for the client after verifying password (provided in POST body).
Responds with 200 OK and a cookie containing the new session ID on success.
Responds with 500 Internal Server Error on failure.
Responds with 401 Unauthorized if an incorrect password was provided."
  (let [password (self:get_argument "password" nil true)]
    (if (auth_utils.verify_password password)
        (let [session_id (sessions_db.start_session)]
          (if session_id
              (self:add_header "Set-Cookie" (generate_cookie_header "session_id" session_id config.session_max_length_hours))
              (self:set_status 500)))
        (self:set_status 401))))

;; ---------------------------------- ;;
;; DELETE

(fn SessionAPIHandler.delete [self]
  "End the client's current active session, if there is one.
Responds with 205 Reset Content on success.
Responds with 500 Internal Server Error on failure.
Responds with 404 Not Found if there is no active session."
  (let [session_id (self:get_cookie "session_id")]
    (if session_id
        (if (sessions_db.end_session session_id)
            (self:set_status 205)
            (self:set_status 500))
        (self:set_status 404))))

;; -------------------------------------------------------------------------- ;;
;; Return handler

SessionAPIHandler
