(ns logseq.outliner.page-test
  (:require [cljs.test :refer [deftest is testing]]
            [datascript.core :as d]
            [logseq.common.config :as common-config]
            [logseq.common.util.page-ref :as page-ref]
            [logseq.db :as ldb]
            [logseq.db.frontend.db :as db-db]
            [logseq.db.test.helper :as db-test]
            [logseq.outliner.page :as outliner-page]))

(deftest create-class
  (let [conn (db-test/create-conn)
        _ (outliner-page/create! conn "movie" {:class? true})
        _ (outliner-page/create! conn "Movie" {:class? true})
        movie-class (ldb/get-case-page @conn "movie")
        Movie-class (ldb/get-case-page @conn "Movie")]

    (is (ldb/class? movie-class) "Creates a class")
    (is (ldb/class? Movie-class) "Creates another class with a different case sensitive name")
    (is (not= movie-class Movie-class) "The two classes are not the same")))

(deftest create-namespace-pages
  (let [conn (db-test/create-conn-with-blocks
              {:properties {:user.property/property1 {:logseq.property/type :default}}
               :classes {:class1 {}}
               :pages-and-blocks [{:page {:block/title "page1"}}]})]

    (testing "Basic valid workflows"
      (let [[_ child-uuid] (outliner-page/create! conn "foo/bar/baz" {:split-namespace? true})
            child-page (d/entity @conn [:block/uuid child-uuid])
            ;; Create a 2nd child page using existing parent pages
            [_ child-uuid2] (outliner-page/create! conn "foo/bar/baz2" {:split-namespace? true})
            child-page2 (d/entity @conn [:block/uuid child-uuid2])
            ;; Create a child page for a class
            [_ child-uuid3] (outliner-page/create! conn "c1/c2" {:split-namespace? true :class? true})
            child-page3 (d/entity @conn [:block/uuid child-uuid3])
            library (ldb/get-built-in-page @conn common-config/library-page-name)
            bar (ldb/get-page @conn "bar")]
        (is (= ["foo"] (map :block/title (:block/_parent library)))
            "Namespace (non-class) pages are added to the Library page")
        (is (= ["baz" "baz2"] (map :block/title (:block/_parent bar)))
            "Child pages are created under the same parent")
        (is (= ["foo" "bar"] (map :block/title [(:block/parent (:block/parent child-page))
                                                (:block/parent child-page)]))
            "Child page with new parent has correct parents")
        (is (= (map :block/uuid (db-db/get-page-parents child-page))
               (map :block/uuid (db-db/get-page-parents child-page2)))
            "Child page with existing parents has correct parents")
        (is (= #{"Root Tag" "c1"} (set (map :block/title (ldb/get-classes-parents [child-page3]))))
            "Child class with new parent has correct parents")

        (outliner-page/create! conn "foo/class1/baz3" {:split-namespace? true})
        (is (= #{"Tag" "Page"}
               (set (d/q '[:find [?tag-title ...]
                           :where
                           [?b :block/title "class1"]
                           [?b :block/tags ?t]
                           [?t :block/title ?tag-title]] @conn)))
            "Using an existing class page in a multi-parent namespace doesn't allow a page to have a class parent and instead creates a new page")))

    (testing "Child pages with same name and different parents"
      (let [_ (outliner-page/create! conn "vim/keys" {:split-namespace? true})
            _ (outliner-page/create! conn "emacs/keys" {:split-namespace? true})]
        (is (= #{"vim" "emacs"}
               (->> (d/q '[:find [(pull ?b [{:block/parent [:block/title]}]) ...] :where [?b :block/title "keys"]] @conn)
                    (map #(get-in % [:block/parent :block/title]))
                    set))
            "Two child pages with same name exist and have different parents")))

    (testing "Invalid workflows"
      (is (thrown-with-msg?
           js/Error
           #"Cannot create"
           (outliner-page/create! conn "class1/page" {:split-namespace? true}))
          "Page can't have a class parent")
      (is (thrown-with-msg?
           js/Error
           #"Cannot create"
           (outliner-page/create! conn "property1/page" {:split-namespace? true}))
          "Page can't have a property parent")
      (is (thrown-with-msg?
           js/Error
           #"Cannot create"
           (outliner-page/create! conn "property1/class" {:split-namespace? true :class? true}))
          "Class can't have a property parent"))))

(deftest create-page
  (let [conn (db-test/create-conn)
        [_ page-uuid] (outliner-page/create! conn "fooz" {})]
    (is (= "fooz" (:block/title (d/entity @conn [:block/uuid page-uuid])))
        "Page created correctly")

    (is (thrown-with-msg?
         js/Error
         #"can't include \"/"
         (outliner-page/create! conn "foo/bar" {}))
        "Page can't have '/'n title")))

(deftest delete-page
  (let [conn (db-test/create-conn-with-blocks
              [{:page {:block/title "D1"}
                :blocks [{:block/title "b1"}]}])
        d1 (ldb/get-page @conn "D1")
        b1 (db-test/find-block-by-content @conn "b1")]
    (ldb/transact! conn [{:db/id (:db/id b1)
                          :block/title (str "b1 " (page-ref/->page-ref (:block/uuid d1)))
                          :block/refs #{(:db/id d1)}}])
    (is (contains? (set (map :db/id (:block/refs (d/entity @conn (:db/id b1)))))
                   (:db/id d1)))
    (outliner-page/delete! conn (:block/uuid d1))
    (is (nil? (d/entity @conn (:db/id d1))))
    (is (nil? (d/entity @conn (:db/id b1))))))

(deftest create-journal
  (let [conn (db-test/create-conn)
        [_ page-uuid] (outliner-page/create! conn "Dec 16th, 2024" {})]

    (is (= "Dec 16th, 2024" (:block/title (d/entity @conn [:block/uuid page-uuid])))
        "Journal created correctly")

    (is (= [:logseq.class/Journal]
           (->> (d/entity @conn [:block/uuid page-uuid])
                :block/tags
                (map #(:db/ident (d/entity @conn (:db/id %))))))
        "New journal only has Journal tag")))
