(ns auto-ap.views.components.vendor-dialog
  (:require [re-frame.core :as re-frame]
            [auto-ap.views.utils :refer [dispatch-event horizontal-field bind-field with-user with-is-admin? active-when account->match-text]]
            [auto-ap.views.components.address :refer [address-field]]
            [auto-ap.views.components.typeahead :refer [typeahead-v3]]
            [auto-ap.views.components.dropdown :refer [drop-down drop-down-contents]]
            [auto-ap.events :as events]
            [clj-fuzzy.metrics :refer [jaccard jaro-winkler]]
            [clojure.spec.alpha :as s]
            [clojure.string :as str]
            [auto-ap.entities.vendors :as entity]
            [auto-ap.entities.contact :as contact]
            [auto-ap.subs :as subs]
            [auto-ap.forms :as forms]
            [auto-ap.views.components.modal :as modal]
            [auto-ap.status :as status]))

(re-frame/reg-sub
 ::can-submit
 :<- [::forms/form ::vendor-form]
 (fn [form]
   (s/valid? ::entity/vendor (:data form))))

(defn ngrams [text len]
  (mapv #(.toLowerCase (str/join "" %))
        (partition len 1 text)))

(re-frame/reg-event-db
  ::settled
  [(forms/in-form ::vendor-form)]
  (fn [{:keys [data] :as form} _]
    form
    #_(let [vendors @(re-frame/subscribe [::subs/vendors])
          text (.toLowerCase (:name data ""))
          matching-vendor (when (> (count text) 5)
                            (->> vendors 
                                 (filter #(not= (:id %) (:id data)))
                                 (map :name)
                                 (mapcat
                                  (fn [v] (mapv
                                           (fn [n] [v (jaro-winkler text n ) n text])
                                           (ngrams v (count text)))))
                                 (filter #(> (second %) 0.9))
                                 
                                 (map first)
                                 first))]
      (if matching-vendor
        (assoc form :warning (str "Are you sure you don't mean " matching-vendor "?"))
        (dissoc form :warning)))))

(re-frame/reg-event-db
 ::removed-override
 [(forms/in-form ::vendor-form)]
 (fn  [form [_ override-key index]]

   (update-in form [:data override-key]
           (fn [overrides]
             (reduce
              (fn [overrides [i override]]
                (if (= i index)
                  overrides
                  (conj overrides override)))
              []
              (map vector (range) overrides))))))




