;; 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 Sucursal en España SL


(ns app.main.ui.workspace.tokens.management.group
  (:require-macros [app.main.style :as stl])
  (:require
   [app.common.data :as d]
   [app.common.data.macros :as dm]
   [app.common.types.tokens-lib :as ctob]
   [app.main.data.modal :as modal]
   [app.main.data.notifications :as ntf]
   [app.main.data.workspace.tokens.application :as dwta]
   [app.main.data.workspace.tokens.library-edit :as dwtl]
   [app.main.refs :as refs]
   [app.main.store :as st]
   [app.main.ui.context :as ctx]
   [app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
   [app.main.ui.ds.foundations.assets.icon :as i]
   [app.main.ui.ds.layers.layer-button :refer [layer-button*]]
   [app.main.ui.workspace.tokens.management.token-tree :refer [token-tree*]]
   [app.util.dom :as dom]
   [app.util.i18n :refer [tr]]
   [okulary.core :as l]
   [rumext.v2 :as mf]))

(def ref:folded-token-paths
  (l/derived (l/key :folded-token-paths) refs/workspace-tokens))

(def ref:unfolded-token-types
  (l/derived (l/key :unfolded-token-types) refs/workspace-tokens))

(defn token-section-icon
  [type]
  (case type
    :border-radius i/corner-radius
    :color i/drop
    :boolean i/boolean-difference
    :font-family i/text-font-family
    :font-size i/text-font-size
    :letter-spacing i/text-letterspacing
    :text-case i/text-mixed
    :text-decoration i/text-underlined
    :font-weight i/text-font-weight
    :typography i/text-typography
    :opacity i/percentage
    :number i/number
    :rotation i/rotation
    :spacing i/padding-extended
    :string i/text-mixed
    :stroke-width i/stroke-size
    :dimensions i/expand
    :sizing i/expand
    :shadow i/drop-shadow
    "add"))

(def ^:private schema:token-group
  [:map
   [:type :keyword]
   [:tokens :any]
   [:selected-shapes :any]
   [:is-selected-inside-layout {:optional true} [:maybe :boolean]]
   [:active-theme-tokens {:optional true} :any]
   [:selected-token-set-id {:optional true} :any]
   [:tokens-lib {:optional true} :any]
   [:on-token-pill-click {:optional true} fn?]
   [:on-context-menu {:optional true} fn?]])

(mf/defc token-group*
  {::mf/schema schema:token-group}
  [{:keys [type tokens selected-shapes is-selected-inside-layout active-theme-tokens selected-token-set-id tokens-lib selected-ids]}]
  (let [{:keys [modal title]}
        (get dwta/token-properties type)

        folded-token-paths (mf/deref ref:folded-token-paths)
        unfolded-token-types-state (mf/deref ref:unfolded-token-types)

        current-file (mf/deref refs/file)

        is-same-file-set? (and (= (:file-id unfolded-token-types-state) (:id current-file))
                               (= (:set-id unfolded-token-types-state) selected-token-set-id))
        is-type-unfolded (and is-same-file-set?
                              (contains? (set (:types unfolded-token-types-state)) type))

        editing-ref  (mf/deref refs/workspace-editor-state)
        edition      (mf/deref refs/selected-edition)
        objects      (mf/deref refs/workspace-page-objects)
        not-editing? (and (empty? editing-ref)
                          (not (and (some? edition)
                                    (= :text (:type (get objects edition))))))

        can-edit?
        (mf/use-ctx ctx/can-edit?)

        is-selected-inside-layout (d/nilv is-selected-inside-layout false)

        tokens
        (mf/with-memo [tokens]
          (vec (sort-by :name tokens)))
        expandable? (d/nilv (seq tokens) false)

        on-pill-context-menu
        (mf/use-fn
         (mf/deps active-theme-tokens)
         (fn [event token]
           (dom/prevent-default event)
           (let [resolved-token (get active-theme-tokens (:name token))]
             (st/emit! (dwtl/assign-token-context-menu
                        {:type :token
                         :position (dom/get-client-position event)
                         :errors (:errors resolved-token)
                         :token-id (:id token)})))))

        on-node-context-menu
        (mf/use-fn
         (fn [event node]
           (dom/prevent-default event)
           (st/emit! (dwtl/assign-token-node-context-menu
                      {:node node
                       :type type
                       :position (dom/get-client-position event)}))))

        on-toggle-open-click
        (mf/use-fn
         (mf/deps type expandable?)
         (fn []
           (when expandable?
             (st/emit! (dwtl/toggle-token-type type)))))

        on-popover-open-click
        (mf/use-fn
         (mf/deps type title modal)
         (fn [event]
           (dom/stop-propagation event)
           (st/emit!
            (let [pos (dom/get-client-position event)]
              (modal/show (:key modal)
                          {:x (:x pos)
                           :y (:y pos)
                           :position :right
                           :fields (:fields modal)
                           :title title
                           :action "create"
                           :token-type type})))))

        on-token-pill-click
        (mf/use-fn
         (mf/deps not-editing? selected-ids tokens-lib selected-token-set-id)
         (fn [event token]
           (let [token (ctob/get-token tokens-lib selected-token-set-id (:id token))]
             (dom/stop-propagation event)
             ;; Number tokens can't be applied via button click
             (when (not= (:type token) :number)
               (if (and not-editing? (seq selected-shapes))
                 (st/emit! (dwta/toggle-token {:token token
                                               :shape-ids selected-ids}))
                 (when (seq selected-shapes)
                   (st/emit! (ntf/show {:content (tr "workspace.tokens.error-text-edition")
                                        :type :toast
                                        :level :warning
                                        :timeout 3000}))))))))]

    (mf/use-effect
     (fn []
       (st/emit! (dwtl/restore-unfolded-token-types))))

    [:div {:class (stl/css :token-section-wrapper)
           :data-testid (dm/str "section-" (name type))}
     [:> layer-button* {:label title
                        :expanded is-type-unfolded
                        :description (when expandable? (dm/str (count tokens)))
                        :is-expandable expandable?
                        :aria-expanded is-type-unfolded
                        :aria-controls (dm/str "token-tree-" (name type))
                        :on-toggle-expand on-toggle-open-click
                        :icon (token-section-icon type)}
      (when can-edit?
        [:> icon-button* {:id (str "add-token-button-" title)
                          :icon "add"
                          :aria-label (tr "workspace.tokens.add-token" title)
                          :variant "ghost"
                          :on-click on-popover-open-click
                          :class (stl/css :token-section-icon)}])]
     (when is-type-unfolded
       [:> token-tree* {:tokens tokens
                        :type type
                        :folded-token-paths folded-token-paths
                        :selected-shapes selected-shapes
                        :is-selected-inside-layout is-selected-inside-layout
                        :active-theme-tokens active-theme-tokens
                        :selected-token-set-id selected-token-set-id
                        :tokens-lib tokens-lib
                        :on-token-pill-click on-token-pill-click
                        :on-pill-context-menu on-pill-context-menu
                        :on-node-context-menu on-node-context-menu}])]))
