(ns ajet.chat.api.middleware-unit-test
  (:require [clojure.test :refer [deftest testing is]]
            [clojure.data.json :as json]
            [ajet.chat.api.middleware :as mw]))

;;; ---------------------------------------------------------------------------
;;; json-response tests
;;; ---------------------------------------------------------------------------

(deftest json-response-default-200
  (let [resp (mw/json-response {:ok true})]
    (is (= 200 (:status resp)))
    (is (= "application/json" (get-in resp [:headers "Content-Type"])))
    (is (= {:ok true} (json/read-str (:body resp) :key-fn keyword)))))

(deftest json-response-custom-status
  (let [resp (mw/json-response 201 {:id "abc"})]
    (is (= 201 (:status resp)))))

;;; ---------------------------------------------------------------------------
;;; error-response tests
;;; ---------------------------------------------------------------------------

(deftest error-response-structure
  (let [resp (mw/error-response 404 "NOT_FOUND" "Entity not found")
        body (json/read-str (:body resp) :key-fn keyword)]
    (is (= 404 (:status resp)))
    (is (= "NOT_FOUND" (get-in body [:error :code])))
    (is (= "Entity not found" (get-in body [:error :message])))))

(deftest error-response-with-details
  (let [resp (mw/error-response 422 "VALIDATION_ERROR" "Bad input" {:field "name"})
        body (json/read-str (:body resp) :key-fn keyword)]
    (is (= "name" (get-in body [:error :details :field])))))

;;; ---------------------------------------------------------------------------
;;; wrap-exception-handler tests
;;; ---------------------------------------------------------------------------

(deftest wrap-exception-handler-passes-normal
  (let [handler (mw/wrap-exception-handler (fn [_] (mw/json-response {:ok true})))
        resp (handler {})]
    (is (= 200 (:status resp)))))

(deftest wrap-exception-handler-catches-validation-error
  (let [handler (mw/wrap-exception-handler
                  (fn [_] (throw (ex-info "Bad" {:type :ajet.chat/validation-error}))))
        resp (handler {})]
    (is (= 422 (:status resp)))))

(deftest wrap-exception-handler-catches-not-found
  (let [handler (mw/wrap-exception-handler
                  (fn [_] (throw (ex-info "Missing" {:type :ajet.chat/not-found}))))
        resp (handler {})]
    (is (= 404 (:status resp)))))

(deftest wrap-exception-handler-catches-generic-exception
  (let [handler (mw/wrap-exception-handler
                  (fn [_] (throw (Exception. "boom"))))
        resp (handler {})]
    (is (= 500 (:status resp)))))

;;; ---------------------------------------------------------------------------
;;; wrap-user-context tests
;;; ---------------------------------------------------------------------------

(deftest wrap-user-context-extracts-headers
  (let [handler (mw/wrap-user-context identity)
        req     {:headers {"x-user-id" "user-123"
                           "x-user-role" "admin"
                           "x-community-id" "comm-456"}}
        result  (handler req)]
    (is (= "user-123" (:user-id result)))
    (is (= "admin" (:user-role result)))
    (is (= "comm-456" (:community-id result)))))

(deftest wrap-user-context-handles-missing-headers
  (let [handler (mw/wrap-user-context identity)
        result  (handler {:headers {}})]
    (is (nil? (:user-id result)))))

;;; ---------------------------------------------------------------------------
;;; wrap-json-body tests
;;; ---------------------------------------------------------------------------

(deftest wrap-json-body-parses-json
  (let [handler (mw/wrap-json-body identity)
        req {:headers {"content-type" "application/json"}
             :body (java.io.ByteArrayInputStream.
                     (.getBytes "{\"name\":\"test\"}" "UTF-8"))}
        result (handler req)]
    (is (= "test" (get-in result [:body-params :name])))))

(deftest wrap-json-body-returns-400-for-invalid-json
  (let [handler (mw/wrap-json-body identity)
        req {:headers {"content-type" "application/json"}
             :body (java.io.ByteArrayInputStream.
                     (.getBytes "{invalid" "UTF-8"))}
        resp (handler req)]
    (is (= 400 (:status resp)))))
