;; 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 frontend-tests.logic.copying-and-duplicating-test
  (:require
   [app.common.test-helpers.components :as cthc]
   [app.common.test-helpers.compositions :as ctho]
   [app.common.test-helpers.files :as cthf]
   [app.common.test-helpers.ids-map :as cthi]
   [app.common.test-helpers.shapes :as cths]
   [app.common.uuid :as uuid]
   [app.main.data.workspace :as dw]
   [app.main.data.workspace.colors :as dc]
   [app.main.data.workspace.libraries :as dwl]
   [app.main.data.workspace.selection :as dws]
   [cljs.test :as t :include-macros true]
   [frontend-tests.helpers.pages :as thp]
   [frontend-tests.helpers.state :as ths]))

(t/use-fixtures :each
  {:before thp/reset-idmap!})

;; Related .penpot file: common/test/cases/copying-and-duplicating.penpot
(defn- setup-file []
  (-> (cthf/sample-file :file1 :page-label :page-1)
      (ctho/add-simple-component :simple-1 :frame-simple-1 :rect-simple-1 :child-params {:type :rect :fills (cths/sample-fills-color :fill-color "#2152e5") :name "rect-simple-1"})

      (ctho/add-frame :frame-composed-1 :name "frame-composed-1")
      (cthc/instantiate-component :simple-1 :copy-simple-1 :parent-label :frame-composed-1 :children-labels [:composed-1-simple-1])
      (cths/add-sample-shape :rect-composed-1 :parent-label :frame-composed-1 :fills (cths/sample-fills-color :fill-color "#B1B2B5"))
      (cthc/make-component :composed-1 :frame-composed-1)

      (ctho/add-frame :frame-composed-2 :name "frame-composed-2")
      (cthc/instantiate-component :composed-1 :copy-composed-1-composed-2 :parent-label :frame-composed-2 :children-labels [:composed-1-composed-2])
      (cthc/make-component :composed-2 :frame-composed-2)

      (cthc/instantiate-component :composed-2 :copy-composed-2)

      (ctho/add-frame :frame-composed-3 :name "frame-composed-3")
      (ctho/add-group :group-3 :parent-label :frame-composed-3)
      (cthc/instantiate-component :composed-2 :copy-composed-1-composed-3 :parent-label :group-3 :children-labels [:composed-1-composed-2])
      (cths/add-sample-shape :circle-composed-3 :parent-label :group-3 :fills (cths/sample-fills-color :fill-color "#B1B2B5"))
      (cthc/make-component :composed-3 :frame-composed-3)

      (cthc/instantiate-component :composed-3 :copy-composed-3 :children-labels [:composed-2-composed-3])
      (cthf/add-sample-page :page-2)
      (cthf/switch-to-page :page-1)))


(defn- copy-paste-shape
  [id file & {:keys [target-page-label target-container-id]}]
  (let [features            #{"components/v2"}
        version             46
        page                (cthf/current-page file)
        target-page-id      (cthi/id target-page-label)
        shape               (if (keyword? id)
                              (cths/get-shape file id)
                              (cths/get-shape-by-id file id))
        pdata               (thp/simulate-copy-shape #{(:id shape)} (:objects page) {(:id  file) file} page file features version)
        target-container-id (or target-container-id (:parent-id shape))]

    (filter some?
            [(when target-page-id (dw/initialize-page target-page-id))
             (dws/select-shape target-container-id)
             (dw/paste-shapes pdata)
             (when target-page-id (dw/initialize-page (:id page)))])))

(defn- sync-file [file]
  (map (fn [component-tag]
         (->> component-tag
              (cthc/get-component file)
              :component-id
              (dwl/sync-file (:id file) (:id file) :components)))
       [:simple-1 :composed-1 :composed-2 :composed-3]))

(defn- set-color-bottom-shape [label file color]
  (let [shape (ctho/bottom-shape file label)]
    (concat
     [(dws/select-shape (:id shape))
      (dc/apply-color-from-palette color false)]
     (sync-file file))))

(defn- count-shapes [file name color]
  (let [page (cthf/current-page file)]
    (->> (vals (:objects page))
         (filter #(and
                   (= (:name %) name)
                   (-> (cths/get-shape-by-id file (:id %))
                       :fills
                       first
                       :fill-color
                       (= color))))
         (count))))

