(ns ajet.chat.shared.db-integration-test
  (:require [clojure.test :refer [deftest testing is use-fixtures]]
            [ajet.chat.shared.db :as db]
            [ajet.chat.test-helpers :as th]))

(use-fixtures :once th/with-db-suite)
(use-fixtures :each th/with-clean-db)

;;; ---------------------------------------------------------------------------
;;; Connection / datasource tests
;;; ---------------------------------------------------------------------------

(deftest datasource-is-usable
  (testing "datasource can execute a simple query"
    (let [result (db/execute-sql! th/*ds* ["SELECT 1 AS n"])]
      (is (= [{:n 1}] result)))))

(deftest execute!-with-honeysql
  (testing "execute! runs HoneySQL map and returns results"
    (let [result (db/execute! th/*ds* {:select [[1 :n]]})]
      (is (= [{:n 1}] result)))))

(deftest execute-one!-returns-single
  (let [result (db/execute-one! th/*ds* {:select [[42 :answer]]})]
    (is (= {:answer 42} result))))

(deftest execute-one!-returns-nil-for-no-match
  (let [result (db/execute-one! th/*ds*
                 {:select [:*] :from [:users] :where [:= :id [:cast (th/uuid) :uuid]]})]
    (is (nil? result))))

;;; ---------------------------------------------------------------------------
;;; Insert / read-back tests
;;; ---------------------------------------------------------------------------

(deftest insert-and-read-user
  (let [user (th/create-user! {:username "testuser" :display-name "Test User"})
        found (db/execute-one! th/*ds*
                {:select [:*] :from [:users] :where [:= :id (:id user)]})]
    (is (some? found))
    (is (= "testuser" (:username found)))))

(deftest insert-returns-generated-columns
  (let [user (th/create-user!)]
    (is (some? (:id user)))
    (is (some? (:created-at user)))))

;;; ---------------------------------------------------------------------------
;;; Transaction tests
;;; ---------------------------------------------------------------------------

(deftest with-transaction-commits-on-success
  (db/with-transaction [tx th/*ds*]
    (th/create-user! {:username "tx-user"}))
  (let [result (db/execute-one! th/*ds*
                 {:select [:*] :from [:users] :where [:= :username "tx-user"]})]
    (is (some? result))))

(deftest with-transaction-rolls-back-on-exception
  (try
    (db/with-transaction [tx th/*ds*]
      (db/execute! tx
        {:insert-into :users
         :values [{:id [:cast (th/uuid) :uuid] :username "rollback-user" :email ""}]})
      (throw (Exception. "force rollback")))
    (catch Exception _))
  (let [result (db/execute-one! th/*ds*
                 {:select [:*] :from [:users] :where [:= :username "rollback-user"]})]
    (is (nil? result))))

;;; ---------------------------------------------------------------------------
;;; Migration tests
;;; ---------------------------------------------------------------------------

(deftest migrate!-is-idempotent
  (testing "calling migrate! again doesn't throw"
    (is (nil? (db/migrate! th/*ds* {:location "migrations"})))))

(deftest tables-exist-after-migration
  (let [tables (db/execute! th/*ds*
                 {:select [:table-name]
                  :from   [:information-schema.tables]
                  :where  [:= :table-schema "public"]})
        names  (set (map :table-name tables))]
    (is (contains? names "users"))
    (is (contains? names "communities"))
    (is (contains? names "channels"))
    (is (contains? names "messages"))))
