(ns auto-ap.routes.invoices
  (:require [auto-ap.db.companies :as companies]
            [auto-ap.db.vendors :as vendors]
            [auto-ap.db.invoices :as invoices]
            [auto-ap.db.utils :refer [query]]
            [auto-ap.utils :refer [by]]
            [auto-ap.parse :as parse]
            [auto-ap.routes.utils :refer [wrap-secure]]
            [compojure.core :refer [GET POST context defroutes
                                    wrap-routes]]
            [clojure.string :as str]))

(defn reset-id [i]
  (update i :invoice-number
          (fn [n] (if (re-matches #"#+" n)
                   nil
                   n))))

(defn assoc-company-code [i]
  (assoc i :company-code (first (str/split (:location i) #"-" ))))

(defn parse-company [{:keys [company-code company]} companies]
  (if-let [id (:id (or (companies company-code)
                       (companies company)))]
    id
    (throw (Exception. (str "Company code '" company-code "' and company named '" company "' not found.")))))

(defn parse-invoice-number [{:keys [invoice-number]}]
  (or invoice-number ""))

(defn parse-vendor [{:keys [vendor-name check]} vendors]
  (let [id (:id (vendors vendor-name))]
    (cond id
          id

          (= "Cash" check)
          nil

          :else
          (throw (Exception. (str "Vendor '" vendor-name "' not found."))))))

(defn parse-amount [i]
  (try
    (Double/parseDouble (str/replace (or  (second
                                           (re-matches #"[^0-9\.,]*([0-9\.,]+)[^0-9\.,]*" (:amount i)))
                                          "0")
                                     #"," ""))
    (catch Exception e
      (throw (Exception. (str "Could not parse total from value '" (:amount i) "'") e)))))

(defn parse-date [{:keys [raw-date]}]
  (try 
    (parse/parse-value :clj-time "MM/dd/yyyy" raw-date)
    (catch Exception e
      (throw (Exception. (str "Could not parse date from '" raw-date "'") e)))))

(defn parse-or-error [key f]
  (fn [x]
    (try
      (assoc x key (f x))
      (catch Exception e
        (update x :errors conj {:info (.getMessage e)
                                :details (str e)})))))

(defroutes routes
  (wrap-routes
   (context "/invoices" []
            (GET "/" []
                 {:status 200
                  :body (pr-str (invoices/get-all))
                  :headers {"Content-Type" "application/edn"}})

            (GET "/unpaid" {:keys [query-params] :as r}
                 {:status 200
                  :body (pr-str (invoices/get-unpaid (query-params "company")))
                  :headers {"Content-Type" "application/edn"}})

            (GET "/pending" {:keys [query-params]}
                 {:status 200
                  :body (pr-str (invoices/get-pending (query-params "company")))
                  :headers {"Content-Type" "application/edn"}})
            (POST "/" {:keys [edn-params]}
                  (invoices/insert-multi! (:rows edn-params))
                  {:status 200
                   :body (pr-str (invoices/get-all))
                   :headers {"Content-Type" "application/edn"}})
            (POST "/approve" {:keys [query-params]}
                  (invoices/approve)
                  {:status 200
                   :body (pr-str (invoices/get-pending (query-params "company")))
                   :headers {"Content-Type" "application/edn"}})
            (POST "/reject" {:keys [query-params]}
                  (invoices/reject)
                  {:status 200
                   :body (pr-str (invoices/get-pending (query-params "company")))
                   :headers {"Content-Type" "application/edn"}})
            (POST "/upload"
                  {{  files "file"} :params :as params}
                  (let [{:keys [filename tempfile]} files
                        companies (companies/get-all)
                        vendors (vendors/get-all)]
                    (invoices/import (parse/parse-file (.getPath tempfile) filename) companies vendors)
                    {:status 200
                     :body (pr-str (invoices/get-pending ((:query-params params ) "company")))
                     :headers {"Content-Type" "application/edn"}}))

            (POST "/upload-integreat"
                  {{:keys [excel-rows]}  :edn-params}
                  (let [columns [:raw-date :vendor-name :check :location :invoice-number :amount :company :bill-entered :bill-rejected :added-on :exported-on]
                        
                        all-vendors (by :name (vendors/get-all)) 

                        all-companies (companies/get-all)
                        all-companies (merge  (by :code all-companies) (by :name all-companies))
                        
                        rows (->> (str/split excel-rows #"\n" )
                                  (map #(str/split % #"\t"))
                                  (map #(into {} (map (fn [c k] [k c] ) % columns)))
                                  (map reset-id)
                                  (map assoc-company-code)
                                  (map (parse-or-error :company-id #(parse-company % all-companies)))
                                  (map (parse-or-error :vendor-id #(parse-vendor % all-vendors)))
                                  (map (parse-or-error :invoice-number parse-invoice-number))
                                  (map (parse-or-error :total parse-amount))
                                  (map (parse-or-error :date parse-date)))
                        error-rows (filter :errors rows)
                        vendors-not-found (->> rows
                                               (filter #(and (nil? (:vendor-id %))
                                                             (not= "Cash" (:check %))))
                                               (map :vendor-name)
                                               set)
                        insert-rows (vec (->> (filter #(not (seq (:errors %))) rows)
                                              (map (fn [{:keys [vendor-id total company-id amount date invoice-number]}]
                                                     {:vendor-id vendor-id
                                                      :company-id company-id
                                                      :total total 
                                                      :outstanding-balance total
                                                      :imported true
                                                      :status "unpaid"
                                                      :invoice-number invoice-number
                                                      :date date}))))

                        inserted-row-count (invoices/upsert-multi! insert-rows)
                        already-imported-count (- (count insert-rows) inserted-row-count)
                        ]

                    

                    {:status 200
                     :body (pr-str {:imported inserted-row-count
                                    :already-imported already-imported-count
                                    :vendors-not-found vendors-not-found
                                    :errors (map #(dissoc % :date) error-rows)})
                     :headers {"Content-Type" "application/edn"}}))

            ;; Removing the export view for now...
            #_(wrap-json-response (GET "/export" {:keys [query-params]}
                                       (println query-params)
                                       (doto (invoices/get-unpaid (query-params "company"))
                                         println))))
   wrap-secure))