(defn- duplicate-each-main-and-first-level-copy [file]
  (concat (copy-paste-shape :frame-simple-1 file)
          (copy-paste-shape :frame-simple-1 file)
          (copy-paste-shape :frame-composed-1 file)
          (copy-paste-shape :frame-composed-1 file)
          (copy-paste-shape :frame-composed-2 file)
          (copy-paste-shape :frame-composed-2 file)
          (copy-paste-shape :frame-composed-3 file)
          (copy-paste-shape :frame-composed-3 file)
          (copy-paste-shape :copy-composed-2 file)
          (copy-paste-shape :copy-composed-2 file)
          (copy-paste-shape :copy-composed-3 file)
          (copy-paste-shape :copy-composed-3 file)))

(defn- duplicate-simple-nested-in-main-and-group [file]
  (concat (copy-paste-shape :copy-simple-1 file)
          (copy-paste-shape :copy-simple-1 file)
          (copy-paste-shape :group-3 file)
          (copy-paste-shape :group-3 file)))

(defn- duplicate-copy-nested-and-group-out-of-the-main
  [file & {:keys [target-page-label]}]
  (let [page (cthf/current-page file)
        frame-1-instance-ids (->> (vals (:objects page))
                                  (filter #(and
                                            (or
                                             (= (:name %) "Frame1")
                                             (= (:name %) "Group1"))
                                            (not (:component-root %))))
                                  (map :id))]
    (concat
     (apply concat (mapv #(copy-paste-shape % file :target-page-label target-page-label :target-container-id uuid/zero) frame-1-instance-ids))
     (apply concat (mapv #(copy-paste-shape % file :target-page-label target-page-label :target-container-id uuid/zero) frame-1-instance-ids)))))

(t/deftest main-and-first-level-copy-1
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)
         ;; ==== Action


         ;; For each main and first level copy:
         ;; - Duplicate it two times with copy-paste.
            events
            (concat
             (duplicate-each-main-and-first-level-copy file)
           ;; - Change color of Simple1
             (set-color-bottom-shape :frame-simple-1 file {:color "#111111"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file'         (ths/get-file-from-store new-state)]
             (t/is (= (count-shapes file' "rect-simple-1" "#111111") 18)))))))))

(t/deftest main-and-first-level-copy-2
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)
         ;; ==== Action


         ;; For each main and first level copy:
         ;; - Duplicate it two times with copy-paste.
            events
            (concat
             (duplicate-each-main-and-first-level-copy file)
           ;; - Change color of Simple1
             (set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
            ;; - Change color of the nearest main and check propagation to duplicated.
             (set-color-bottom-shape :frame-composed-1 file {:color "#222222"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file'         (ths/get-file-from-store new-state)]
             (t/is (= (count-shapes file' "rect-simple-1" "#222222") 15)))))))))

(t/deftest main-and-first-level-copy-3
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)
         ;; ==== Action


         ;; For each main and first level copy:
         ;; - Duplicate it two times with copy-paste.
            events
            (concat
             (duplicate-each-main-and-first-level-copy file)
           ;; - Change color of Simple1
             (set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
            ;; - Change color of the nearest main and check propagation to duplicated.
             (set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
             (set-color-bottom-shape :frame-composed-2 file {:color "#333333"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file'         (ths/get-file-from-store new-state)]
             (t/is (= (count-shapes file' "rect-simple-1" "#333333") 12)))))))))


(t/deftest main-and-first-level-copy-4
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)
         ;; ==== Action


         ;; For each main and first level copy:
         ;; - Duplicate it two times with copy-paste.
            events
            (concat
             (duplicate-each-main-and-first-level-copy file)
           ;; - Change color of Simple1
             (set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
            ;; - Change color of the nearest main and check propagation to duplicated.
             (set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
             (set-color-bottom-shape :frame-composed-2 file {:color "#333333"})
             (set-color-bottom-shape :frame-composed-3 file {:color "#444444"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file'         (ths/get-file-from-store new-state)]
             (t/is (= (count-shapes file' "rect-simple-1" "#444444") 6)))))))))

(t/deftest copy-nested-in-main-1
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)

            ;; ==== Action
            ;; For each copy of Simple1 nested in a main, and the group inside Composed3 main:
            ;; - Duplicate it two times, keeping the duplicated inside the same main.
            events
            (concat
             (duplicate-simple-nested-in-main-and-group file)
              ;; - Change color of Simple1
             (set-color-bottom-shape :frame-simple-1 file {:color "#111111"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file' (ths/get-file-from-store new-state)]
              ;; Check propagation to all copies.
             (t/is (= (count-shapes file' "rect-simple-1" "#111111") 28)))))))))

(t/deftest copy-nested-in-main-2
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)

            ;; ==== Action
            ;; For each copy of Simple1 nested in a main, and the group inside Composed3 main:
            ;; - Duplicate it two times, keeping the duplicated inside the same main.
            events
            (concat
             (duplicate-simple-nested-in-main-and-group file)
              ;; - Change color of the nearest main
             (set-color-bottom-shape :frame-composed-1 file {:color "#222222"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file' (ths/get-file-from-store new-state)]
              ;; Check propagation to duplicated.
             (t/is (= (count-shapes file' "rect-simple-1" "#222222") 9)))))))))

(t/deftest copy-nested-in-main-3
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)

            ;; ==== Action
            ;; For each copy of Simple1 nested in a main, and the group inside Composed3 main:
            ;; - Duplicate it two times, keeping the duplicated inside the same main.
            events
            (concat
             (duplicate-simple-nested-in-main-and-group file)
              ;; - Change color of the copy you duplicated from.
             (set-color-bottom-shape :group-3 file {:color "#333333"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file' (ths/get-file-from-store new-state)]
              ;; Check that it's NOT PROPAGATED.
             (t/is (= (count-shapes file' "rect-simple-1" "#333333") 2)))))))))

(t/deftest copy-nested-1
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)

            ;; ==== Action
            ;; For each copy of Simple1 nested in a main or other copy, and the group inside Composed3
            ;; main and copy:
            ;;   - Duplicate it two times, moving the duplicates out of the main.
            events
            (concat
             (duplicate-copy-nested-and-group-out-of-the-main file)
              ;; - Change color of Simple1
             (set-color-bottom-shape :frame-simple-1 file {:color "#111111"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file' (ths/get-file-from-store new-state)]
              ;; Check propagation to all copies.
             (t/is (= (count-shapes file' "rect-simple-1" "#111111") 20)))))))))


(t/deftest copy-nested-2
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)

            ;; ==== Action
            ;; For each copy of Simple1 nested in a main or other copy, and the group inside Composed3
            ;; main and copy:
            ;;   - Duplicate it two times, moving the duplicates out of the main.
            events
            (concat
             (duplicate-copy-nested-and-group-out-of-the-main file)
              ;; - Change color of Simple1
             (set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
              ;; - Change color of the previous main
             (set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
             (set-color-bottom-shape :group-3 file {:color "#333333"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file' (ths/get-file-from-store new-state)]
              ;; Check that it's NOT PROPAGATED.
             (t/is (= (count-shapes file' "rect-simple-1" "#111111") 11))
             (t/is (= (count-shapes file' "rect-simple-1" "#222222") 7))
             (t/is (= (count-shapes file' "rect-simple-1" "#333333") 2)))))))))


(t/deftest copy-nested-3
  (t/async
    done
    (with-redefs [uuid/next cthi/next-uuid]
      (let [;; ==== Setup
            file     (setup-file)
            store    (ths/setup-store file)

            ;; ==== Action
            ;; For each copy of Simple1 nested in a main or other copy, and the group inside Composed3
            ;; main and copy:
            ;;   - Duplicate it two times, moving the duplicates to another page
            events
            (concat
             (duplicate-copy-nested-and-group-out-of-the-main file :target-page-label :page-2)
              ;; - Change color of Simple1
             (set-color-bottom-shape :frame-simple-1 file {:color "#111111"})
              ;; - Change color of the previous main
             (set-color-bottom-shape :frame-composed-1 file {:color "#222222"})
             (set-color-bottom-shape :group-3 file {:color "#333333"}))]

        (ths/run-store
         store done events
         (fn [new-state]
           (let [file' (-> (ths/get-file-from-store new-state)
                           (cthf/switch-to-page :page-2))]
              ;; Check that it's NOT PROPAGATED.
             (t/is (= (count-shapes file' "rect-simple-1" "#111111") 10))
             (t/is (= (count-shapes file' "rect-simple-1" "#222222") 4))
             (t/is (= (count-shapes file' "rect-simple-1" "#333333") 0)))))))))