;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) KALEIDOS INC

(ns app.util.storage
  (:require
   [app.common.exceptions :as ex]
   [app.common.transit :as t]
   [app.util.globals :as g]
   [app.util.timers :as tm]))

(defn- persist
  [storage prev curr]
  (run! (fn [key]
          (let [prev* (get prev key)
                curr* (get curr key)]
            (when (not= curr* prev*)
              (tm/schedule-on-idle
               #(if (some? curr*)
                  (.setItem ^js storage (t/encode-str key) (t/encode-str curr*))
                  (.removeItem ^js storage (t/encode-str key)))))))

        (into #{} (concat (keys curr)
                          (keys prev)))))

(defn- load
  [storage]
  (when storage
    (let [len (.-length ^js storage)]
      (reduce (fn [res index]
                (let [key (.key ^js storage index)
                      val (.getItem ^js storage key)]
                  (try
                    (assoc res (t/decode-str key) (t/decode-str val))
                    (catch :default _e
                      res))))
              {}
              (range len)))))

;; Using ex/ignoring because can receive a DOMException like this when importing the code as a library:
;; Failed to read the 'localStorage' property from 'Window': Storage is disabled inside 'data:' URLs.
(defonce storage (atom (load (ex/ignoring (unchecked-get g/global "localStorage")))))

(add-watch storage :persistence #(persist js/localStorage %3 %4))
