(ns auto-ap.views.pages.unpaid-invoices
  (:require
   [auto-ap.effects.forward :as forward]
   [auto-ap.events :as events]
   [auto-ap.forms :as forms]
   [auto-ap.status :as status]
   [auto-ap.subs :as subs]
   [auto-ap.views.components.buttons :as buttons]
   [auto-ap.views.pages.invoices.bulk-change :as bulk-change]
   [auto-ap.views.components.dropdown :refer [drop-down]]
   [auto-ap.views.components.expense-accounts-dialog
    :as expense-accounts-dialog]
   [auto-ap.views.components.invoice-table :as table]
   [auto-ap.views.components.invoices.side-bar
    :as side-bar
    :refer [invoices-side-bar]]
   [auto-ap.views.components.layouts
    :refer [appearing-side-bar side-bar-layout]]
   [auto-ap.views.components.modal :as modal]
   [auto-ap.views.pages.data-page :as data-page]
   [auto-ap.views.pages.invoices.advanced-print-checks
    :as advanced-print-checks]
   [auto-ap.views.pages.invoices.common :refer [invoice-read]]
   [auto-ap.views.pages.invoices.form :as form]
   [auto-ap.views.pages.invoices.handwritten-checks :as handwritten-checks]
   [auto-ap.views.utils
    :refer [dispatch-event dispatch-event-with-propagation with-user]]
   [clojure.set :as set]
   [clojure.string :as str]
   [goog.string :as gstring]
   [re-frame.core :as re-frame]
   [reagent.core :as r]
   [vimsical.re-frame.cofx.inject :as inject]
   [vimsical.re-frame.fx.track :as track]))

(re-frame/reg-event-fx
 ::params-change
 [with-user]
 (fn [{:keys [user]} [_ params]]
   (try 
     {:graphql {:token user
                :owns-state {:single [::data-page/page :invoices]}
                :query-obj (table/query params )
                :on-success (fn [result]
                              (let [result (set/rename-keys (first (:invoice-page result))
                                                            {:invoices :data})]

                                [::data-page/received :invoices result]))}}
     (catch js/Error e
         (println "Warning" e)))))

(re-frame/reg-event-fx
 ::unmounted
 (fn [_ _]
   {:dispatch-n [[::data-page/dispose :invoices]
                 [::forms/form-closing ::form/form]]
    ::forward/dispose [{:id ::updated}
                       {:id ::checks-printed}]
    ::track/dispose [{:id ::params}]}))

(re-frame/reg-event-fx
 ::mounted
 (fn [_ _]
   {::track/register [{:id ::params
                       :subscription [::data-page/params :invoices]
                       :event-fn (fn [params]
                                   [::params-change params])}]
    ::forward/register [{:id ::updated
                         :events #{::table/invoice-updated ::form/updated ::expense-accounts-dialog/updated}
                         :event-fn (fn [[_ invoice]]
                                     [::data-page/updated-entity :invoices invoice])}
                        {:id ::checks-printed
                         :events #{::form/checks-printed ::advanced-print-checks/checks-printed ::handwritten-checks/succeeded}
                         :event-fn (fn [[_ invoices pdf-url]]
                                     [::checks-printed invoices pdf-url])}]}))


(defn print-checks-query [invoice-payments bank-account-id type client-id]
  {:venia/operation {:operation/type :mutation
                     :operation/name "PrintChecks"}
   :venia/queries [[:print-checks
                    {:invoice_payments invoice-payments
                     :type type
                     :bank_account_id bank-account-id
                     :client_id client-id}
                    [[:invoices invoice-read]
                     :pdf_url]]]})

(re-frame/reg-event-fx
 ::print-checks
 (fn [{:keys [db]} [_ bank-account-id type]]
   {:graphql
    {:token (-> db :user)
     :owns-state {:single ::print-checks}
     
     :query-obj (print-checks-query (->> @(re-frame/subscribe [::data-page/checked :invoices])
                                         (vals )
                                         (filter (fn [{:keys [id outstanding-balance] }]
                                                   (and id outstanding-balance)))
                                         (map (fn [{:keys [id outstanding-balance] }]
                                                {:invoice-id id
                                                 :amount outstanding-balance})))
                                    bank-account-id
                                    type
                                    (:client db))  
     :on-success (fn [result]
                   [::checks-printed
                    (:invoices (:print-checks result))
                    (:pdf-url (:print-checks result))])}}))

