(ns auto-ap.views.components.invoice-table
  (:require [auto-ap.events :as events]
            [auto-ap.routes :as routes]
            [auto-ap.subs :as subs]
            [auto-ap.status :as status]
            [auto-ap.views.components.buttons :as buttons]
            [auto-ap.views.components.dropdown
             :refer
             [drop-down drop-down-contents]]
            [auto-ap.views.components.grid :as grid]
            [auto-ap.views.pages.invoices.form :as form]
            [auto-ap.views.pages.invoices.common :refer [invoice-read]]
            [auto-ap.views.utils :refer [date->str dispatch-event dispatch-event-with-propagation nf days-until]]
            [bidi.bidi :as bidi]
            [cemerick.url :as url]
            [cljs-time.core :as t]
            [clojure.string :as str]
            [goog.string :as gstring]
            [re-frame.core :as re-frame]
            [auto-ap.views.components.expense-accounts-dialog :as expense-accounts-dialog]
            [auto-ap.views.pages.data-page :as data-page]))

(defn query [params]
  {:venia/queries [[:invoice_page
                    {
                     :exact-match-id (some-> params :exact-match-id str)
                     :start (:start params 0)
                     :sort (:sort params)
                     :per-page (:per-page params)

                     :vendor-id (:id (:vendor params))
                     :date-range (:date-range params)
                     :due-range (:due-range params)
                     :amount-gte (:amount-gte (:amount-range params))
                     :amount-lte (:amount-lte (:amount-range params))
                     :location (:location params)
                     :unresolved (:unresolved params)
                     :scheduled-payments (:scheduled-payments params)
                     :invoice-number-like (:invoice-number-like params)
                     :client-id (:id @(re-frame/subscribe [::subs/client]))
                     :import-status (:import-status params)
                     :status (condp = @(re-frame/subscribe [::subs/active-page])
                                   :invoices nil
                                   :import-invoices nil
                                   :unpaid-invoices :unpaid
                                   :paid-invoices :paid
                                   :voided-invoices :voided)}
                    [[:invoices [:id :total :outstanding-balance :invoice-number :date :due :status :client-identifier :scheduled-payment
                                 [:vendor [:name :id]]
                                 [:expense_accounts [:amount :id :location
                                                     [:account [:id ]]]]
                                 [:client [:name :id :locations]]
                                 [:payments [:amount :id [:payment [:id :status :amount :s3_url :check_number
                                                                    [:transaction [:post_date]]]]]]]]
                     :outstanding
                     :total
                     :start
                     :end]]]})



(re-frame/reg-event-fx
 ::void-invoice
 (fn [{:keys [db]} [_ {id :id}]]
   {:graphql
    {:token (-> db :user)
     :owns-state {:multi ::void
                  :which id}
     :query-obj {:venia/operation {:operation/type :mutation
                                   :operation/name "VoidInvoice"}
                 
                 :venia/queries [{:query/data [:void-invoice
                                               {:invoice-id id}
                                               invoice-read]}]}  
     :on-success (fn [result]
                   [::invoice-updated (assoc (:void-invoice result)
                                             :class "live-removed")])}}))

(re-frame/reg-event-fx
 ::unvoid-invoice
 (fn [{:keys [db]} [_ {id :id}]]
   {:graphql
    {:token (-> db :user)
     :owns-state {:multi ::unvoid
                  :which id}
     :query-obj {:venia/operation {:operation/type :mutation
                                   :operation/name "UnvoidInvoice"}
                 
                 :venia/queries [{:query/data [:unvoid-invoice
                                               {:invoice-id id}
                                               invoice-read]}]}  
     :on-success (fn [result]
                   [::invoice-updated (:unvoid-invoice result)])}}))
