(ns auto-ap.views.pages.unpaid-invoices
  (:require [re-frame.core :as re-frame]
            [reagent.core :as r]
            [clojure.string :as str]
            [auto-ap.entities.companies :as company]
            [auto-ap.entities.vendors :as vendor]
            [auto-ap.views.utils :refer [dispatch-event bind-field horizontal-field]]
            [auto-ap.utils :refer [by]]
            [auto-ap.views.pages.check :as check]
            [auto-ap.views.components.invoice-table :refer [invoice-table] :as invoice-table]
            [auto-ap.views.components.modal :refer [modal action-modal]]
            [auto-ap.subs :as subs]
            [auto-ap.events :as events]))

(re-frame/reg-sub
 ::invoice-page
 (fn [db]
   (-> db ::invoice-page)))

(re-frame/reg-sub
 ::advanced-print-checks
 (fn [db]
   (-> db ::advanced-print-checks)))

(re-frame/reg-sub
 ::check-results
 (fn [db]
   (-> db ::check-results)))

(re-frame/reg-sub
 ::new-invoice
 (fn [db]
   (-> db ::new-invoice)))

(re-frame/reg-sub
 ::params
 (fn [db]
   (-> db (::params {}))))

(re-frame/reg-event-fx
 ::params-change
 (fn [cofx [_ params]]
   {:db (-> (:db cofx)
            (assoc-in [:status :loading] true)
            (assoc-in [::params] params))
    :graphql {:token (-> cofx :db :user)
              :query-obj (invoice-table/query (assoc params :imported true :status "unpaid")) 
              :on-success [::received]}}))

(re-frame/reg-event-db
 ::received
 (fn [db [_ data]]
   (-> db
       (assoc ::invoice-page (first (:invoice-page data)))
       (assoc-in [:status :loading] false))))