(re-frame/reg-event-fx
 ::pay-invoices-from-balance
 [with-user (re-frame/inject-cofx ::inject/sub [::data-page/checked :invoices])]
 (fn [{:keys [db user] ::data-page/keys [checked]} _]
   {:graphql
    {:token user
     :owns-state {:single ::print-checks}
     
     :query-obj {:venia/operation {:operation/type :mutation
                                   :operation/name "PayInvoicesFromBalance"}
                 :venia/queries   [[:pay-invoices-from-balance
                                    {:invoices  (->> checked
                                                     (vals )
                                                     (filter (fn [{:keys [id outstanding-balance] }]
                                                               (and id outstanding-balance)))
                                                     (map :id))
                                     :client_id (:client db)}
                                    [[:invoices invoice-read]
                                     :pdf_url]]]}  
     :on-success (fn [result]
                   [::checks-printed
                    (:invoices (:pay-invoices-from-balance result))
                    nil])}}))


(re-frame/reg-event-fx
 ::checks-printed
 (fn [_ [_ invoices pdf-url]]
   {:dispatch-n (cond->> [[::data-page/reset-checked :invoices]]
                    true (into (mapv
                                (fn [i]
                                  [::data-page/updated-entity :invoices i])
                                invoices))

                  pdf-url (into [[::modal/modal-requested {:title "Your checks are ready!"
                                                           :body [:div
                                                                  [:div "Click " [:a {:href pdf-url :target "_new"} "here"] " to print them."]
                                                                  [:div [:em "Remember to turn off all scaling and margins."]]]}]]))}))


(re-frame/reg-event-fx
 ::new-invoice-clicked
 (fn [_ _]
   {:dispatch [::form/adding {:client @(re-frame/subscribe [::subs/client])
                              :status :unpaid
                              #_#_:date (date->str (c/now) standard)
                              :location (first (:locations @(re-frame/subscribe [::subs/client])))}]}))
(re-frame/reg-event-fx
  ::voided-selected
  (fn [_ [_]]
    {:dispatch-n [[::modal/modal-closed]
                  [::params-change @(re-frame/subscribe [::data-page/params ::page])]
                  [::data-page/reset-checked :invoices]]}))

(re-frame/reg-event-fx
 ::void-selected
 (fn [cofx [_ which]]
   (let [checked-params (get which "header")
         specific-invoices (map :id (vals (dissoc which "header")))]
     {:graphql {:token (-> cofx :db :user)
                :owns-state {:single ::void-selected}
                :query-obj
                {:venia/operation {:operation/type :mutation
                                   :operation/name "VoidInvoices"}
                 :venia/queries [{:query/data
                                  [:void-invoices
                                   {:filters (some-> checked-params table/data-params->query-params)
                                    :ids specific-invoices}
                                   [:message]]}]}
                :on-success (fn [_]
                              [::voided-selected])}})))

(re-frame/reg-event-fx
  ::void-selected-requested
  (fn [_ [_ which]]
    (let [to-delete (if (get which "header")
                      "all visible invoices"
                      (str (count which) " invoices"))]
      
      
      {:dispatch [::modal/modal-requested {:title       "Confirmation"
                                           :body        [:div (str "Are you sure you want to void " to-delete "?")]
                                           :cancel?     true
                                           :confirm     {:value       "Void"
                                                         :class       "is-danger"
                                                         :status-from [::status/single ::void-selected]
                                                         :on-click    (dispatch-event [::void-selected which] )}
                                           :close-event [::status/completed ::void-selected]}]})))

(defn void-selected-button []
  (let [status           @(re-frame/subscribe [::status/single ::void-selected])
        checked-invoices @(re-frame/subscribe [::data-page/checked :invoices])
        is-admin? @(re-frame/subscribe [::subs/is-admin?])]
    (when is-admin? 
      [:button.button.is-danger {:on-click (dispatch-event [::void-selected-requested checked-invoices])
                                 :class    (status/class-for status)
                                 :disabled (or (status/disabled-for status)
                                               (not (seq checked-invoices)))}
       "Void"])))


