;; 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
  (:require
   [app.common.data.macros :as dm]
   [app.common.logging :as log]
   [app.common.time :as ct]
   [app.common.transit :as t]
   [app.common.types.objects-map]
   [app.config :as cf]
   [app.main.data.auth :as da]
   [app.main.data.event :as ev]
   [app.main.data.profile :as dp]
   [app.main.data.websocket :as ws]
   [app.main.errors]
   [app.main.features :as feat]
   [app.main.rasterizer :as thr]
   [app.main.store :as st]
   [app.main.ui :as ui]
   [app.main.ui.alert]
   [app.main.ui.confirm]
   [app.main.ui.css-cursors :as cur]
   [app.main.ui.delete-shared]
   [app.main.ui.routes :as rt]
   [app.main.worker :as mw]
   [app.plugins :as plugins]
   [app.util.dom :as dom]
   [app.util.i18n :as i18n]
   [beicon.v2.core :as rx]
   [cuerdas.core :as str]
   [debug]
   [features]
   [potok.v2.core :as ptk]
   [rumext.v2 :as mf]))

(log/setup! {:app :info})
(log/set-level! :debug)

(when (= :browser cf/target)
  (log/inf :version (:full cf/version)
           :asserts *assert*
           :build-date cf/build-date
           :public-uri (dm/str cf/public-uri)
           :session-id (str cf/session-id))
  (log/inf :hint "enabled flags" :flags (str/join " " (map name cf/flags))))

(declare reinit)

(defonce app-root
  (let [el (dom/get-element "app")]
    (mf/create-root el)))

(defn init-ui
  []
  (mf/render! app-root (mf/element ui/app)))

(defn- initialize-rasterizer
  []
  (ptk/reify ::initialize-rasterizer
    ptk/EffectEvent
    (effect [_ _ _]
      ;; The rasterizer is used for the dashboard thumbnails
      (thr/init!))))

(defn initialize
  []
  (ptk/reify ::initialize
    ptk/UpdateEvent
    (update [_ state]
      (assoc state :session-id cf/session-id))

    ptk/WatchEvent
    (watch [_ _ stream]
      (rx/merge
       (rx/of (ev/initialize)
              (dp/refresh-profile))

       ;; Watch for profile deletion events
       (->> stream
            (rx/filter dp/profile-deleted-event?)
            (rx/map da/logged-out))

       ;; Once profile is fetched, initialize all penpot application
       ;; routes
       (->> stream
            (rx/filter dp/profile-fetched?)
            (rx/take 1)
            (rx/map #(rt/init-routes)))

       ;; Once profile fetched and the current user is authenticated,
       ;; proceed to initialize the websockets connection.
       (->> stream
            (rx/filter dp/profile-fetched?)
            (rx/map deref)
            (rx/filter dp/is-authenticated?)
            (rx/take 1)
            (rx/map #(ws/initialize)))

       (->> stream
            (rx/filter (ptk/type? ::feat/initialize))
            (rx/take 1)
            (rx/map #(initialize-rasterizer)))))))

(defn ^:export init
  [options]
  ;; WORKAROUND: we set this really not useful property for signal a
  ;; side effect and prevent GCC remove it. We need it because we need
  ;; to populate the Date prototype with transit related properties
  ;; before SES hardening is applied on loading MCP plugin
  (unchecked-set js/globalThis "penpotStartDate"
                 (-> (ct/now)
                     (t/encode-str)
                     (t/decode-str)))

  ;; Before initializing anything, check if the browser has loaded
  ;; stale JS from a previous deployment. If so, do a hard reload so
  ;; the browser fetches fresh assets matching the current index.html.
  (if (cf/stale-build?)
    (cf/throttled-reload
     :reason (dm/str "stale JS: compiled=" cf/compiled-version-tag
                     " expected=" cf/version-tag))
    (do
      (some-> (unchecked-get options "defaultTranslations")
              (i18n/set-default-translations))
      (mw/init!)
      (i18n/init)
      (cur/init-styles)

      (init-ui)
      (st/emit! (plugins/initialize)
                (initialize)))))

(defn ^:export reinit
  ([]
   (reinit false))
  ([hard?]
   ;; The hard flag will force to unmount the whole UI and will redraw every component
   (when hard?
     (mf/unmount! app-root)
     (set! app-root (mf/create-root (dom/get-element "app"))))
   (st/emit! (ev/initialize))
   (init-ui)))

(defn ^:dev/after-load after-load
  []
  (reinit))

(set! (.-stackTraceLimit js/Error) 50)
