(ns document-storage.postgres.documents
  (:require [document-storage.postgres.encoding :as encoding]
            [editscript.core :as escript]))


(defn get-document-frame-by-version [storage document-id version repository]
  (.run-query storage :get-document-frame-by-version
              {:repository repository
               :version version
               :document-id document-id}))

(defn get-document-frame-by-id [storage frame-id repository]
  (.run-query storage :get-document-frame
              {:repository repository
               :id frame-id}))

(defn get-last-document-frame [storage document-id repository]
  (when-let [frame (.run-query storage :get-last-document-frame
                             {:repository repository
                              :document-id document-id})]
    frame))

(defn get-last-document-version [storage document-id repository]
  (when-let [frame (get-last-document-frame storage document-id repository)]
    (:version frame)))


(defn restore-document [storage frame-id repository]
  (when-let [frame (get-document-frame-by-id storage frame-id repository)]
    (case (name (:data_type frame))
      "key" (encoding/decode-document (:data frame))
      "diff" (let [data (encoding/decode-document (:data frame))]
               (escript/patch
                       ;;;FIXME load document-frame by ID
                (restore-document storage (:reference frame) repository)
                (escript/edits->script data)))
      (throw
       (ex-info (str "Unknown data-type " (:data_type frame) " for frame " frame-id)
                {:frame-id frame-id
                 :frame frame})))))

(defn load-document
  ([storage document-id repository]
   (when-let [last-document-version (get-last-document-version storage document-id repository)]
     (load-document storage document-id last-document-version repository)))
  ([storage document-id version repository]
   (when-let [frame (get-document-frame-by-version storage document-id version repository)]
     (restore-document storage (:id frame) repository))))

(defn save-keyframe [storage document-id document last-version repository]
  (let [doc (encoding/encode-document document)
        last-version (or last-version 0)]
    ;;VALUES (:uuid, :document-id, :type, :encoding, :version, :reference, :data)
    (.run-query storage :create-document-frame!
                {:repository repository
                 :document-id document-id
                 :type :ds-type/key
                 :encoding :ds-encoding/nippy
                 :version (inc last-version)
                 :reference nil
                 :data doc})))


(defn save-pframe [storage document-id doc last-version repository]
  (let [last-doc (load-document storage document-id last-version repository)
        last-frame (get-document-frame-by-version storage document-id last-version repository)
        diff (escript/diff last-doc doc {:str-diff? false :algo :quick})
        diff-doc (encoding/encode-document (escript/get-edits diff))]
    (.run-query storage :create-document-frame!
                {:repository repository
                 :document-id document-id
                 :type :ds-type/diff
                 :encoding :ds-encoding/nippy
                 :version (inc last-version)
                 :reference (:id last-frame) ;;;FIXME save ID in reference not version
                 :data diff-doc})))

(defn save-document [storage document-id document repository options]
  (if-let [last-version (get-last-document-version storage document-id repository)]
    (if (true? (:force-keyframe options))
      (save-keyframe storage document-id document last-version repository)
      (save-pframe storage document-id document last-version repository))
    (save-keyframe storage document-id document -1 repository)))


(defn delete-document [storage document-id repository]
    (.run-query storage :create-document-frame!
                {:repository repository
                 :document-id document-id
                 :type :ds-type/delete
                 :encoding :ds-encoding/edn
                 :version (inc (get-last-document-version storage document-id repository))
                 :reference nil
                 :data ""}))