(ns ajet.chat.shared.logging
  "Structured logging with trace ID propagation.

   Uses clojure.tools.logging with Logback backend.
   MDC (Mapped Diagnostic Context) carries trace-id, user-id per request."
  (:require [clojure.string :as str]
            [clojure.tools.logging :as log])
  (:import [org.slf4j MDC]))

(defn set-mdc!
  "Set MDC context values from a map. Keys are converted to strings."
  [context-map]
  (doseq [[k v] context-map]
    (when v
      (MDC/put (name k) (str v)))))

(defn clear-mdc!
  "Clear all MDC context values."
  []
  (MDC/clear))

(defmacro with-mdc
  "Execute body with MDC context set. Clears MDC after completion."
  [context-map & body]
  `(let [cm# ~context-map]
     (set-mdc! cm#)
     (try
       ~@body
       (finally
         (clear-mdc!)))))

(def ^:private sensitive-keys
  #{:password :token :secret :secret-key :client-secret :token-hash :authorization})

(defn redact-map
  "Replace sensitive values with [REDACTED] in a map."
  [m]
  (reduce-kv (fn [acc k v]
               (assoc acc k (if (sensitive-keys (keyword (name k)))
                              "[REDACTED]"
                              v)))
             {} m))

(defn wrap-request-logging
  "Ring middleware that logs request/response with timing and MDC context."
  [handler]
  (fn [request]
    (let [method    (str/upper-case (name (:request-method request)))
          path      (:uri request)
          trace-id  (get-in request [:headers "x-trace-id"] (str (java.util.UUID/randomUUID)))
          user-id   (get-in request [:headers "x-user-id"])
          start     (System/nanoTime)]
      (with-mdc {:trace-id trace-id
                 :user-id  user-id
                 :method   method
                 :path     path}
        (let [response (handler (assoc-in request [:headers "x-trace-id"] trace-id))
              duration (/ (- (System/nanoTime) start) 1e6)]
          (log/info (format "%s %s %d (%.0fms)" method path (:status response 500) duration))
          (-> response
              (assoc-in [:headers "X-Trace-Id"] trace-id)))))))
