(ns frontend.core
  (:require [clojure.string :as string]
            [fipp.edn :as fedn]
            [reagent.core :as r]
            [reitit.coercion.spec :as rss]
            [reitit.frontend :as rf]
            [reitit.frontend.easy :as rfe]
            [spec-tools.data-spec :as ds]))

;; Components similar to react-router `Link`, `NavLink` and `Redirect`
;; with Reitit frontend.

(defn home-page []
  [:div
   [:h2 "Welcome to frontend"]
   [:p "This is home page"]])

(defn about-page []
  [:div
   [:h2 "About frontend"]
   [:p "This is about page"]])

(defn redirect!
  "If `push` is truthy, previous page will be left in history."
  [{:keys [to path-params query-params push]}]
  (if push
    (rfe/push-state to path-params query-params)
    (rfe/replace-state to path-params query-params)))

(defn Redirect
  "Component that only causes a redirect side-effect."
  [props]
  (r/create-class
   {:component-did-mount  (fn [this] (redirect! (r/props this)))
    :component-did-update (fn [this [_ prev-props]]
                            (if (not= (r/props this) prev-props)
                              (redirect! (r/props this))))
    :render (fn [this] nil)}))

(defn item-page [match]
  (let [{:keys [path query]} (:parameters match)
        {:keys [id]} path]
    (if (< id 1)
      [Redirect {:to ::frontpage}]
      [:div
       [:h2 "Selected item " id]
       (when (:foo query)
         [:p "Optional foo query param: " (:foo query)])])))

(def routes
  [["/"
    {:name ::frontpage
     :view home-page}]

   ["/about"
    {:name ::about
     :view about-page}]

   ["/item/:id"
    {:name ::item
     :view item-page
     :parameters
     {:path {:id int?}
      :query {(ds/opt :foo) keyword?}}}]])

(def router
  (rf/router routes {:data {:coercion rss/coercion}}))

(defonce current-match (r/atom nil))

(defn- resolve-href
  [to path-params query-params]
  (if (keyword? to)
    (rfe/href to path-params query-params)
    (let [match  (rf/match-by-path router to)
          route  (-> match :data :name)
          params (or path-params (:path-params match))
          query  (or query-params (:query-params match))]
      (if match
        (rfe/href route params query)
        to))))

(defn Link
  [{:keys [to path-params query-params active]} & children]
  (let [href (resolve-href to path-params query-params)]
    (into
     [:a {:href href} (when active "> ")] ;; Apply styles or whatever
     children)))

(defn- name-matches?
  [name path-params match]
  (and (= name (-> match :data :name))
       (= (not-empty path-params)
          (-> match :parameters :path not-empty))))

