;; lib/dream/auth.sx — Dream-on-SX authentication helpers.
;; HTTP Basic auth (with a pure-SX base64 codec) and Bearer-token guards.
;; Depends on types.sx.

;; ── base64 (pure SX; arithmetic, no bitwise) ───────────────────────
(define
  dr/b64-alpha
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
(define dr/b64-char (fn (n) (char-at dr/b64-alpha n)))
(define dr/b64-index (fn (c) (index-of dr/b64-alpha c)))

(define
  dr/b64-encode-loop
  (fn
    (s i n acc)
    (if
      (>= i n)
      acc
      (let
        ((b0 (char-code (char-at s i))) (rem (- n i)))
        (cond
          ((>= rem 3)
            (let
              ((triple (+ (* b0 65536) (* (char-code (char-at s (+ i 1))) 256) (char-code (char-at s (+ i 2))))))
              (dr/b64-encode-loop
                s
                (+ i 3)
                n
                (str
                  acc
                  (dr/b64-char (mod (quotient triple 262144) 64))
                  (dr/b64-char (mod (quotient triple 4096) 64))
                  (dr/b64-char (mod (quotient triple 64) 64))
                  (dr/b64-char (mod triple 64))))))
          ((= rem 2)
            (let
              ((triple (+ (* b0 65536) (* (char-code (char-at s (+ i 1))) 256))))
              (str
                acc
                (dr/b64-char (mod (quotient triple 262144) 64))
                (dr/b64-char (mod (quotient triple 4096) 64))
                (dr/b64-char (mod (quotient triple 64) 64))
                "=")))
          (else
            (let
              ((triple (* b0 65536)))
              (str
                acc
                (dr/b64-char (mod (quotient triple 262144) 64))
                (dr/b64-char (mod (quotient triple 4096) 64))
                "=="))))))))

(define
  dream-base64-encode
  (fn (s) (dr/b64-encode-loop s 0 (string-length s) "")))

(define
  dr/b64-decode-loop
  (fn
    (s i n acc)
    (if
      (>= i n)
      acc
      (let
        ((p2 (char-at s (+ i 2)))
          (p3 (char-at s (+ i 3))))
        (let
          ((c0 (dr/b64-index (char-at s i)))
            (c1 (dr/b64-index (char-at s (+ i 1))))
            (c2 (if (= p2 "=") 0 (dr/b64-index p2)))
            (c3 (if (= p3 "=") 0 (dr/b64-index p3))))
          (let
            ((triple (+ (* c0 262144) (* c1 4096) (* c2 64) c3)))
            (dr/b64-decode-loop
              s
              (+ i 4)
              n
              (str
                acc
                (char-from-code
                  (mod (quotient triple 65536) 256))
                (if
                  (= p2 "=")
                  ""
                  (char-from-code
                    (mod (quotient triple 256) 256)))
                (if (= p3 "=") "" (char-from-code (mod triple 256)))))))))))

(define
  dream-base64-decode
  (fn
    (s)
    (if (= s "") "" (dr/b64-decode-loop s 0 (string-length s) ""))))

;; ── Authorization header parsing ───────────────────────────────────
(define dream-authorization (fn (req) (dream-header req "authorization")))

(define
  dream-bearer-token
  (fn
    (req)
    (let
      ((a (dream-authorization req)))
      (if (and a (starts-with? a "Bearer ")) (substr a 7) nil))))

(define
  dream-basic-credentials
  (fn
    (req)
    (let
      ((a (dream-authorization req)))
      (if
        (and a (starts-with? a "Basic "))
        (let
          ((decoded (dream-base64-decode (substr a 6))))
          (let
            ((colon (index-of decoded ":")))
            (if (< colon 0) nil {:pass (substr decoded (+ colon 1)) :user (substr decoded 0 colon)})))
        nil))))

;; ── Basic auth middleware ──────────────────────────────────────────
;; check is (fn (user pass) -> bool). On success the request gains :dream-user.
(define
  dr/www-authenticate
  (fn
    (realm)
    (dream-add-header
      (dream-response 401 {:content-type "text/plain; charset=utf-8"} "Unauthorized")
      "www-authenticate"
      (str "Basic realm=\"" realm "\""))))

(define
  dream-basic-auth
  (fn
    (realm check)
    (fn
      (next)
      (fn
        (req)
        (let
          ((creds (dream-basic-credentials req)))
          (if
            (and creds (check (get creds :user) (get creds :pass)))
            (next (assoc req :dream-user (get creds :user)))
            (dr/www-authenticate realm)))))))

(define dream-user (fn (req) (get req :dream-user)))

;; ── Bearer-token middleware ────────────────────────────────────────
;; check is (fn (token) -> principal | nil). On success the request gains
;; :dream-principal. Missing/invalid -> 401.
(define
  dream-require-bearer
  (fn
    (check)
    (fn
      (next)
      (fn
        (req)
        (let
          ((tok (dream-bearer-token req)))
          (let
            ((principal (if tok (check tok) nil)))
            (if
              (nil? principal)
              (dream-add-header
                (dream-response 401 {:content-type "text/plain; charset=utf-8"} "Unauthorized")
                "www-authenticate"
                "Bearer")
              (next (assoc req :dream-principal principal)))))))))

(define dream-principal (fn (req) (get req :dream-principal)))
