   (:require [futura.atomic :as atomic]
             [buddy.core.nonce :as nonce]
             [buddy.core.codecs :as codecs]
-            [catacumba.core :as ct])
+            [catacumba.impl.handlers :as handlers]
+            [catacumba.impl.context :as context])
   (:import clojure.lang.IAtom
            clojure.lang.IDeref
            clojure.lang.Counted

  (^:private modified? [_] "Check if session is modified"))

(defprotocol ISessionStorage
  (^:private load-data [_ key] "")
  (^:private persist-data [_ key data] ""))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Types

(defn- context->session
  [context {:keys [storage cookie-name]
            :or {cookie-name default-cookie-name}}]
  (let [cookies (handlers/get-cookies context)
        cookie (get cookies (keyword cookie-name) nil)
        sid (:value cookie)]
    (if sid
      (let [data (read-session storage sid)]
        [sid (->session sid data)])
      (let [sid (codecs/bytes->safebase64 (nonce/random-nonce 48))]
        [sid (->session sid)]))))

(defn session
  ([] (session {}))
  ([{:keys [storage cookie-name]
     :or {storage :inmemory cookie-name default-cookie-name}
     :as options}]
   (let [storage (lookup-storage storage)
         options (assoc options :storage storage)]
     (fn [context]
       (let [[sid session] (context->session context options)]
         (context/before-send context (fn [^ResponseMetaData response]
                                        (cond
                                          (empty? session)
                                          (let [cookie (-> (make-cookie sid options)
                                                           (assoc :max-age 0))]
                                            (handlers/set-cookies! context {cookie-name cookie}))

                                          (modified? session)
                                          (let [cookie (make-cookie sid options)]
                                            (write-session storage sid @session)
                                            (handlers/set-cookies! context {cookie-name cookie})))))
         (context/delegate context {:session session}))))))
