(ns auto-ap.parse.csv
  (:require [auto-ap.parse.util :as u]
            [clojure.data.csv :as csv]
            [clojure.java.io :as io]
            [clojure.string :as str]
            [auto-ap.logging :as alog]))

(defn determine
  [[header]]
  (alog/info ::importing :header header)
  (let [csv-type (cond (str/includes? (second header) "Customer's PO No.")
                       :mama-lus

                       (str/includes? (str header) "Ship-To Number")
                       :sysco-style-2

                       (str/includes? (str header) "Closed Date")
                       :sysco-style-1

                       (str/includes? (str header) "Business Unit")
                       :mission

                       (str/includes? (str header) "Document Number")
                       :philz


                       (str/includes? (str header) "DISCOUNT_MESSAGE")
                       :wismettac

                       (str/includes? (str header) "Status")
                       :ledyard

                       (str/includes? (str header) "Due Date")
                       :ledyard

                       (str/includes? (str header) "PARENT CUSTOMER NAME")
                       :worldwide
                       
                       :else
                       nil)]
    (alog/info ::csv-type-determined :type csv-type)
    csv-type))

(defmulti parse-csv
  determine
  :default #_{:clj-kondo/ignore [:unused-binding]}
           (fn default [rows]
             nil))

(defn parse-date-fallover [d fmts]
  (when-let [valid-fmt (->> fmts
                          (filter (fn [f]
                                    (try
                                      (u/parse-value :clj-time f d)
                                      (catch Exception _
                                        nil))
                                    ))
                          (first))]
    (u/parse-value :clj-time valid-fmt d)))

(defmethod parse-csv :sysco-style-1
  [rows]
  (let [header (first rows)]
    (transduce
     (comp (drop 1)
           (map (fn [row]
                  (into {} (map vector header row))))
           (filter (fn [row]
                     (and (not (str/blank? (get row "Invoice Date")))
                          (not (str/blank? (get row "Orig Amt")))
                          (not (str/blank? (get row "Inv #"))))))
           (map (fn [row]
                  {:vendor-code nil
                   :customer-identifier nil
                   :invoice-number (get row "Inv #")
                   :date (parse-date-fallover (get row "Invoice Date") ["M/d/yyyy"])
                   :total (str/replace (get row "Orig Amt") #"[,\$]" "")
                   :text (str/join " " (vals row))
                   :full-text (str/join " " (vals row))})))
     conj
     []
     rows)))

(defmethod parse-csv :sysco-style-2
  [rows]
  
  (let [header (first rows)]
    (transduce
     (comp (drop 1)
           (map (fn [row]
                  (into {} (map vector header row))))
           (map (fn [row]
                  {:vendor-code nil
                   :customer-identifier (str (get row "Ship-To Name") " " (or (get row "Ship-To Number")
                                                                              (get row "﻿\"Ship-To Number\"")))
                   :invoice-number (str/trim (get row "Invoice Number"))
                   :date (parse-date-fallover (get row "Invoice Date") ["yyyy-MM-dd" "M/d/yyyy"])
                   :total (str/replace (get row "Original Amount") #"[,\$]" "")
                   :text (str/join " " (vals row))
                   :full-text (str/join " " (vals row))})))
     conj
     []
     rows)))

(defmethod parse-csv :mama-lus
  [rows]
  (transduce
   (comp (drop 1)
         (map (fn [[_ po-number _ invoice-number invoice-date customer value :as row]]
                {:vendor-code "Mama Lu's Foods"
                 :customer-identifier customer
                 :invoice-number (str po-number "-" invoice-number )
                 :date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss"  "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"])
                 :total (str/replace value #"," "")
                 :text (str/join " " row)
                 :full-text (str/join " " row)})))
   conj
   []
   rows))

(defmethod parse-csv :mission
  [rows]
  (transduce
   (comp (drop 1)
         (map (fn [[ po-number _ invoice-number invoice-date customer value :as row]]
                {:vendor-code "Mama Lu's Foods"
                 :customer-identifier customer
                 :invoice-number (str po-number "-" invoice-number )
                 :date (parse-date-fallover invoice-date ["M/d/yyyy HH:ss"  "M/d/yyyy HH:mm:ss aa" "M/d/yyyy"])
                 :total (str/replace value #"," "")
                 :text (str/join " " row)
                 :full-text (str/join " " row)})))
   conj
   []
   rows))

(defmethod parse-csv :philz
  [rows]
  (transduce
   (comp 
    (filter (fn [[_ _ _ _ _ status _ _ _ ]]
              (= status "Billed")))
    (map (fn [[dt _ doc-number name _ _ _ _ amount :as row]]
           {:vendor-code "PHILZ COFFEE, INC"
            :customer-identifier name
            :invoice-number doc-number
            :date (some-> dt not-empty (parse-date-fallover ["MM/dd/yyyy"]))
            :total (str/replace amount #"," "")
            :text (str/join " " row)
            :full-text (str/join " " row)}))

    
    )
   conj
   []
   (drop 1 rows)))

(defmethod parse-csv :wismettac
  [rows]
  (transduce
   (comp 
    (map (fn [[inv_number inv_dt total :as row]]
           {:vendor-code "Wismettac"
            :invoice-number inv_number
            :date (some-> inv_dt not-empty (parse-date-fallover ["MM/dd/yyyy"]))
            :total (str/replace total #"," "")
            :text (str/join " " row)
            :full-text (str/join " " row)}))
    )
   conj
   []
   (drop 1 rows)))

(defmethod parse-csv :ledyard
  [rows]
  (transduce
    (comp 
      (map (fn [[invoice-number date due amount standard :as row]]
             {:vendor-code    "Performance Food Group - LEDYARD"
              :invoice-number invoice-number
              :date           (some-> date not-empty (parse-date-fallover ["MM/dd/yy"]))
              :due (some-> due not-empty (parse-date-fallover ["MM/dd/yy"]))
              :total          (str/replace amount #"[\$,]" "")
              :text           (str/join " " row)
              :full-text      (str/join " " row)}))
      )
    conj
    []
    (drop 1 rows)))

(defmethod parse-csv :worldwide
  [rows]
  (transduce
    (comp 
      (map (fn [[_ customer-name _ inv date amount :as row]]
             {:vendor-code    "Worldwide Produce"
              :customer-identifier customer-name
              :invoice-number (str/replace inv  #"[=\"]" "")
              :date           (some-> date not-empty (parse-date-fallover ["MM/dd/yy"]))
              :total          (str/replace amount #"[\$,]" "")
              :text           (str/join " " row)
              :full-text      (str/join " " row)}))
      )
    conj
    []
    (drop 1 rows)))

#_{:clj-kondo/ignore [:unused-binding]}
(defmethod parse-csv nil
  [rows]
  nil)

(defn parse-file [file _]
  (with-open [reader (io/reader file)]
    (let [rows (csv/read-csv reader :separator \,)]
      (parse-csv rows))))

