(ns auto-ap.square.core3-test
  (:require
   [auto-ap.datomic :refer [conn]]
   [auto-ap.integration.util :refer [setup-test-data wrap-setup]]
   [auto-ap.square.core3 :as sut]
   [clojure.test :refer [deftest is testing use-fixtures]]
   [datomic.api :as dc]))

(use-fixtures :each wrap-setup)

(def client {:client/code "NGCD"})
(def location {:square-location/client-location "CD"})

(defn- refund-count []
  (count (dc/q '[:find ?e :where [?e :sales-refund/external-id]] (dc/db conn))))

(defn- resolve-refund [id]
  (sut/existing-id (dc/db conn) :sales-refund/external-id "square/refund/" client location id))

(deftest scoped-key-carries-client-and-location
  (testing "the same shape sales order keys already use, so a shared location cannot contend"
    (is (= "square/refund/NGCD-CD-abc" (sut/scoped-key "square/refund/" client location "abc")))
    (is (= "square/charge/NGCD-CD-xyz" (sut/scoped-key "square/charge/" client location "xyz")))))

(deftest legacy-keyed-entity-is-updated-not-duplicated
  (testing "an entity still carrying its unscoped key is found and re-keyed in place.

           This is the sharpest hazard in the migration: these external ids are
           :db.unique/identity, so writing the new key without resolving the old one first
           matches nothing and creates a second entity, orphaning the original."
    (setup-test-data [])
    @(dc/transact conn [{:db/id "r"
                         :sales-refund/external-id "square/refund/abc"
                         :sales-refund/total 10.0}])
    (is (= 1 (refund-count)))
    (let [eid (resolve-refund "abc")]
      (is (some? eid) "resolves an entity carrying the legacy key")
      @(dc/transact conn [{:db/id eid
                           :sales-refund/external-id (sut/scoped-key "square/refund/" client location "abc")
                           :sales-refund/total 10.0}])
      (is (= 1 (refund-count)) "no second entity was created")
      (is (= eid (dc/entid (dc/db conn) [:sales-refund/external-id "square/refund/NGCD-CD-abc"]))
          "the same entity now answers to the scoped key")
      (is (nil? (dc/entid (dc/db conn) [:sales-refund/external-id "square/refund/abc"]))
          "and no longer to the legacy one"))))

(deftest already-scoped-entity-resolves-by-its-new-key
  (testing "re-running the importer after migration finds the entity by the scoped key, so the
           migration is not undone and nothing is duplicated"
    (setup-test-data [])
    @(dc/transact conn [{:db/id "r"
                         :sales-refund/external-id "square/refund/NGCD-CD-abc"
                         :sales-refund/total 10.0}])
    (is (= (dc/entid (dc/db conn) [:sales-refund/external-id "square/refund/NGCD-CD-abc"])
           (resolve-refund "abc")))
    (is (= 1 (refund-count)))))

(deftest unknown-id-resolves-to-nothing
  (testing "a refund never seen before has no id to pin, so the importer creates it fresh"
    (setup-test-data [])
    (is (nil? (resolve-refund "never-seen")))))

(deftest two-clients-on-one-location-get-their-own-entities
  (testing "the point of the re-key: with the client in the key, a second client importing the
           same Square refund creates its own entity instead of taking ownership of the first"
    (setup-test-data [])
    (let [other {:client/code "NGCC"}
          other-loc {:square-location/client-location "CC"}]
      @(dc/transact conn [{:db/id "r"
                           :sales-refund/external-id (sut/scoped-key "square/refund/" client location "shared")
                           :sales-refund/total 10.0}])
      (is (nil? (sut/existing-id (dc/db conn) :sales-refund/external-id "square/refund/" other other-loc "shared"))
          "the second client does not resolve onto the first client's entity")
      @(dc/transact conn [{:db/id "r2"
                           :sales-refund/external-id (sut/scoped-key "square/refund/" other other-loc "shared")
                           :sales-refund/total 10.0}])
      (is (= 2 (refund-count)) "two stable entities, one per client, rather than one that flips"))))

(deftest payouts-and-shifts-are-client-scoped-too
  (testing "expected deposits and cash drawer shifts are fetched per location, so two clients on
           one location collide on them exactly as refunds and charges did"
    (is (= "square/payout/NGCD-CD-po1"
           (sut/scoped-key "square/payout/" client location "po1")))
    (is (= "square/cash-drawer-shift/NGCD-CD-sh1"
           (sut/scoped-key "square/cash-drawer-shift/" client location "sh1")))))

(deftest legacy-keyed-deposit-is-updated-not-duplicated
  (testing "a payout still carrying its unscoped key is found and re-keyed in place"
    (setup-test-data [])
    @(dc/transact conn [{:db/id "d"
                         :expected-deposit/external-id "square/payout/po1"
                         :expected-deposit/total 100.0}])
    (let [eid (sut/existing-id (dc/db conn) :expected-deposit/external-id "square/payout/" client location "po1")]
      (is (some? eid) "resolves the entity carrying the legacy key")
      @(dc/transact conn [{:db/id eid
                           :expected-deposit/external-id (sut/scoped-key "square/payout/" client location "po1")
                           :expected-deposit/total 100.0}])
      (is (= 1 (count (dc/q '[:find ?e :where [?e :expected-deposit/external-id]] (dc/db conn))))
          "no second deposit was created")
      (is (nil? (dc/entid (dc/db conn) [:expected-deposit/external-id "square/payout/po1"]))
          "the legacy key is gone"))))

(deftest two-clients-get-their-own-deposit
  (testing "with the client in the key, a second client importing the same Square payout creates
           its own entity instead of taking ownership of the first"
    (setup-test-data [])
    (let [other {:client/code "NGCC"}
          other-loc {:square-location/client-location "CC"}]
      @(dc/transact conn [{:db/id "d"
                           :expected-deposit/external-id (sut/scoped-key "square/payout/" client location "shared")
                           :expected-deposit/total 100.0}])
      (is (nil? (sut/existing-id (dc/db conn) :expected-deposit/external-id "square/payout/" other other-loc "shared"))
          "the second client does not resolve onto the first client's deposit")
      @(dc/transact conn [{:db/id "d2"
                           :expected-deposit/external-id (sut/scoped-key "square/payout/" other other-loc "shared")
                           :expected-deposit/total 100.0}])
      (is (= 2 (count (dc/q '[:find ?e :where [?e :expected-deposit/external-id]] (dc/db conn))))
          "two stable entities, one per client, rather than one that flips"))))
