(ns ajet.chat.shared.config
  "Configuration loader — EDN files + env var overrides.

   Env var convention: AJET__DB__HOST -> {:db {:host \"...\"}}
   Double underscores separate nesting levels, single underscores become hyphens.
   Example: AJET__MINIO__ACCESS_KEY -> {:minio {:access-key \"...\"}}"
  (:require [clojure.edn :as edn]
            [clojure.java.io :as io]
            [clojure.string :as str]))

(def ^:private shared-defaults
  {:db    {:host "localhost" :port 5432 :dbname "ajet_chat" :user "ajet" :pool-size 10
           :migrations {:enabled true :location "migrations"}}
   :nats  {:url "nats://localhost:4222"}
   :minio {:endpoint "http://localhost:9000"
           :access-key "minioadmin"
           :secret-key "minioadmin"
           :bucket "ajet-chat"}})

(def ^:private secret-keys
  "Keys whose values must never be logged."
  #{:password :secret :secret-key :client-secret :token :token-hash})

(defn- deep-merge
  "Recursively merge maps. Later values win for non-map leaves."
  [& maps]
  (reduce (fn [acc m]
            (reduce-kv (fn [a k v]
                         (let [existing (get a k)]
                           (if (and (map? existing) (map? v))
                             (assoc a k (deep-merge existing v))
                             (assoc a k v))))
                       acc m))
          {} maps))

(defn- coerce-value
  "Coerce string env var value to appropriate type."
  [s]
  (cond
    (re-matches #"\d+" s) (parse-long s)
    (= "true" s)         true
    (= "false" s)        false
    :else                 s))

(defn- env-var->path
  "Convert AJET__DB__HOST to [:db :host].
   Double underscore __ separates nesting levels.
   Single underscore within a segment becomes hyphen (kebab-case).
   Example: AJET__MINIO__ACCESS_KEY -> [:minio :access-key]"
  [var-name]
  (let [stripped (str/replace-first var-name #"(?i)^AJET__" "")
        segments (str/split stripped #"__")]
    (mapv #(keyword (-> % str/lower-case (str/replace "_" "-"))) segments)))

(defn- env-overrides
  "Read all AJET__* env vars and convert to nested config map.
   Uses double-underscore prefix (AJET__) to distinguish from other vars."
  []
  (reduce-kv (fn [acc k v]
               (if (str/starts-with? k "AJET__")
                 (let [path (env-var->path k)]
                   (assoc-in acc path (coerce-value v)))
                 acc))
             {} (System/getenv)))

(defn- load-edn-resource
  "Load EDN from classpath resource."
  [path]
  (when-let [r (io/resource path)]
    (edn/read-string (slurp r))))

(defn- load-edn-file
  "Load EDN from filesystem path."
  [path]
  (let [f (io/file path)]
    (when (.exists f)
      (edn/read-string (slurp f)))))

(defn redact
  "Replace secret values in config with [REDACTED] for safe logging."
  [config]
  (cond
    (map? config)
    (reduce-kv (fn [m k v]
                 (assoc m k (if (secret-keys k)
                              "[REDACTED]"
                              (redact v))))
               {} config)
    (sequential? config)
    (mapv redact config)
    :else config))

(defn load-config
  "Load configuration with priority: env vars > module config > shared defaults.

   Options:
     :file      - EDN file path (filesystem)
     :resource  - EDN classpath resource name (default: \"config.edn\")
     :profile   - :dev, :test, or :prod (merges profile-specific overrides)"
  [& [{:keys [file resource profile] :or {resource "config.edn"}}]]
  (let [file-config     (if file
                          (or (load-edn-file file)
                              (throw (ex-info (str "Config file not found: " file)
                                              {:file file})))
                          (load-edn-resource resource))
        base            (deep-merge shared-defaults (or file-config {}))
        profile-config  (when profile (get-in file-config [:profiles profile]))
        with-profile    (if profile-config
                          (deep-merge base profile-config)
                          base)
        with-env        (deep-merge with-profile (env-overrides))]
    with-env))
