(ns ajet.chat.shared.mentions
  "Parse and render mention/channel-link syntax in message bodies.

   Storage format:
     @<user:uuid>     — user mention
     @<here>          — @here mention
     #<channel:uuid>  — channel link

   Rendered output (resolved at display time):
     @displayName, @here, #channelName"
  (:require [clojure.string :as str]))

(def ^:private user-mention-re
  #"@<user:([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})>")

(def ^:private here-mention-re
  #"@<here>")

(def ^:private channel-link-re
  #"#<channel:([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})>")

(def ^:private code-block-re
  "Matches inline code and fenced code blocks."
  #"(?s)```.*?```|`[^`]+`")

(defn- strip-code-blocks
  "Remove code blocks from text to avoid parsing mentions inside them."
  [text]
  (str/replace text code-block-re ""))

(defn parse
  "Parse all mentions from a message body.

   Returns a vector of maps:
     [{:type :user    :id \"uuid\"}
      {:type :here}
      {:type :channel :id \"uuid\"}]"
  [body-md]
  (let [clean (strip-code-blocks body-md)]
    (vec
      (concat
        (for [[_ uuid] (re-seq user-mention-re clean)]
          {:type :user :id uuid})
        (for [_ (re-seq here-mention-re clean)]
          {:type :here})
        (for [[_ uuid] (re-seq channel-link-re clean)]
          {:type :channel :id uuid})))))

(defn render
  "Replace mention syntax with display names.

   lookup-fn: (fn [type id]) -> display-name-string
     type is :user or :channel, id is UUID string.
     Returns nil if unknown -> falls back to @unknown-user / #unknown-channel."
  [body-md lookup-fn]
  (-> body-md
      (str/replace user-mention-re
        (fn [[_ uuid]]
          (str "@" (or (lookup-fn :user uuid) "unknown-user"))))
      (str/replace here-mention-re "@here")
      (str/replace channel-link-re
        (fn [[_ uuid]]
          (str "#" (or (lookup-fn :channel uuid) "unknown-channel"))))))
