(ns auto-ap.integration.rule-matching
  (:require
   [auto-ap.integration.util :refer [wrap-setup]]
   [auto-ap.rule-matching :as sut]
   [clojure.test :as t]))

(t/use-fixtures :each wrap-setup)

(def base-transaction #:transaction {:amount -12.0
                                     :date #inst "2014-01-02T08:00:00.000-00:00"
                                     :bank-account 456
                                     :client 123
                                     :post-date #inst "2014-01-04T08:00:00.000-00:00"
                                     :account-id 1234
                                     :description-original "original-description"
                                     :status "POSTED"
                                     :id "6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b"
                                     :raw-id "1"
                                     :approval-status :transaction-approval-status/unapproved
                                     :description-simple "simple-description"})



(t/deftest rule-applying-fn
  (t/testing "Should apply if description matches"
    (t/is (sut/rule-applies?
           base-transaction
           {:transaction-rule/description #"original-description"
            :transaction-rule/transaction-approval-status :transaction-approval-status/approved}))
    
    (t/is (not (sut/rule-applies?
                base-transaction
                {:transaction-rule/description #"xxx"
                 :transaction-rule/transaction-approval-status :transaction-approval-status/approved}))))

  (t/testing "Should do nothing if there are no rules"
    (let [process (sut/rule-applying-fn [])]
      (t/is (= base-transaction
               (process base-transaction ["NG"])))))

  (t/testing "Should approve a simple rule"
    (let [process (sut/rule-applying-fn [{:transaction-rule/description "simple-description"
                                          :transaction-rule/transaction-approval-status :transaction-approval-status/approved}])
          transaction (assoc base-transaction :transaction/description-original "simple-description")]
      (t/is (= :transaction-approval-status/approved 
               (:transaction/approval-status (process transaction ["NG"]))))))

  (t/testing "spread cents"
    (t/testing "Should split evenly"
      (t/is (= [50 50] (sut/spread-cents 100 2)))
      (t/is (= [50 49] (sut/spread-cents 99 2)))
      (t/is (= [34 33 33] (sut/spread-cents 100 3)))
      (t/is (= [100] (sut/spread-cents 100 1)))
      (t/is (= [2 2] (sut/spread-cents 4 2)))
      (t/is (= [2 1 1] (sut/spread-cents 4 3)))
      (t/is (= [2 2 1] (sut/spread-cents 5 3)))
      (t/is (= [2 1 1 1] (sut/spread-cents 5 4)))
      (t/is (= [3 2 2] (sut/spread-cents 7 3)))
      (t/is (= [2 2 2 1] (sut/spread-cents 7 4)))
      (t/is (= [2 1] (sut/spread-cents 3 2)))
      (t/is (= [1 1 1] (sut/spread-cents 3 4)))

      (doseq [x (range 20)]
        (t/is (= 11323 (reduce + 0 (sut/spread-cents 11323 (inc x))))))))

  (t/testing "Should spread across locations"
    (let [process (sut/rule-applying-fn [{:transaction-rule/description "simple-description"
                                          :transaction-rule/accounts [{:transaction-rule-account/location "Shared"
                                                                       :transaction-rule-account/account {:account/numeric-code 2500}
                                                                       :transaction-rule-account/percentage 1.0}]
                                          :transaction-rule/transaction-approval-status :transaction-approval-status/approved}])
          transaction (assoc base-transaction :transaction/description-original "simple-description" :transaction/amount 1.0)]
      (t/is (= [1.0]
               (map :transaction-account/amount (:transaction/accounts (process transaction ["NG"])))))
      (t/is (= [0.5 0.5]
               (map :transaction-account/amount (:transaction/accounts (process transaction ["NG" "BT"])))))
      (t/is (= [0.34 0.33 0.33]
               (map :transaction-account/amount (:transaction/accounts (process transaction ["NG" "BT" "DE"])))))
      (t/is (= [0.01 0.01]
               (map :transaction-account/amount (:transaction/accounts (process (assoc transaction :transaction/amount 0.02) ["NG" "BT" "DE"])))))
      (t/is (= [0.02 0.01 0.01]
               (map :transaction-account/amount (:transaction/accounts (process (assoc transaction :transaction/amount 0.04) ["NG" "BT" "DE"])))))
      )))