(defn pay-button [status]
  (let [current-client @(re-frame/subscribe [::subs/client])
        checked-invoices @(re-frame/subscribe [::data-page/checked :invoices])
        is-admin? @(re-frame/subscribe [::subs/is-admin?])
        print-checks-status @(re-frame/subscribe [::status/single ::print-checks])
        params @(re-frame/subscribe [::data-page/params :invoices]) 
        {:keys [total]} @(re-frame/subscribe [::data-page/data :invoices])
        ]
    [:div.buttons
     (when is-admin?
       [void-selected-button])
     
     [buttons/new-button {:event [::new-invoice-clicked]
                          :name  "Invoice"
                          :class "is-primary"}]

     (when (and is-admin?
                current-client)
       [buttons/event-button {:event [::bulk-change/bulk-change-requested checked-invoices params total]
                              :name  "Bulk Edit"
                              :class "is-secondary"
                              :disabled (not (seq checked-invoices))}])

     (when (and current-client
                (= :unpaid status))
       (let [balance (->> checked-invoices
                          vals
                          (map (comp js/parseFloat :outstanding-balance))
                          (reduce + 0)
                          )]
         [drop-down {:header    [:button.button.is-primary {:aria-haspopup true
                                                            :on-click      (dispatch-event [::events/toggle-menu ::print-checks ])
                                                            :disabled      (or (status/disabled-for print-checks-status) (not (seq checked-invoices)))
                                                            :class         (status/class-for @(re-frame/subscribe [::status/single ::print-checks]))}
                                 "Pay "
                                 (when (> (count checked-invoices ) 0)
                                   (str
                                     (count checked-invoices)
                                     " invoices "
                                     "("  (gstring/format "$%.2f" balance  ) ")"))
                                 [:span " "]
                                 [:span.icon.is-small [:i.fa.fa-angle-down {:aria-hidden "true"}]]]
                     :id        ::print-checks
                     :is-right? true}
          [:div
           (list
             (for [{:keys [id name type]} (->> (:bank-accounts current-client) (filter :visible) (sort-by :sort-order))]
               (if (= :cash type)
                 ^{:key id} [:a.dropdown-item {:on-click (dispatch-event [::print-checks id :cash])
                                               :disabled (status/disabled-for print-checks-status)} "With cash"]
                 (if (> balance 0.001)
                   (list
                     ^{:key (str id "-check")} [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::print-checks id :check])
                                                                  :disabled (status/disabled-for print-checks-status)} "Print checks from " name]
                     ^{:key (str id "-debit")} [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::print-checks id :debit])
                                                                  :disabled (status/disabled-for print-checks-status)} "Debit from " name])
                   (list
                     ^{:key (str id "-credit")} [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::print-checks id :credit])
                                                                   :disabled (status/disabled-for print-checks-status)} "Credit from " name]
                     ))))

             ^{:key "advanced-divider"} [:hr.dropdown-divider]

             (when (and (> (count checked-invoices) 1)
                        (= 1 (count (set (map (comp :id :vendor) (vals checked-invoices)))))
                        (< balance 0.001))
               ^{:key (str "balance-credit")} [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::pay-invoices-from-balance])
                                                                 :disabled (status/disabled-for print-checks-status)} "Pay invoices using balance "])

             (when (and (= 1 (count (set (map (comp :id :vendor) (vals checked-invoices)))))
                        (> balance 0.001))
               ^{:key "handwritten"} [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::handwritten-checks/show (vals checked-invoices)])
                                                        :disabled (status/disabled-for print-checks-status)} "Handwritten Check..."])
             (when (> balance 0.001)
               ^{:key "advanced"} [:a.dropdown-item {:on-click (dispatch-event-with-propagation [::advanced-print-checks/show (vals checked-invoices)])
                                                     :disabled (status/disabled-for print-checks-status)} "Advanced..."]))]]))]))


(defn unpaid-invoices-content [{:keys [status]}]
  (let [page           @(re-frame/subscribe [::data-page/page :invoices]) 
        _ @(re-frame/subscribe [::subs/client])]
    [:div 
     [:h1.title (str (str/capitalize (name (or status :all))) " invoices")]
     [status/status-notification {:statuses [[::status/single ::print-checks]
                                             [::status/last-multi ::table/void]
                                             [::status/last-multi ::table/unvoid]]}]
     
     [table/invoice-table {:id                    (:id page)
                           :data-page             :invoices
                           :check-boxes           true
                           :checkable-fn          (fn [i] (not (:scheduled-payment i)))
                           :actions               #{:edit :void :expense-accounts}
                           :action-buttons        [pay-button status]}]]))

(defn layout [params]
  (let [{invoice-bar-active? :active?} @(re-frame/subscribe [::forms/form ::form/form])]
    [side-bar-layout {:side-bar [invoices-side-bar {:data-page :invoices}]
                      :main [unpaid-invoices-content params]
                      :right-side-bar [appearing-side-bar {:visible? invoice-bar-active?} [form/form {}]]}]))

(defn unpaid-invoices-page []
  (r/create-class
   {:display-name "unpaid-invoices-page"
    :component-will-unmount #(re-frame/dispatch-sync [::unmounted])
    :component-did-mount #(re-frame/dispatch [::mounted])
    :reagent-render layout}))

(defn paid-invoices-page []
  (r/create-class
   {:display-name "paid-invoices-page"
    :component-will-unmount #(re-frame/dispatch-sync [::unmounted])
    :component-did-mount #(re-frame/dispatch [::mounted])
    :reagent-render layout}))

(defn voided-invoices-page []
  (r/create-class
   {:display-name "voided-invoices-page"
    :component-will-unmount #(re-frame/dispatch-sync [::unmounted])
    :component-did-mount #(re-frame/dispatch [::mounted])
    :reagent-render layout}))

(defn all-invoices-page []
  (r/create-class
   {:display-name "all-invoices-page"
    :component-will-unmount #(re-frame/dispatch-sync [::unmounted])
    :component-did-mount #(re-frame/dispatch [::mounted])
    :reagent-render layout}))