(re-frame/reg-event-db
 ::toggle-check
 (fn [db [_ data]]
   (update-in db [::invoice-page :checked] (fn [x]
                                             
                                             (let [x (or x #{})]
                                               (if (x data)
                                                 (disj x data)
                                                 (conj x data)))))))

(re-frame/reg-event-db
 ::print-checks-clicked
 (fn [db _]
   (update-in db [::invoice-page :print-checks-shown?] #(not %) )))

(re-frame/reg-event-db
 ::advanced-print-checks
 (fn [db _]
   (let [{:keys [checked invoices]} (get-in db [::invoice-page])]
     (-> db
         (update-in [::invoice-page :print-checks-shown?] #(not %) )
         (assoc-in [::advanced-print-checks] {:shown? true
                                              :bank-account-id (:id (first (:bank-accounts @(re-frame/subscribe [::subs/company]))))
                                              :invoices (->> invoices
                                                             (filter (comp checked :id))
                                                             (map #(assoc % :amount (:outstanding-balance %))))} )))))

(re-frame/reg-event-db
 ::cancel-advanced-print
 (fn [db _]
   (assoc-in db [::advanced-print-checks :shown?] false )))

(re-frame/reg-event-db
 ::close-check-results
 (fn [db _]
   (assoc-in db [::check-results :shown?] false )))

(re-frame/reg-event-db
 ::edit-payment-bank-account
 (fn [db [_ f v]]
   (assoc-in db [::advanced-print-checks :bank-account-id] v)))

(re-frame/reg-event-db
 ::edit-payment
 (fn [db [_ which f v]]
   (update-in db [::advanced-print-checks :invoices]
              (fn [is]
                (for [i is]
                  (if (= which (:id i))
                    (assoc-in i f v)
                    i))))))

(defn print-checks-query [invoice-payments bank-account-id company-id]
  {:venia/operation {:operation/type :mutation
                     :operation/name "PrintChecks"}
   
   :venia/queries [[:print-checks
                    {:invoice_payments invoice-payments
                     :bank_account_id bank-account-id
                     :company_id company-id}
                    [[:invoices [:id :outstanding-balance [:checks [:amount [:check [:amount :s3_url :check_number ]]]]]]
                     :pdf_url]]]})

(re-frame/reg-event-fx
 ::print-checks
 (fn [{:keys [db]} [_ bank-account-id]]
   (let [invoice-amounts (by :id :outstanding-balance (get-in db [::invoice-page :invoices]))]

     {:db (-> db
              (assoc-in [::invoice-page :print-checks-shown?] false )
              (assoc-in [::invoice-page :print-checks-loading?] true ))
      :graphql
      {:token (-> db :user)
       
       :query-obj (print-checks-query (map (fn [id]
                                             {:invoice-id id
                                              :amount (invoice-amounts id)})
                                           (get-in db [::invoice-page :checked]))
                                      bank-account-id
                                      (:company db))  
       :on-success [::checks-created]}})))



(re-frame/reg-event-fx
 ::advanced-print-checks-submitted
 (fn [{:keys [db]} [_ bank-account-id]]
   (let [invoice-amounts (by :id (comp js/parseFloat :amount) (get-in db [::advanced-print-checks :invoices]))
         bank-account-id (get-in db [::advanced-print-checks :bank-account-id])]
     {:db (-> db
              (assoc-in [::advanced-print-checks :printing?] true ))
      :graphql
      {:token (-> db :user)
       
       :query-obj (print-checks-query (map (fn [x]
                                             {:invoice-id (:id x)
                                              :amount (invoice-amounts (:id x))})
                                           (get-in db [::advanced-print-checks :invoices]))
                                      bank-account-id
                                      (:company db))  

       :on-success [::checks-created]}})))

(re-frame/reg-event-fx
 ::checks-created
 (fn [{:keys [db]} [_ data]]
   (let [{{:keys [pdf-url invoices]} :print-checks} data
         invoices-by-id (by :id invoices) ]
     {
      :db (-> db
              (update-in [::invoice-page :invoices]
                            (fn [invoices]
                              (map (fn [i]
                                     (merge i (invoices-by-id (:id i))))
                                   invoices)))
              (assoc-in [::invoice-page :checked] nil)
              (assoc-in [::invoice-page :print-checks-loading?] false)
              (assoc-in [::advanced-print-checks :printing?] false)
              (assoc-in [::advanced-print-checks :shown?] false)
              (assoc-in [::check-results :shown?] true)
              (assoc-in [::check-results :pdf-url] pdf-url)
              )})))

(re-frame/reg-event-fx
 ::invalidated
 (fn [cofx [_ params]]
   {:dispatch [::params-change @(re-frame/subscribe [::params])]}))

(re-frame/reg-event-fx
 ::new-invoice
 (fn [{:keys [db]} _]
   {:dispatch [::events/modal-status ::new-invoice {:visible? true}]}))

(re-frame/reg-event-fx
 ::create-invoice
 (fn [{:keys [db]} _]
   {}))

(defn print-checks-modal []
  (let [{:keys [checked]} @(re-frame/subscribe [::invoice-page]) 
        {:keys [shown? invoices printing?] :as advanced-print-checks} @(re-frame/subscribe [::advanced-print-checks])
        current-company @(re-frame/subscribe [::subs/company])] 

    (when shown?
      [modal {:title "Print Checks"
              :foot [:button.button.is-primary {:on-click (dispatch-event [::advanced-print-checks-submitted])
                                                :class (if printing? "is-loading" "")}
                     [:span "Print"]]
              :hide-event [::cancel-advanced-print]}
       "Print using"
       [:span.field
        [:span.select
         [bind-field
          
          [:select {:type "select"
                    :field :bank-account-id
                    :event ::edit-payment-bank-account
                    :subscription advanced-print-checks}
           (for [{:keys [id number name]} (:bank-accounts current-company)]
             ^{:key id} [:option {:value id} name])]]]]

       [:table.table
        [:thead
         [:tr
          [:th "Vendor"]
          [:th "Invoice ID"]
          [:th {:style {"width" "10em"}} "Payment"]]]
        [:tbody
         (for [{:keys [vendor payment outstanding-balance invoice-number id] :as i} invoices]
           ^{:key id}
           [:tr
            [:td (:name vendor)]
            [:td invoice-number]
            
            [:td  [:div.field.has-addons.is-extended
                   [:p.control [:a.button.is-static "$"]]
                   [:p.control
                    [bind-field
                     [:input.input {:type "number"
                                    :field :amount
                                    :event [::edit-payment id]
                                    :subscription i
                                    :value payment
                                    :max outstanding-balance
                                    :step "0.01"}]]]]]])]]])))

(defn typeahead [{:keys [matches on-change field value]}]
  (let [text (r/atom (or (second (first (filter #(= (first %) value) matches))) ""))
        highlighted (r/atom 0)
        selected (r/atom (first (first (filter #(= (first %) value) matches))))
        select (fn [[id t]]
                 (reset! selected id)
                 (reset! text t)
                 (println [id t])
                 (when on-change
                   (on-change id)))]
    (fn [{:keys [matches on-change field value]}]
      (let [valid-matches (take 5 (for [[[id t :as match] i] (map vector  matches (range))
                                        :when (str/includes? (.toLowerCase t) (.toLowerCase @text))]
                                    match))]
        [:div.typeahead
         [:input.input {:type "text"
                        :field [:vendor]
                        :value @text
                        :on-blur (fn [e]
                                   (cond @selected
                                         nil 

                                         (seq valid-matches)
                                         (do (select (first valid-matches))
                                             true)

                                         :else
                                         (do (select [nil ""])
                                             true))
                                   )
                        :on-key-up (fn [e]
                                    (if (= 13 (.-keyCode e))
                                      (do
                                        (select (first valid-matches))
                                        false)
                                      true))
                        :on-change (fn [e]
                                     (reset! highlighted (ffirst valid-matches))
                                     (select [nil (.. e -target -value)]))}]
         (when (and (seq @text)
                    (not @selected)
                    (seq valid-matches))
           [:div.typeahead-menu
            [:ul
             (for [[id t :as match] valid-matches]

               [:li.typeahead-suggestion {:class (if (= id @highlighted)
                                                   "typeahead-highlighted")
                                          :on-mouse-down #(do (println "MATCH" match) (select match))} t])]])]))))

(defn new-invoice-modal []
  (let [data @(re-frame/subscribe [::new-invoice])
        change-event [::events/change-form [::new-invoice]]]
    (println data)
    [action-modal {:id ::new-invoice
                   :title "New Invoice"
                   :action-text "Create"
                   :save-event [::create-invoice]}
     [horizontal-field
      [:label.label  "Vendor"]
      [bind-field
       [typeahead {:matches (map (fn [x] [(:id x) (:name x)]) @(re-frame/subscribe [::subs/vendors]))
                   :type "typeahead"
                   :field [:vendor-id]
                   :event change-event
                   :subscription data}]]]
     [horizontal-field
      [:label.label  "Date"]
      [bind-field
       [:input.input {:type "text"
                      :field [:date]
                      :event change-event
                      :subscription data}]]]

     [horizontal-field
      [:label.label  "Company"]
      [bind-field
       [typeahead {:matches (map (fn [x] [(:id x) (:name x)]) @(re-frame/subscribe [::subs/companies]))
                   :type "typeahead"
                   :field [:company-id]
                   :event change-event
                   :subscription data}]]]

     [horizontal-field
      [:label.label  "Invoice #"]
      [bind-field
       [:input.input {:type "text"
                      :field [:invoice-number]
                      :event change-event
                      :subscription data}]]]

     [horizontal-field
      [:label.label "Total"]
      [:div.field.has-addons.is-extended
       [:p.control [:a.button.is-static "$"]]
       [:p.control
        [bind-field
         [:input.input {:type "number"
                        :field [:total]
                        :event change-event
                        :subscription data
                        :step "0.01"}]]]]]]))

(def unpaid-invoices-page
  (with-meta
    (fn []
      (let [{:keys [checked print-checks-shown? print-checks-loading? advanced-print-shown?]} @(re-frame/subscribe [::invoice-page]) 
            current-company @(re-frame/subscribe [::subs/company])
            {check-results-shown? :shown? pdf-url :pdf-url} @(re-frame/subscribe [::check-results])]
        [:div 
         [:h1.title "Unpaid invoices"]
         [:div.is-pulled-right
          [:button.button.is-danger {:on-click (dispatch-event [::new-invoice])} "New Invoice"]
          (when current-company
            [:div.dropdown.is-right {:class (if print-checks-shown?
                                     "is-active"
                                     "")}
             [:div.dropdown-trigger
              [:button.button.is-success {:aria-haspopup true
                                          :on-click (dispatch-event [::print-checks-clicked ])
                                          :disabled (if (seq checked)
                                                      ""
                                                      "disabled")

                                          :class (if print-checks-loading?
                                                   "is-loading"
                                                   "")}
               "Print checks "
               [:span.icon.is-small [:i.fa.fa-angle-down {:aria-hidden "true"}]]]]
             [:div.dropdown-menu {:role "menu"}
              [:div.dropdown-content
               (list
                (for [{:keys [id number name]} (:bank-accounts current-company)]
                  ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id])} name])
                ^{:key "advanced-divider"} [:hr.dropdown-divider]
                ^{:key "advanced"} [:a.dropdown-item {:on-click (dispatch-event [::advanced-print-checks])} "Advanced..."])]]])]
         [invoice-table {:id :unpaid
                         :params (re-frame/subscribe [::params])
                         :invoice-page (re-frame/subscribe [::invoice-page])
                         :status (re-frame/subscribe [::subs/status])
                         :on-params-change (fn [params]
                                             (re-frame/dispatch [::params-change params]))
                         :check-boxes true
                         :checked checked
                         :on-check-changed (fn [which]
                                             (re-frame/dispatch [::toggle-check which]))}]

         [print-checks-modal]
         [new-invoice-modal]
         (when check-results-shown?
           [modal
            {:title "Your checks are ready!"
             :hide-event [::close-check-results]}
            [:div "Click " [:a {:href pdf-url :target "_new"} "here"] " to print them."]])
         ]))
    {:component-will-mount #(re-frame/dispatch-sync [::params-change {}]) }))