(re-frame/reg-event-fx
 ::unautopay
 (fn [{:keys [db]} [_ {id :id}]]
   {:graphql
    {:token (-> db :user)
     :owns-state {:multi ::unautopay
                  :which id}
     :query-obj {:venia/operation {:operation/type :mutation
                                   :operation/name "UnautopayInvoice"}
                 
                 :venia/queries [{:query/data [:unautopay-invoice
                                               {:invoice-id id}
                                               invoice-read]}]}  
     :on-success (fn [result]
                   [::invoice-updated (:unautopay-invoice result)])}}))

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

(defn row [{:keys [invoice check-boxes selected-client overrides checkable? expense-event actions]}]
  (let [{:keys [client status payments expense-accounts invoice-number date due total outstanding-balance id vendor] :as i} invoice
        accounts-by-id @(re-frame/subscribe [::subs/accounts-by-id client])
        unautopay-states @(re-frame/subscribe [::status/multi ::unautopay])
        account->name #(:name (accounts-by-id (:id %)))]
    [grid/row {:class (:class i) :id id :checkable? checkable? :entity invoice}
     (when-not selected-client
       [grid/cell {}
        (if-let  [client-override (:client overrides)]
          (client-override i)
          (:name client))])
     [grid/cell {} (:name vendor)]
     [grid/cell {} invoice-number]
     [grid/cell {:class "is-hidden-mobile"}  (date->str date) ]
     [grid/cell {}
      (when due 
        (if (#{":paid" :paid ":voided" :voided} status)
          nil
          (let [due-in (days-until due)]
            (if (> due-in 0)
              [:span.has-text-success due-in " days"]
              [:span.has-text-danger due-in " days"])
            )))]
     [grid/cell {} (str/join ", " (set (map :location expense-accounts)))]
     
     [grid/cell {:class "has-text-right"} (nf total )]
     [grid/cell {:class "has-text-right"}
      (if (:scheduled-payment i)
        [:<> [:div.tag.is-info.is-light "Autopay"] " "]
        )
      (nf outstanding-balance )]
     [grid/button-cell {} 
      [:div.buttons 
       (when (seq expense-accounts)
         [drop-down {:id [::expense-accounts id  ]
                     :is-right? true
                     :header [buttons/sl-icon {:class "badge"
                                               :event [::events/toggle-menu [::expense-accounts id]]
                                               :data-badge (str (clojure.core/count expense-accounts))
                                               :icon "icon-navigation-menu"}]}
          [drop-down-contents
           [:div
            (for [e expense-accounts]
              ^{:key (:id e)}
              [:span.dropdown-item  (account->name (:account e)) " "  (gstring/format "$%.2f" (:amount e) ) ])

            (when (get actions :expense-accounts)
              [:<>

               [:hr.dropdown-divider]

               [:a.dropdown-item.is-primary {:on-click (dispatch-event [::expense-accounts-dialog/show i])} "Change"]])]]])
       [:span {:style {:margin-left "1em"}}]
       (when (seq payments)
         [:<>
          [drop-down {:id [::payments id]
                      :is-right? true
                      :header [buttons/fa-icon {:class "badge"
                                                :on-click (dispatch-event-with-propagation [::events/toggle-menu [::payments id]])
                                                :data-badge (str (clojure.core/count payments))
                                                :icon "fa-paperclip"}]}
           [drop-down-contents
            [:div.dropdown-item 
             [:table.table.grid.compact
              [:tbody 
               (for [invoice-payment payments]
                 ^{:key (:id invoice-payment)}
                 [:tr
                  [:td
                   "Payment"
                   ]
                  [:td (gstring/format "$%.2f" (:amount invoice-payment) )]
                  [:td 
                   (when (= :cleared (:status (:payment invoice-payment)))
                     (str "cleared")
                     )]
                  [:td (:post-date (:transaction (:payment invoice-payment)))]
                  [:td 
                   [buttons/fa-icon {:icon "fa-external-link"
                                     :href (str (bidi/path-for routes/routes :payments )
                                                "?"
                                                (url/map->query {:exact-match-id (:id (:payment invoice-payment))}))}]]])]]]]]
          [:span {:style {:margin-right "1em"}}]])
       (when (and (get actions :edit)
                  (not= ":voided" (:status i)))
         [buttons/fa-icon {:icon "fa-pencil"
                           :event [::form/editing i]}])
       (when (and (get actions :void)
              (= (:outstanding-balance i) (:total i)) (not= ":voided" (:status i)))
         [buttons/sl-icon {:icon "icon-bin-2"
                           :event [::void-invoice i]}])
       (when (and (get actions :void)
              (= ":voided" (:status i)))
         [buttons/fa-icon {:icon "fa-undo"
                           :event [::unvoid-invoice i]}])
       (when (and (get actions :void)
                  (= ":paid" (:status i))
                  (:scheduled-payment i))
         [buttons/fa-icon {:icon "fa-undo"
                           :class (status/class-for (get unautopay-states (:id i)))
                           :event [::unautopay i]}])]]]))

(defn invoice-table [{:keys [id check-boxes overrides actions data-page checkable-fn]}]
  (let [selected-client @(re-frame/subscribe [::subs/client])
        {:keys [data status table-params]} @(re-frame/subscribe [::data-page/page data-page])
        
        selected-client @(re-frame/subscribe [::subs/client])
        is-loading? (= :loading (:state status))
        is-sorted-by-vendor? (and (= "vendor" (:sort-key (first (:sort table-params))))
                                  (not is-loading?)
                                  (or (apply <= (map (comp :name :vendor) (:data data)))
                                      (apply >= (map (comp :name :vendor) (:data data)))))
        
        [invoice-groups] (if is-sorted-by-vendor?
                           (reduce
                            (fn [[acc last-vendor] invoice]
                              (if (not= (:id (:vendor invoice))
                                        last-vendor)
                                [(update-in acc [(clojure.core/count acc)] #(conj (or % []) invoice))
                                 (:id (:vendor invoice))]
                                [(update-in acc [(dec (clojure.core/count acc))] #(conj (or % []) invoice))
                                 (:id (:vendor invoice))]))
                            [[] nil]
                            (:data data))
                           [[(:data data)]])]
    [grid/grid {:data-page data-page
                :check-boxes? check-boxes
                :column-count (if selected-client 8 9)}
     [grid/controls data
      [:div.level-item
       "Outstanding " (nf (:outstanding data))]]
     (for [invoices invoice-groups]
       ^{:key (or (:id (first invoices)) "init")}
       [grid/table {:fullwidth true}
        [grid/header {}
         [grid/row {}
          (when-not selected-client
            [grid/sortable-header-cell {:sort-key "client" :sort-name "Client"} "Client"])
          [grid/sortable-header-cell {:sort-key "vendor" :sort-name "Vendor"}
           (if is-sorted-by-vendor?
             (:name (:vendor (first invoices)))
             "Vendor")]

          [grid/sortable-header-cell {:sort-key "invoice-number" :sort-name "Invoice Number"} "Invoice #"]
          [grid/sortable-header-cell {:sort-key "date" :sort-name "Date" :style {:width "8em"}} "Date"]
          [grid/sortable-header-cell {:sort-key "due" :sort-name "Due" :style {:width "8em"} :class "is-hidden-mobile"} "Due"]
          [grid/sortable-header-cell {:sort-key "location" :sort-name "Location" :style {:width "5em"}} "Loc"]
          [grid/sortable-header-cell {:sort-key "total" :sort-name "Total" :style {:width "8em"} :class "has-text-right"} "Total"]
          
          [grid/sortable-header-cell {:sort-key "outstanding-balance" :sort-name "Outstanding" :style {:width "10em"} :class "has-text-right"} "Outstanding"]
          [grid/header-cell {:style {:width "14rem" }}]
          ]]

        [grid/body
         (for [{:keys [client payments expense-accounts invoice-number date due total outstanding-balance id vendor] :as i} invoices]
           ^{:key id}
           [row {:invoice i
                 :selected-client selected-client
                 :checkable? (if checkable-fn
                               (checkable-fn i)
                               true)
                 :actions actions
                 :overrides overrides}])]])]))
