(ns com.oly.static-sites.clojure-demos.sci-init
  (:require
   ["@codemirror/language" :refer [LanguageSupport StreamLanguage]]
   ["@codemirror/legacy-modes/mode/yaml" :refer [yaml]]
   ["@codemirror/state" :as cm-state :refer [EditorState Transaction]]
   ["@codemirror/theme-one-dark" :refer [oneDark]]
   ["@codemirror/view" :as cm-view :refer [EditorView ViewUpdate]]
   ["@nextjournal/lang-clojure" :refer [clojure]]
   ["react-dom/client" :refer [createRoot]]
   ["tarts" :as tarts]
   [ajax.core :refer [GET raw-response-format]]
   [cl-eorg.html :as h :refer [body headers org->replacements]]
   [cl-eorg.parser :as o :refer [parse]]
   [cl-eorg.themes.tachyon :refer [tachyon-theme]]
   [com.oly.static-sites.clojure-demos.components
    :refer [header-lvl1 header-lvl2 header-lvl3 notification]]
   [com.oly.static-sites.clojure-demos.helpers :refer [slugify]]
   [com.oly.static-sites.clojure-demos.state :refer [site-state]]
   [com.oly.static-sites.clojure-demos.tailwind-theme :refer [tailwind-theme]]
   [clojure.data.xml :as xml]
   [clojure.string :as str]
   [honey.sql :as sql]
   [promesa.core :as promesa]
   [honey.sql.helpers :as sqlh]
   [reagent.core :as reagent]
   [reitit.coercion.spec :as rss]
   [reitit.frontend :as rf]
   [reitit.frontend.easy :as rfe]
   [reitit.frontend.history :refer [ignore-anchor-click?]]
   [sci.core :as sci]
   [sci.configs.funcool.promesa :as sci-promesa]
   [sci.configs.reagent.reagent :as sci-reagent]
   [sci.configs.tonsky.datascript :as sci-datascript]
   [com.oly.static-sites.clojure-demos.routes :refer [site-data routes]]
   [com.oly.static-sites.clojure-demos.sitemap :as sm #_#_:refer [spit-pwa-sitemap]]
   [tick.core :as t]))

(def languages
  {"clojure" {:mode "clojure"}
   "clojurescript" {:mode "clojure"}
   "html" {:mode "html"}
   "json" {:mode "json"}
   "yaml" {:mode "yaml"}})

(def yaml-mode (LanguageSupport. (.define StreamLanguage yaml)))

(defn ^:sci/macro my-js-await
  [_ _ [name thenable] & body]
  (let [last-expr (last body)
        [body catch]
        (if (and (seq? last-expr) (= 'catch (first last-expr)))
          [(butlast body) last-expr]
          [body nil])]

    ;; FIXME: -> here will always return a promise so shouldn't be necessary to add js hint?
    `(-> ~thenable
         ~@(when (seq body)
             [`(.then (fn [~name] ~@body))])
         ~@(when catch
             (let [[name & body] catch]
               [`(.catch (fn [~name] ~@body))])))))

;; https://github.com/babashka/sci.configs
(def rf-ns (sci/create-ns 'reitit.frontend nil))
(def rfe-ns (sci/create-ns 'reitit.frontend.easy nil))
(def rss-ns (sci/create-ns 'reitit.coercion.spec nil))
(def sql-ns (sci/create-ns 'honey.sql.core nil))
(def sqlh-ns (sci/create-ns 'honey.sql.helpers nil))
(def shadow-ns (sci/create-ns 'shadow.cljs.modern nil))
(def xml-ns (sci/create-ns 'clojure.data.xml nil))
(def tick-ns (sci/create-ns 'tick.core nil))

;; core async does not work with sci
(def async-ns (sci/create-ns 'cljs.core.async nil))
(def async-in-ns (sci/create-ns 'cljs.core.async.interop nil))

;; https://github.com/babashka/sci.configs
(def sci-ctx
  (sci/init
   {:classes {'js js/globalThis :allow :all}
    :features #{:cljs}
    :aliases {'promesa 'promesa.core
              'reagent 'reagent.core
              'd 'datascript.core}
    :namespaces
    {'reagent.core sci-reagent/reagent-namespace
     'reagent.ratom sci-reagent/reagent-ratom-namespace
     ;'reagent.dom.server sci-reagent-server/namespaces
     'promesa.core sci-promesa/promesa-namespace
     'datascript.core sci-datascript/core-namespace
     'datascript.db sci-datascript/db-namespace

     ;'h {'html (sci/copy-var h/html hc-ns)}
     ;'async {'go (sci/copy-var async/go async-ns)}
     ;'shadow {'js-await (sci/copy-var js-await shadow-ns)}
     'shadow {'js-await (sci/copy-var my-js-await shadow-ns)}
     ;'async-in {'<p! (sci/copy-var async-in/<p! async-in-ns)}
     'tick {'format (sci/copy-var t/format tick-ns)
            'zoned-date-time (sci/copy-var t/zoned-date-time tick-ns)}
     'xml {'emit-str (sci/copy-var xml/emit-str xml-ns)
           ;; dont think indent-str & alias uri is supported in clojurescript
           ;;'indent-str (sci/copy-var xml/indent-str xml-ns)
           ;;'alias-uri (sci/copy-var xml/alias-uri xml-ns)
           }
     'sql {'format (sci/copy-var sql/format sql-ns)
           #_#_'raw (sci/copy-var sql/raw sql-ns)}
     'sqlh {'select (sci/copy-var sqlh/select sqlh-ns)
            'from (sci/copy-var sqlh/from sqlh-ns)
            'limit (sci/copy-var sqlh/limit sqlh-ns)
            'join (sci/copy-var sqlh/join sqlh-ns)
            'values (sci/copy-var sqlh/values sqlh-ns)
            'on-conflict (sci/copy-var sqlh/on-conflict sqlh-ns)
            'do-update-set (sci/copy-var sqlh/do-update-set sqlh-ns)
            'insert-into (sci/copy-var sqlh/insert-into sqlh-ns)
            'order-by (sci/copy-var sqlh/order-by sqlh-ns)
            'group-by (sci/copy-var sqlh/group-by sqlh-ns)
            'where (sci/copy-var sqlh/where sqlh-ns)}
     'rf {'router (sci/copy-var rf/router rf-ns)}
     'rss {'coercion (sci/copy-var rss/coercion rss-ns)}
     'rfe {'start! (sci/copy-var rfe/start! rfe-ns)
           'href (sci/copy-var rfe/href rfe-ns)}}}))

;(def sci-ctx  (sci/empty-environment))
(sci/alter-var-root sci/print-fn (constantly *print-fn*))