(re-frame/reg-event-db
 ::changed
 [(forms/settles {:key ::vendor-form
            :time 500
            :event [::settled]})]
 (forms/change-handler
  ::vendor-form
  (fn  [data field value]
    (let [[override-key? i?] field]
      (if (and (#{:account-overrides :terms-overrides :schedule-payment-dom} override-key?)
               (nil? (get-in data [override-key? i? :key])))
        [[override-key? i? :key] (random-uuid)]
        [])))))

(re-frame/reg-event-fx
 ::save-complete
 [(forms/triggers-stop ::vendor-form)]
 (fn  [{:keys [db]} [_ {vendor :upsert-vendor} ]]
   {:dispatch [::modal/modal-closed ]
    :db (-> db (assoc-in [:vendors (:id vendor)] vendor))}))

(re-frame/reg-event-fx
 ::save
 [with-user with-is-admin? (forms/triggers-loading ::vendor-form) (forms/in-form ::vendor-form)]
 (fn  [{:keys [user is-admin?] {{:keys [name hidden print-as terms invoice-reminder-schedule primary-contact automatically-paid-when-due schedule-payment-dom secondary-contact address default-account terms-overrides account-overrides id legal-entity-tin legal-entity-tin-type legal-entity-first-name legal-entity-last-name legal-entity-middle-name legal-entity-1099-type] :as data} :data} :db} _]
   (println data)
   (when (s/valid? ::entity/vendor data)
     { :graphql
      {:token user
       :owns-state {:single ::vendor-form}
       :query-obj {:venia/operation {:operation/type :mutation
                                     :operation/name "UpsertVendor"} :venia/queries [{:query/data [:upsert-vendor
                                                                                                   {:vendor  (doto (cond-> {:id id
                                                                                                                           :name name
                                                                                                                           :print-as print-as
                                                                                                                           :terms terms
                                                                                                                           :default-account-id (:id default-account)
                                                                                                                           :address address
                                                                                                                           :primary-contact primary-contact
                                                                                                                           :secondary-contact secondary-contact
                                                                                                                           :invoice-reminder-schedule invoice-reminder-schedule}
                                                                                                                    is-admin? (assoc :hidden hidden
                                                                                                                                     :terms-overrides (mapv
                                                                                                                                                       (fn [{:keys [client override id]}]
                                                                                                                                                         {:id id
                                                                                                                                                          :client-id (:id client)
                                                                                                                                                          :terms override})
                                                                                                                                                       terms-overrides)
                                                                                                                                     :account-overrides (mapv
                                                                                                                                                         (fn [{:keys [client override id]}]
                                                                                                                                                           {:id id
                                                                                                                                                            :client-id (:id client)
                                                                                                                                                            :account-id (:id override)})
                                                                                                                                                         account-overrides)
                                                                                                                                     :schedule-payment-dom (mapv
                                                                                                                                                            (fn [{:keys [client override id]}]
                                                                                                                                                              {:id id
                                                                                                                                                               :client-id (:id client)
                                                                                                                                                               :dom override})
                                                                                                                                                            schedule-payment-dom)
                                                                                                                                     :automatically-paid-when-due (mapv
                                                                                                                                                                   :id
                                                                                                                                                                   automatically-paid-when-due)
                                                                                                                                     :legal-entity-first-name legal-entity-first-name
                                                                                                                                     :legal-entity-middle-name legal-entity-middle-name
                                                                                                                                     :legal-entity-last-name legal-entity-last-name
                                                                                                                                     :legal-entity-tin legal-entity-tin
                                                                                                                                     :legal-entity-tin-type (some-> legal-entity-tin-type clojure.core/name not-empty keyword)
                                                                                                                                     :legal-entity-1099-type (some-> legal-entity-1099-type  clojure.core/name not-empty keyword)
                                                                                                                                     ))
                                                                                                               println)}
                                                 events/vendor-query]}]}  
       :on-success [::save-complete]}})))

(defn client-list [{:keys [override-key override-value-key change-event default-key data]} template]
  (let [clients @(re-frame/subscribe [::subs/clients])
        is-admin? @(re-frame/subscribe [::subs/is-admin?])]
    (when is-admin?
      [horizontal-field
       [:label.label "Client"]
       (doall 
        (for [[i override] (map vector (range) (conj (override-key data) {:key (random-uuid)}))]
          ^{:key (or
                  (:id override)
                  (:key override))}
          [:div.columns 
           [:div.column
            [bind-field
             [typeahead-v3 {:entities clients
                            :entity->text :name
                            :type "typeahead-v3"
                            :field [override-key i]
                            :event change-event
                            :subscription data}]]]
           
           [:div.column.is-1
            [:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]))])))

(defn default-with-overrides [{:keys [override-key override-value-key change-event default-key data mandatory?]} template]
  (let [clients @(re-frame/subscribe [::subs/clients])
        is-admin? @(re-frame/subscribe [::subs/is-admin?])]
    [:div 
     [horizontal-field
      [:label.label [:span "Default"
                     (when mandatory?
                       [:span.has-text-danger " *"])]]
      [bind-field
       (template default-key nil)
       #_(assoc-in template [1 :field ] default-key)]]
     (when is-admin?
       [horizontal-field
        [:label.label "Overrides"]
        (doall 
         (for [[i override] (map vector (range) (conj (override-key data) {:key (random-uuid)}))]
           ^{:key (or
                   (:id override)
                   (:key override))}
           [:div.columns 
            [:div.column
             [bind-field
              [typeahead-v3 {:entities clients
                             :entity->text :name
                             :type "typeahead-v3"
                             :field [override-key i :client]
                             :event change-event
                             :subscription data}]]]
            [:div.column
             [bind-field
              (template 
               [override-key i :override]
               (get-in data [override-key i :client])
               )]]
            [:div.column.is-1
             [:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]))])]))

(defn client-overrides [{:keys [override-key override-value-key change-event data mandatory?]} template]
  (let [clients @(re-frame/subscribe [::subs/clients])
        is-admin? @(re-frame/subscribe [::subs/is-admin?])]
    (when is-admin?
      [horizontal-field
       [:label.label "Overrides"]
       (doall 
        (for [[i override] (map vector (range) (conj (override-key data) {:key (random-uuid)}))]
          ^{:key (or
                  (:id override)
                  (:key override))}
          [:div.columns 
           [:div.column
            [bind-field
             [typeahead-v3 {:entities clients
                            :entity->text :name
                            :type "typeahead-v3"
                            :field [override-key i :client]
                            :event change-event
                            :subscription data}]]]
           [:div.column
            [bind-field
             (template 
              [override-key i :override]
              (get-in data [override-key i :client])
              )]]
           [:div.column.is-1
            [:a.button {:on-click (dispatch-event [::removed-override override-key i])} [:span.icon [:span.icon-remove]]]]]))])))

(defn form-content [{:keys [data change-event]}]
  (let [accounts @(re-frame/subscribe [::subs/accounts])
        clients @(re-frame/subscribe [::subs/clients])
        is-admin? @(re-frame/subscribe [::subs/is-admin?])]
    [:div 
     [horizontal-field
      [:label.label [:span "Name "
                     [:span.has-text-danger "*"]]]
      [:div.control
       [bind-field
        [:input.input {:type "text"
                       :auto-focus true
                       :field :name
                       :spec ::entity/name
                       :event change-event
                       :subscription data}]]]]

     [horizontal-field
      [:label.label "Print Checks As"]
      [:div.control
       [bind-field
        [:input.input {:type "text"
                       :field :print-as
                       :spec ::entity/print-as
                       :event change-event
                       :subscription data}]]]]

     (when is-admin?
       [horizontal-field
        [:label.label "Hidden"]
        [:div.control
         [bind-field
          [:input {:type "checkbox"
                   :field :hidden
                   :spec ::entity/hidden
                   :event change-event
                   :subscription data}]]]])
     [:h2.subtitle "Terms"]
     [default-with-overrides {:data data :change-event change-event
                              :default-key :terms
                              :override-key :terms-overrides}
      (fn [field client]
        [:input.input {:type "number"
                       :step "1"
                       :style {:width "4em"}
                       :field field
                       :size 3
                       :spec ::entity/terms
                       :event change-event
                       :subscription data}])]

     [:h2.subtitle "Schedule payment when due"]
     [client-list {:data data :change-event change-event
                   :override-key :automatically-paid-when-due}]

     [:h2.subtitle "Schedule payment on day of month"]
     [client-overrides {:data data :change-event change-event
                        :mandatory? true
                        :override-key :schedule-payment-dom}
      (fn [field client]
        [:input.input {:type "number"
                       :step "1"
                       :style {:width "5em"}
                       :field field
                       :size 3
                       :spec ::entity/dom
                       :event change-event
                       :subscription data}])]
     
     [:h2.subtitle "Expense Accounts"]
     [default-with-overrides {:data data :change-event change-event
                              :mandatory? true
                              :default-key :default-account
                              :override-key :account-overrides}
      (fn [field client]
        
        [typeahead-v3 {:entities @(re-frame/subscribe [::subs/accounts client])
                       :entity->text account->match-text
                       :field field
                       :type "typeahead-v3"
                       :event change-event
                       :subscription data}])]

     [:h2.subtitle "Address"]
     [address-field {:field [:address]
                     :event change-event
                     :subscription data}]
     [:h2.subtitle "Contact"]
     [horizontal-field
      [:label.label "Primary"]
      [:div.control.has-icons-left
       [bind-field
        [:input.input.is-expanded {:type "text"
                                   :field [:primary-contact :name]
                                   :spec ::contact/name
                                   :event change-event
                                   :subscription data}]]
       [:span.icon.is-small.is-left
        [:i.fa.fa-user]]]

      [:div.control.has-icons-left
       [:span.icon.is-small.is-left
        [:i.fa.fa-envelope]]
       [bind-field
        [:input.input {:type "email"
                       :field [:primary-contact :email]
                       :spec ::contact/email
                       :event change-event
                       :subscription data}]]]

      [:div.control.has-icons-left
       [bind-field
        [:input.input {:type "phone"
                       :field [:primary-contact :phone]
                       :spec ::contact/phone
                       :event change-event
                       :subscription data}]]
       [:span.icon.is-small.is-left
        [:i.fa.fa-phone]]]]
     [horizontal-field
      [:label.label "Secondary"]
      [:div.control.has-icons-left
       [bind-field
        [:input.input.is-expanded {:type "text"
                                   :field [:secondary-contact :name]
                                   :spec ::contact/name
                                   :event change-event
                                   :subscription data}]]
       [:span.icon.is-small.is-left
        [:i.fa.fa-user]]]
      [:div.control.has-icons-left
       [:span.icon.is-small.is-left
        [:i.fa.fa-envelope]]
       [bind-field
        [:input.input {:type "email"
                       :field [:secondary-contact :email]
                       :spec ::contact/email
                       :event change-event
                       :subscription data}]]]
      [:div.control.has-icons-left
       [bind-field
        [:input.input {:type "phone"
                       :field [:secondary-contact :phone]
                       :spec ::contact/phone
                       :event change-event
                       :subscription data}]]
       [:span.icon.is-small.is-left
        [:i.fa.fa-phone]]]]
     [horizontal-field
      [:label.label "Invoice Reminders"]
      [:div.control
       [:label.radio 
        [bind-field
         [:input {:type "radio"
                  :name "schedule"
                  :value "Weekly"
                  :field :invoice-reminder-schedule
                  :spec ::entity/invoice-reminder-schedule
                  :event change-event
                  :subscription data}]]
        " Send weekly"]

       [:label.radio 
        [bind-field
         [:input {:type "radio"
                  :name "schedule"
                  :value "Never"
                  :field :invoice-reminder-schedule
                  :spec ::entity/invoice-reminder-schedule
                  :event change-event
                  :subscription data}]]
        " Never"]]]

     [:h2.subtitle "Legal Entity"]
     [horizontal-field
      [:label.label "Name"]
      [:div.control
       [bind-field
        [:input.input {:type "text"
                       :placeholder "First Name"
                       :field [:legal-entity-first-name]
                       :spec ::contact/name
                       :event change-event
                       :subscription data}]]]

      [:div.control
       [bind-field
        [:input.input {:type "text"
                       :placeholder "Middle Name"
                       :field [:legal-entity-middle-name]
                       :spec ::contact/name
                       :event change-event
                       :subscription data}]]]

      [:div.control
       [bind-field
        [:input.input {:type "text"
                       :placeholder "Last Name"
                       :field [:legal-entity-last-name]
                       :spec ::contact/name
                       :event change-event
                       :subscription data}]]]]
     [horizontal-field
      [:label.label "TIN"]
      [:div.control
       [bind-field
        [:input.input {:type "text"
                       :placeholder "SSN or EIN"
                       :field [:legal-entity-tin]
                       :size "12"
                       :spec ::contact/name
                       :event change-event
                       :subscription data}]]]

      [:div.control
       [:div.select
        [bind-field
         [:select {:type "select"
                   :field [:legal-entity-tin-type]
                   :event change-event
                   :subscription data}
          [:option  {:value nil} ""]
          [:option {:value "ein"} "EIN"]
          [:option {:value "ssn"} "SSN"]]]]]]

     [horizontal-field
      [:label.label "1099 Type"]
      [:div.control
       [:div.select
        [bind-field
         [:select {:type "select"
                   :field [:legal-entity-1099-type]
                   :event change-event
                   :subscription data}
          [:option {:value nil} ""]
          [:option {:value "none"} "Don't 1099"]
          [:option {:value "misc"} "Misc"]
          [:option {:value "landlord"} "Landlord"]]]]]]
     ]))

(defn vendor-dialog [{:keys [save-event] }]
  (let [clients @(re-frame/subscribe [::subs/clients])
        all-vendors @(re-frame/subscribe [::subs/vendors])
        {:keys [data error warning] :as f} @(re-frame/subscribe [::forms/form ::vendor-form])
        change-event [::changed]]
    [:div
     [form-content {:data data :change-event change-event}]]))

(re-frame/reg-event-fx
 ::started
 (fn  [{:keys [db]} [_ vendor]]
   {:db (-> db (forms/start-form ::vendor-form (-> vendor
                                                   (update :account-overrides #(mapv
                                                                                (fn [ao]
                                                                                  {:id (:id ao)
                                                                                   :client (:client ao)
                                                                                   :override (:account ao)})
                                                                                %))

                                                   (update :schedule-payment-dom #(mapv
                                                                                   (fn [spdom]
                                                                                     {:id (:id spdom)
                                                                                      :client (:client spdom)
                                                                                      :override (:dom spdom)})
                                                                                   %))
                                                   (update :terms-overrides #(mapv
                                                                              (fn [to]
                                                                                {:id (:id to)
                                                                                 :client (:client to)
                                                                                 :override (:terms to)})
                                                                              %))
                                                   (update :automatically-paid-when-due #(mapv identity %))
                                                   (update :hidden #(if (nil? %)
                                                                      false
                                                                      %))
                                                   (update :default-account
                                                           (fn [da]
                                                             @(re-frame/subscribe [::subs/account nil (:id da)]))))))
    :dispatch [::modal/modal-requested
               {:title "Vendor"
                :class "is-wide"
                :body [vendor-dialog]
                :confirm {:value "Save Vendor"
                          :status-from [::status/single ::vendor-form]
                          :class "is-primary"
                          :on-click (dispatch-event [::save])
                          :can-submit [::can-submit]
                          :close-event [::status/completed ::vendor-form]}}]}))