(defn- url-matches?
  [url match]
  (= (-> url (string/split #"\?") first)
     (:path match)))

(defn NavLink
  [{:keys [to path-params] :as props} & children]
  (let [active (or (name-matches? to path-params @current-match)
                   (url-matches? to @current-match))]
    [Link (assoc props :active active) children]))

(defn current-page []
  [:div

   [:h4 "Link"]
   [:ul
    [:li [Link {:to ::frontpage} "Frontpage"]]
    [:li [Link {:to "/about"} "About"]]
    [:li [Link {:to ::item :path-params {:id 1}} "Item 1"]]
    [:li [Link {:to "/item/2?foo=bar"} "Item 2"]]
    [:li [Link {:to "/item/-1"} "Item -1 (redirects to frontpage)"]]
    [:li [Link {:to ""} "Google"]]]

   [:h4 "NavLink"]
   [:ul
    [:li [NavLink {:to ::frontpage} "Frontpage"]]
    [:li [NavLink {:to "/about"} "About"]]
    [:li [NavLink {:to ::item :path-params {:id 1}} "Item 1"]]
    [:li [NavLink {:to "/item/2?foo=bar"} "Item 2"]]
    [:li [NavLink {:to "/item/-1"} "Item -1 (redirects to frontpage)"]]
    [:li [NavLink {:to ""} "Google"]]]

   (if @current-match
     (let [view (:view (:data @current-match))]
       [view @current-match]))

   [:pre (with-out-str (fedn/pprint @current-match))]])

(defn init! []
  (rfe/start!
    router
    (fn [m] (reset! current-match m))
  set to false to enable HistoryAPI
    {:use-fragment true})
  (r/render [current-page] (.getElementById js/document "app")))

(init!)

(comment
  (rf/match-by-path router "/about?kissa=1&koira=true")
  (rf/match-by-path router "/item/2?kissa=1&koira=true"))

 Components similar to react-router `Link`, `NavLink` and `Redirect`
 with Reitit frontend.
 Apply styles or whatever
(ns frontend.core
  (:require [clojure.string :as string]
            [fipp.edn :as fedn]
            [reagent.core :as r]
            [reitit.coercion.spec :as rss]
            [reitit.frontend :as rf]
            [reitit.frontend.easy :as rfe]
            [spec-tools.data-spec :as ds]))


(defn home-page []
  [:div
   [:h2 "Welcome to frontend"]
   [:p "This is home page"]])

(defn about-page []
  [:div
   [:h2 "About frontend"]
   [:p "This is about page"]])

(defn redirect!
  "If `push` is truthy, previous page will be left in history."
  [{:keys [to path-params query-params push]}]
  (if push
    (rfe/push-state to path-params query-params)
    (rfe/replace-state to path-params query-params)))

(defn Redirect
  "Component that only causes a redirect side-effect."
  [props]
  (r/create-class
   {:component-did-mount  (fn [this] (redirect! (r/props this)))
    :component-did-update (fn [this [_ prev-props]]
                            (if (not= (r/props this) prev-props)
                              (redirect! (r/props this))))
    :render (fn [this] nil)}))

(defn item-page [match]
  (let [{:keys [path query]} (:parameters match)
        {:keys [id]} path]
    (if (< id 1)
      [Redirect {:to ::frontpage}]
      [:div
       [:h2 "Selected item " id]
       (when (:foo query)
         [:p "Optional foo query param: " (:foo query)])])))

(def routes
  [["/"
    {:name ::frontpage
     :view home-page}]

   ["/about"
    {:name ::about
     :view about-page}]

   ["/item/:id"
    {:name ::item
     :view item-page
     :parameters
     {:path {:id int?}
      :query {(ds/opt :foo) keyword?}}}]])

(def router
  (rf/router routes {:data {:coercion rss/coercion}}))

(defonce current-match (r/atom nil))

(defn- resolve-href
  [to path-params query-params]
  (if (keyword? to)
    (rfe/href to path-params query-params)
    (let [match  (rf/match-by-path router to)
          route  (-> match :data :name)
          params (or path-params (:path-params match))
          query  (or query-params (:query-params match))]
      (if match
        (rfe/href route params query)
        to))))

(defn Link
  [{:keys [to path-params query-params active]} & children]
  (let [href (resolve-href to path-params query-params)]
    (into
     children)))

(defn- name-matches?
  [name path-params match]
  (and (= name (-> match :data :name))
       (= (not-empty path-params)
          (-> match :parameters :path not-empty))))

(defn- url-matches?
  [url match]
  (= (-> url (string/split #"\?") first)
     (:path match)))

(defn NavLink
  [{:keys [to path-params] :as props} & children]
  (let [active (or (name-matches? to path-params @current-match)
                   (url-matches? to @current-match))]
    [Link (assoc props :active active) children]))

(defn current-page []
  [:div

   [:h4 "Link"]
   [:ul
    [:li [Link {:to ::frontpage} "Frontpage"]]
    [:li [Link {:to "/about"} "About"]]
    [:li [Link {:to ::item :path-params {:id 1}} "Item 1"]]
    [:li [Link {:to "/item/2?foo=bar"} "Item 2"]]
    [:li [Link {:to "/item/-1"} "Item -1 (redirects to frontpage)"]]
    [:li [Link {:to ""} "Google"]]]

   [:h4 "NavLink"]
   [:ul
    [:li [NavLink {:to ::frontpage} "Frontpage"]]
    [:li [NavLink {:to "/about"} "About"]]
    [:li [NavLink {:to ::item :path-params {:id 1}} "Item 1"]]
    [:li [NavLink {:to "/item/2?foo=bar"} "Item 2"]]
    [:li [NavLink {:to "/item/-1"} "Item -1 (redirects to frontpage)"]]
    [:li [NavLink {:to ""} "Google"]]]

   (if @current-match
     (let [view (:view (:data @current-match))]
       [view @current-match]))

   [:pre (with-out-str (fedn/pprint @current-match))]])

(defn init! []
  (rfe/start!
    router
    (fn [m] (reset! current-match m))
  set to false to enable HistoryAPI
    {:use-fragment true})
  (r/render [current-page] (.getElementById js/document "app")))

(init!)

(comment
  (rf/match-by-path router "/about?kissa=1&koira=true")
  (rf/match-by-path router "/item/2?kissa=1&koira=true"))
